Merge "Eliminate debug-moves if their preceding phi-move is sufficient."
diff --git a/build.gradle b/build.gradle
index 82feb00..d13029e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -379,6 +379,20 @@
     }
 }
 
+task CompatProguard(type: Jar) {
+    from sourceSets.main.output
+    baseName 'compatproguard'
+    manifest {
+        attributes 'Main-Class': 'com.android.tools.r8.compatproguard.CompatProguard'
+    }
+    // In order to build without dependencies, pass the exclude_deps property using:
+    // gradle -Pexclude_deps CompatProguard
+    if (!project.hasProperty('exclude_deps')) {
+        // Also include dependencies
+        from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
+    }
+}
+
 task D8Logger(type: Jar) {
     from sourceSets.main.output
     baseName 'd8logger'
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 5b2e5cf..76779bb 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -217,8 +217,6 @@
     internal.skipMinification = true;
     assert internal.useTreeShaking;
     internal.useTreeShaking = false;
-    assert internal.interfaceMethodDesugaring == OffOrAuto.Off;
-    assert internal.tryWithResourcesDesugaring == OffOrAuto.Off;
     assert internal.inlineAccessors;
     internal.inlineAccessors = false;
     assert internal.removeSwitchMaps;
diff --git a/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
new file mode 100644
index 0000000..45eb6e4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
@@ -0,0 +1,56 @@
+// 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.compatproguard;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.util.List;
+
+public class CompatProguard {
+  public static class CompatProguardOptions {
+    public final List<String> proguardConfig;
+
+    CompatProguardOptions(List<String> proguardConfig) {
+      this.proguardConfig = proguardConfig;
+    }
+
+    public static CompatProguardOptions parse(String[] args) {
+      ImmutableList.Builder<String> builder = ImmutableList.builder();
+      if (args.length > 0) {
+        StringBuilder currentLine = new StringBuilder(args[0]);
+        for (int i = 1; i < args.length; i++) {
+          String arg = args[i];
+          if (arg.charAt(0) == '-') {
+            builder.add(currentLine.toString());
+            currentLine = new StringBuilder(arg);
+          } else {
+            currentLine.append(' ').append(arg);
+          }
+        }
+        builder.add(currentLine.toString());
+      }
+      return new CompatProguardOptions(builder.build());
+    }
+  }
+
+  private static void run(String[] args) throws IOException, CompilationException {
+    System.out.println("CompatProguard " + String.join(" ", args));
+    // Run R8 passing all the options from the command line as a Proguard configuration.
+    CompatProguardOptions options = CompatProguardOptions.parse(args);
+    R8.run(R8Command.builder().addProguardConfiguration(options.proguardConfig).build());
+  }
+
+  public static void main(String[] args) throws IOException {
+    try {
+      run(args);
+    } catch (CompilationException e) {
+      System.err.println(e.getMessage());
+      System.exit(1);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DebugLocalInfo.java b/src/main/java/com/android/tools/r8/graph/DebugLocalInfo.java
index bf39f6f..23f20a2 100644
--- a/src/main/java/com/android/tools/r8/graph/DebugLocalInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/DebugLocalInfo.java
@@ -3,9 +3,19 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.utils.DescriptorUtils;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
 
 public class DebugLocalInfo {
+  public enum PrintLevel {
+    NONE,
+    NAME,
+    FULL
+  }
+
+  public static final PrintLevel PRINT_LEVEL = PrintLevel.NAME;
+
   public final DexString name;
   public final DexType type;
   public final DexString signature;
@@ -58,6 +68,17 @@
 
   @Override
   public String toString() {
-    return name + ":" + type + (signature == null ? "" : signature);
+    switch (PRINT_LEVEL) {
+      case NONE:
+        return "";
+      case NAME:
+        return name.toString();
+      case FULL:
+        return name + ":" + (signature == null
+            ? type
+            : DescriptorUtils.descriptorToJavaType(signature.toString()));
+      default:
+        throw new Unreachable();
+    }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
index c9e26b4..2134ccf 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.graph.DebugLocalInfo.PrintLevel;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.conversion.DexBuilder;
 import com.android.tools.r8.ir.conversion.IRBuilder;
@@ -767,9 +768,6 @@
     StringBuilder builder = new StringBuilder();
     builder.append("block ");
     builder.append(number);
-    builder.append(" (");
-    builder.append(System.identityHashCode(this));
-    builder.append(')');
     builder.append(", pred-counts: " + predecessors.size());
     if (unfilledPredecessorsCount > 0) {
       builder.append(" (" + unfilledPredecessorsCount + " unfilled)");
@@ -813,11 +811,37 @@
       StringUtils.appendLeftPadded(builder, Integer.toString(instruction.getNumber()), 6);
       builder.append(": ");
       StringUtils.appendRightPadded(builder, instruction.toString(), 20);
-      builder.append('\n');
+      if (DebugLocalInfo.PRINT_LEVEL != PrintLevel.NONE) {
+        List<Value> localEnds = new ArrayList<>(instruction.getDebugValues().size());
+        List<Value> localStarts = new ArrayList<>(instruction.getDebugValues().size());
+        List<Value> localLive = new ArrayList<>(instruction.getDebugValues().size());
+        for (Value value : instruction.getDebugValues()) {
+          if (value.getDebugLocalEnds().contains(instruction)) {
+            localEnds.add(value);
+          } else if (value.getDebugLocalStarts().contains(instruction)) {
+            localStarts.add(value);
+          } else {
+            assert value.debugUsers().contains(instruction);
+            localLive.add(value);
+          }
+        }
+        printDebugValueSet("live", localLive, builder);
+        printDebugValueSet("end", localEnds, builder);
+        printDebugValueSet("start", localStarts, builder);
+      }
+      builder.append("\n");
     }
     return builder.toString();
   }
 
+  private void printDebugValueSet(String header, List<Value> locals, StringBuilder builder) {
+    if (!locals.isEmpty()) {
+      builder.append(" [").append(header).append(": ");
+      StringUtils.append(builder, locals, ", ", BraceType.NONE);
+      builder.append("]");
+    }
+  }
+
   public void print(CfgPrinter printer) {
     printer.begin("block");
     printer.print("name \"B").append(number).append("\"\n");
diff --git a/src/main/java/com/android/tools/r8/ir/code/Phi.java b/src/main/java/com/android/tools/r8/ir/code/Phi.java
index 65995ff..28a4d74 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Phi.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Phi.java
@@ -278,6 +278,40 @@
         && value.isZero();
   }
 
+  /**
+   * Determine if the only possible values for the phi are the integers 0 or 1.
+   */
+  public boolean knownToBeBoolean() {
+    return knownToBeBoolean(new HashSet<>());
+  }
+
+  private boolean knownToBeBoolean(HashSet<Phi> active) {
+    active.add(this);
+
+    for (Value operand : operands) {
+      if (!operand.isPhi()) {
+        if (operand.isConstNumber()) {
+          ConstNumber number = operand.getConstInstruction().asConstNumber();
+          if (!number.isIntegerOne() && !number.isIntegerZero()) {
+            return false;
+          }
+        } else {
+          return false;
+        }
+      }
+    }
+
+    for (Value operand : operands) {
+      if (operand.isPhi() && !active.contains(operand.asPhi())) {
+        if (!operand.asPhi().knownToBeBoolean(active)) {
+          return false;
+        }
+      }
+    }
+
+    return true;
+  }
+
   private MoveType computeOutType(Set<Phi> active) {
     if (outType != null) {
       return outType;
@@ -293,7 +327,7 @@
     }
     // We did not find a non-phi operand that dictates the type. Recurse on phi arguments.
     for (Value operand : operands) {
-      if (operand.isPhi() && !active.contains(operand)) {
+      if (operand.isPhi() && !active.contains(operand.asPhi())) {
         MoveType phiType = operand.asPhi().computeOutType(active);
         // TODO(zerny): If we had a CONST_ZERO type element, we could often avoid going through
         // all phis. We would only have to recurse until we got a non CONST_ZERO out type.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
index b7036f5..d3964d1 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -15,6 +15,8 @@
 import com.android.tools.r8.ir.synthetic.SynthesizedCode;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.IdentityHashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -42,9 +44,7 @@
   }
 
   final void process(DexClass clazz) {
-    if (clazz.isInterface()) {
-      throw new CompilationError("Interface in superclass chain.");
-    }
+    assert !clazz.isInterface();
     if (!clazz.isProgramClass()) {
       // We assume that library classes don't need to be processed, since they
       // are provided by a runtime not supporting default interface methods.
@@ -61,8 +61,15 @@
     // about methods added to superclasses when we decide if we want to add a default
     // method to class `clazz`.
     DexType superType = clazz.superType;
-    if (superType != null && superType != rewriter.factory.objectType) {
-      process(rewriter.findRequiredClass(superType));
+    // If superClass definition is missing, just skip this part and let real processing of its
+    // subclasses report the error if it is required.
+    DexClass superClass = superType == null ? null : rewriter.findDefinitionFor(superType);
+    if (superClass != null && superType != rewriter.factory.objectType) {
+      if (superClass.isInterface()) {
+        throw new CompilationError("Interface `" + superClass.toSourceString()
+        + "` used as super class of `" + clazz.toSourceString() + "`.");
+      }
+      process(superClass);
     }
 
     if (clazz.interfaces.isEmpty()) {
@@ -95,7 +102,10 @@
 
   private DexEncodedMethod addForwardingMethod(DexEncodedMethod defaultMethod, DexClass clazz) {
     DexMethod method = defaultMethod.method;
-    assert !rewriter.findRequiredClass(method.holder).isLibraryClass();
+    // NOTE: Never add a forwarding method to methods of classes unknown or coming from android.jar
+    // even if this results in invalid code, these classes are never desugared.
+    assert rewriter.findDefinitionFor(method.holder) != null
+        && !rewriter.findDefinitionFor(method.holder).isLibraryClass();
     // New method will have the same name, proto, and also all the flags of the
     // default method, including bridge flag.
     DexMethod newMethod = rewriter.factory.createMethod(clazz.type, method.proto, method.name);
@@ -113,23 +123,62 @@
   // in this class.
   private List<DexEncodedMethod> collectMethodsToImplement(DexClass clazz) {
     DefaultMethodsHelper helper = new DefaultMethodsHelper();
-
+    DexClass current = clazz;
+    List<DexEncodedMethod> accumulatedVirtualMethods = new ArrayList<>();
     // Collect candidate default methods by inspecting interfaces implemented
     // by this class as well as its superclasses.
-    DexClass current = clazz;
-    while (true) {
+    //
+    // We assume here that interfaces implemented by java.lang.Object don't
+    // have default methods to desugar since they are library interfaces. And we assume object
+    // methods don't hide any default interface methods. Default interface method matching Object's
+    // methods is supposed to fail with a compilation error.
+    // Note that this last assumption will be broken if Object API is augmented with a new method in
+    // the future.
+    while (current.type != rewriter.factory.objectType) {
       for (DexType type : current.interfaces.values) {
-        helper.merge(getOrCreateInterfaceInfo(type));
+        helper.merge(getOrCreateInterfaceInfo(clazz, current, type));
       }
 
-      DexType superType = current.superType;
-      if (superType == null || superType == rewriter.factory.objectType) {
-        // We assume here that interfaces implemented by java.lang.Object don't
-        // have default methods and don't hide any default interface methods since
-        // they must be library interfaces.
-        break;
+      accumulatedVirtualMethods.addAll(Arrays.asList(clazz.virtualMethods()));
+
+      List<DexEncodedMethod> defaultMethodsInDirectInterface = helper.createFullList();
+      List<DexEncodedMethod> toBeImplementedFromDirectInterface =
+          new ArrayList<>(defaultMethodsInDirectInterface.size());
+      hideCandidates(accumulatedVirtualMethods,
+          defaultMethodsInDirectInterface,
+          toBeImplementedFromDirectInterface);
+      // toBeImplementedFromDirectInterface are those that we know for sure we need to implement by
+      // looking at the already desugared super classes.
+      // Remaining methods in defaultMethodsInDirectInterface are those methods we need to look at
+      // the hierarchy to know how they should be handled.
+      if (toBeImplementedFromDirectInterface.isEmpty()
+          && defaultMethodsInDirectInterface.isEmpty()) {
+        // No interface with default in direct hierarchy, nothing to do: super already has all that
+        // is needed.
+        return Collections.emptyList();
       }
-      current = rewriter.findRequiredClass(superType);
+
+      if (current.superType == null) {
+        break;
+      } else {
+        DexClass superClass = rewriter.findDefinitionFor(current.superType);
+        if (superClass != null) {
+          current = superClass;
+        } else {
+          String message = "Default method desugaring of `" + clazz.toSourceString() + "` failed";
+          if (current == clazz) {
+            message += " because its super class `" + clazz.superType.toSourceString()
+            + "` is missing";
+          } else {
+            message +=
+                " because it's hierarchy is incomplete. The class `"
+                    + current.superType.toSourceString()
+                    + "` is missing and it is the declared super class of `"
+                    + current.toSourceString() + "`";
+          }
+          throw new CompilationError(message);
+        }
+      }
     }
 
     List<DexEncodedMethod> candidates = helper.createCandidatesList();
@@ -142,25 +191,41 @@
     current = clazz;
     while (true) {
       // Hide candidates by virtual method of the class.
-      hideCandidates(current.virtualMethods(), candidates, toBeImplemented);
+      hideCandidates(Arrays.asList(current.virtualMethods()), candidates, toBeImplemented);
       if (candidates.isEmpty()) {
         return toBeImplemented;
       }
 
       DexType superType = current.superType;
-      if (superType == null || superType == rewriter.factory.objectType) {
+      DexClass superClass = null;
+      if (superType != null) {
+        superClass = rewriter.findDefinitionFor(superType);
+        // It's available or we would have failed while analyzing the hierarchy for interfaces.
+        assert superClass != null;
+      }
+      if (superClass == null || superType == rewriter.factory.objectType) {
         // Note that default interface methods must never have same
         // name/signature as any method in java.lang.Object (JLS §9.4.1.2).
 
         // Everything still in candidate list is not hidden.
         toBeImplemented.addAll(candidates);
+        // NOTE: we also intentionally remove all candidates coming from android.jar
+        // since it is only possible in case v24+ version of android.jar is provided.
+        // WARNING: This may result in incorrect code on older platforms!
+        toBeImplemented.removeIf(
+            method -> {
+              DexClass holder = rewriter.findDefinitionFor(method.method.holder);
+              // Holder of a found method to implement is a defined interface.
+              assert holder != null;
+              return holder.isLibraryClass();
+            });
         return toBeImplemented;
       }
-      current = rewriter.findRequiredClass(superType);
+      current = superClass;
     }
   }
 
-  private void hideCandidates(DexEncodedMethod[] virtualMethods,
+  private void hideCandidates(List<DexEncodedMethod> virtualMethods,
       List<DexEncodedMethod> candidates, List<DexEncodedMethod> toBeImplemented) {
     Iterator<DexEncodedMethod> it = candidates.iterator();
     while (it.hasNext()) {
@@ -188,42 +253,56 @@
     }
   }
 
-  private DefaultMethodsHelper.Collection getOrCreateInterfaceInfo(DexType iface) {
+  private DefaultMethodsHelper.Collection getOrCreateInterfaceInfo(
+      DexClass classToDesugar,
+      DexClass implementing,
+      DexType iface) {
     DefaultMethodsHelper.Collection collection = cache.get(iface);
     if (collection != null) {
       return collection;
     }
-    collection = createInterfaceInfo(iface);
+    collection = createInterfaceInfo(classToDesugar, implementing, iface);
     cache.put(iface, collection);
     return collection;
   }
 
-  private DefaultMethodsHelper.Collection createInterfaceInfo(DexType iface) {
+  private DefaultMethodsHelper.Collection createInterfaceInfo(
+      DexClass classToDesugar,
+      DexClass implementing,
+      DexType iface) {
     DefaultMethodsHelper helper = new DefaultMethodsHelper();
-    DexClass clazz = rewriter.findRequiredClass(iface);
-    if (!clazz.isInterface()) {
-      throw new CompilationError(
-          "Type " + iface.toSourceString() + " is expected to be an interface.");
+    DexClass definedInterface = rewriter.findDefinitionFor(iface);
+    if (definedInterface == null) {
+      String message = "Interface `" + iface.toSourceString()
+              + "` not found. It's needed to make sure desugaring of `"
+              + classToDesugar.toSourceString() + "` is correct. Desugaring will assume that this"
+              + " interface has no default method";
+      if (classToDesugar != implementing) {
+        message += ". This missing interface is declared in the direct hierarchy of `"
+              + implementing.toString() + "`";
+      }
+      rewriter.warnMissingClass(iface, message);
+      return helper.wrapInCollection();
     }
-    if (clazz.isLibraryClass()) {
-      // For library interfaces we always assume there are no default
-      // methods, since the interface is part of framework provided by
-      // runtime which does not support default interface methods.
-      return DefaultMethodsHelper.Collection.EMPTY;
+
+    if (!definedInterface.isInterface()) {
+      throw new CompilationError(
+          "Type " + iface.toSourceString() + " is referenced as an interface of `"
+          + implementing.toString() + "`.");
     }
 
     // Merge information from all superinterfaces.
-    for (DexType superinterface : clazz.interfaces.values) {
-      helper.merge(getOrCreateInterfaceInfo(superinterface));
+    for (DexType superinterface : definedInterface.interfaces.values) {
+      helper.merge(getOrCreateInterfaceInfo(classToDesugar, definedInterface, superinterface));
     }
 
     // Hide by virtual methods of this interface.
-    for (DexEncodedMethod virtual : clazz.virtualMethods()) {
+    for (DexEncodedMethod virtual : definedInterface.virtualMethods()) {
       helper.hideMatches(virtual.method);
     }
 
     // Add all default methods of this interface.
-    for (DexEncodedMethod encoded : clazz.virtualMethods()) {
+    for (DexEncodedMethod encoded : definedInterface.virtualMethods()) {
       if (rewriter.isDefaultMethod(encoded)) {
         helper.addDefaultMethod(encoded);
       }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DefaultMethodsHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/DefaultMethodsHelper.java
index 2f25b19..ae9a2ae 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DefaultMethodsHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DefaultMethodsHelper.java
@@ -6,8 +6,10 @@
 
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
+import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -102,6 +104,18 @@
     return candidates;
   }
 
+  final List<DexEncodedMethod> createFullList() {
+    if (candidates.isEmpty() && hidden.isEmpty()) {
+      return Collections.emptyList();
+    }
+
+    List<DexEncodedMethod> fullList =
+        new ArrayList<DexEncodedMethod>(candidates.size() + hidden.size());
+    fullList.addAll(candidates);
+    fullList.addAll(hidden);
+    return fullList;
+  }
+
   // Create default interface collection based on collected information.
   final Collection wrapInCollection() {
     candidates.removeAll(hidden);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index cbeda1e..aa93aa8 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItem;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexMethodHandle;
@@ -25,6 +26,7 @@
 import com.android.tools.r8.ir.code.InvokeStatic;
 import com.android.tools.r8.ir.code.InvokeSuper;
 import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.logging.Log;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 import java.util.ListIterator;
@@ -68,6 +70,11 @@
   // to this collection since it is only filled in ClassProcessor running synchronously.
   private final Set<DexEncodedMethod> forwardingMethods = Sets.newIdentityHashSet();
 
+  /**
+   * A set of dexitems we have reported missing to dedupe warnings.
+   */
+  private Set<DexItem> reportedMissing = Sets.newIdentityHashSet();
+
   /** Defines a minor variation in desugaring. */
   public enum Flavor {
     /** Process all application resources. */
@@ -100,10 +107,11 @@
           // Check that static interface methods are not referenced
           // from invoke-custom instructions via method handles.
           DexCallSite callSite = instruction.asInvokeCustom().getCallSite();
-          reportStaticInterfaceMethodHandle(callSite.bootstrapMethod);
+          reportStaticInterfaceMethodHandle(encodedMethod.method, callSite.bootstrapMethod);
           for (DexValue arg : callSite.bootstrapArgs) {
             if (arg instanceof DexValue.DexValueMethodHandle) {
-              reportStaticInterfaceMethodHandle(((DexValue.DexValueMethodHandle) arg).value);
+              reportStaticInterfaceMethodHandle(encodedMethod.method,
+                  ((DexValue.DexValueMethodHandle) arg).value);
             }
           }
           continue;
@@ -112,7 +120,17 @@
         if (instruction.isInvokeStatic()) {
           InvokeStatic invokeStatic = instruction.asInvokeStatic();
           DexMethod method = invokeStatic.getInvokedMethod();
-          if (isInterfaceClass(method.holder)) {
+          DexClass clazz = findDefinitionFor(method.holder);
+          if (clazz == null) {
+            // NOTE: leave unchanged those calls to undefined targets. This may lead to runtime
+            // exception but we can not report it as error since it can also be the intended
+            // behavior.
+            warnMissingClass(encodedMethod.method, method.holder);
+          } else if (clazz.isInterface() && !clazz.isLibraryClass()) {
+            // NOTE: we intentionally don't desugar static calls into static interface
+            // methods coming from android.jar since it is only possible in case v24+
+            // version of android.jar is provided.
+            // WARNING: This may result in incorrect code on older platforms!
             // Retarget call to an appropriate method of companion class.
             instructions.replaceCurrentInstruction(
                 new InvokeStatic(staticAsMethodOfCompanionClass(method),
@@ -124,7 +142,17 @@
         if (instruction.isInvokeSuper()) {
           InvokeSuper invokeSuper = instruction.asInvokeSuper();
           DexMethod method = invokeSuper.getInvokedMethod();
-          if (isInterfaceClass(method.holder)) {
+          DexClass clazz = findDefinitionFor(method.holder);
+          if (clazz == null) {
+            // NOTE: leave unchanged those calls to undefined targets. This may lead to runtime
+            // exception but we can not report it as error since it can also be the intended
+            // behavior.
+            warnMissingClass(encodedMethod.method, method.holder);
+          } else if (clazz != null && (clazz.isInterface() && !clazz.isLibraryClass())) {
+            // NOTE: we intentionally don't desugar super calls into interface methods
+            // coming from android.jar since it is only possible in case v24+ version
+            // of android.jar is provided.
+            // WARNING: This may result in incorrect code on older platforms!
             // Retarget call to an appropriate method of companion class.
             instructions.replaceCurrentInstruction(
                 new InvokeStatic(defaultAsMethodOfCompanionClass(method),
@@ -135,25 +163,27 @@
     }
   }
 
-  private void reportStaticInterfaceMethodHandle(DexMethodHandle handle) {
-    if (handle.type.isInvokeStatic() && isInterfaceClass(handle.asMethod().holder)) {
-      throw new Unimplemented(
-          "Desugaring of static interface method handle in is not yet supported.");
+  private void reportStaticInterfaceMethodHandle(DexMethod referencedFrom, DexMethodHandle handle) {
+    if (handle.type.isInvokeStatic()) {
+      DexClass holderClass = findDefinitionFor(handle.asMethod().holder);
+      // NOTE: If the class definition is missing we can't check. Let it be handled as any other
+      // missing call target.
+      if (holderClass == null) {
+        warnMissingClass(referencedFrom, handle.asMethod().holder);
+      } else if (holderClass.isInterface()) {
+        throw new Unimplemented(
+            "Desugaring of static interface method handle as in `"
+            + referencedFrom.toSourceString() + "` in is not yet supported.");
+      }
     }
   }
 
-  private boolean isInterfaceClass(DexType type) {
-    return findRequiredClass(type).isInterface();
-  }
-
-  // Returns the class for the type specified, report errors for missing classes.
-  final DexClass findRequiredClass(DexType type) {
-    DexClass clazz = converter.appInfo.definitionFor(type);
-    if (clazz != null) {
-      return clazz;
-    }
-    throw new CompilationError("Type '" + type.toSourceString() +
-        "' required for default and static interface methods desugaring not found.");
+  /**
+   * Returns the class definition for the specified type.
+   * @return may return null if no definition for the given type is available.
+   */
+  final DexClass findDefinitionFor(DexType type) {
+    return converter.appInfo.definitionFor(type);
   }
 
   // Gets the companion class for the interface `type`.
@@ -262,4 +292,18 @@
     }
     return true;
   }
+
+  void warnMissingClass(DexType missing, String message) {
+    // TODO replace by a proper warning mechanic (see b/65154707).
+    // TODO think about using a common deduplicating mechanic with Enqueuer
+    if (reportedMissing.add(missing)) {
+      System.err.println(message);
+    }
+  }
+
+  private void warnMissingClass(DexItem referencedFrom, DexType clazz) {
+      warnMissingClass(clazz,
+          "Type `" + clazz.toSourceString() + "` was not found, it is required for default or"
+          + " static interface methods desugaring of `" + referencedFrom.toSourceString() + "`");
+  }
 }
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 bc93fcf..2aa9015 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
@@ -335,9 +335,10 @@
         ListIterator<LocalRange> it = openRanges.listIterator(0);
         Int2ReferenceMap<DebugLocalInfo> ending = new Int2ReferenceOpenHashMap<>();
         Int2ReferenceMap<DebugLocalInfo> starting = new Int2ReferenceOpenHashMap<>();
+        int endPositionCorrection = instruction.isDebugPosition() ? 1 : 0;
         while (it.hasNext()) {
           LocalRange openRange = it.next();
-          if (openRange.end <= index) {
+          if (openRange.end <= index - endPositionCorrection) {
             it.remove();
             assert currentLocals.get(openRange.register) == openRange.local;
             currentLocals.remove(openRange.register);
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index c241bd7..f27998c 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -84,9 +84,9 @@
   public List<String> logArgumentsFilter = ImmutableList.of();
 
   // Defines interface method rewriter behavior.
-  public OffOrAuto interfaceMethodDesugaring = OffOrAuto.Off;
+  public OffOrAuto interfaceMethodDesugaring = OffOrAuto.Auto;
   // Defines try-with-resources rewriter behavior.
-  public OffOrAuto tryWithResourcesDesugaring = OffOrAuto.Off;
+  public OffOrAuto tryWithResourcesDesugaring = OffOrAuto.Auto;
 
   // Application writing mode.
   public OutputMode outputMode = OutputMode.Indexed;
diff --git a/src/test/debugTestResources/Locals.java b/src/test/debugTestResources/Locals.java
index 5afcabb..1221234 100644
--- a/src/test/debugTestResources/Locals.java
+++ b/src/test/debugTestResources/Locals.java
@@ -318,6 +318,26 @@
     nop();
   }
 
+  public static int localConstant(boolean b) {
+    if (b) {
+      int result1 = 1;
+      return result1;
+    } else {
+      int result2 = 2;
+      return result2;
+    }
+  }
+
+  public static int localConstantBis(boolean b) {
+    int result = 0;
+    if (b) {
+      result = 1;
+    } else {
+      result = 2;
+    }
+    return result;
+  }
+
   public static void main(String[] args) {
     noLocals();
     unusedLocals();
@@ -338,5 +358,7 @@
     switchRewriteToSwitches(1);
     regression65039701(true);
     regression65066975(false);
+    System.out.println(localConstant(true));
+    System.out.println(localConstantBis(true));
   }
 }
diff --git a/src/test/examplesAndroidO/desugaringwithandroidjar25/DefaultMethodInAndroidJar25.java b/src/test/examplesAndroidO/desugaringwithandroidjar25/DefaultMethodInAndroidJar25.java
new file mode 100644
index 0000000..bec4e67
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithandroidjar25/DefaultMethodInAndroidJar25.java
@@ -0,0 +1,100 @@
+// 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 desugaringwithandroidjar25;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.function.Predicate;
+
+public class DefaultMethodInAndroidJar25 {
+  public static void main(String[] args) throws Exception {
+    ClassWithDefaultPlatformMethods.test();
+  }
+}
+
+class ClassWithDefaultPlatformMethods implements Collection<String> {
+  private final ArrayList<String> list =
+      new ArrayList<String>() {{
+        add("First");
+        add("Second");
+      }};
+
+  static void test() {
+    ClassWithDefaultPlatformMethods instance = new ClassWithDefaultPlatformMethods();
+    instance.forEach(x -> System.out.println("BEFORE: " + x));
+    instance.removeIf(x -> true);
+    instance.forEach(x -> System.out.println("AFTER: " + x));
+  }
+
+  @Override
+  public int size() {
+    throw new AssertionError();
+  }
+
+  @Override
+  public boolean isEmpty() {
+    throw new AssertionError();
+  }
+
+  @Override
+  public boolean contains(Object o) {
+    throw new AssertionError();
+  }
+
+  @Override
+  public Iterator<String> iterator() {
+    return list.iterator();
+  }
+
+  @Override
+  public Object[] toArray() {
+    throw new AssertionError();
+  }
+
+  @Override
+  public <T> T[] toArray(T[] a) {
+    throw new AssertionError();
+  }
+
+  @Override
+  public boolean add(String s) {
+    throw new AssertionError();
+  }
+
+  @Override
+  public boolean remove(Object o) {
+    return list.remove(o);
+  }
+
+  @Override
+  public boolean containsAll(Collection<?> c) {
+    throw new AssertionError();
+  }
+
+  @Override
+  public boolean addAll(Collection<? extends String> c) {
+    throw new AssertionError();
+  }
+
+  @Override
+  public boolean removeAll(Collection<?> c) {
+    throw new AssertionError();
+  }
+
+  @Override
+  public boolean retainAll(Collection<?> c) {
+    throw new AssertionError();
+  }
+
+  @Override
+  public void clear() {
+    throw new AssertionError();
+  }
+
+  @Override
+  public boolean removeIf(Predicate<? super String> filter) {
+    return Collection.super.removeIf(filter);
+  }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithandroidjar25/StaticMethodInAndroidJar25.java b/src/test/examplesAndroidO/desugaringwithandroidjar25/StaticMethodInAndroidJar25.java
new file mode 100644
index 0000000..f07ea8f
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithandroidjar25/StaticMethodInAndroidJar25.java
@@ -0,0 +1,17 @@
+// 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 desugaringwithandroidjar25;
+
+import java.util.Comparator;
+
+public class StaticMethodInAndroidJar25 {
+  public static void main(String[] args) throws Exception {
+    Comparator<String> comparing =
+        Comparator.comparing(x -> x, String::compareTo);
+    System.out.println("'a' <> 'b' = " + comparing.compare("a", "b"));
+    System.out.println("'b' <> 'b' = " + comparing.compare("b", "b"));
+    System.out.println("'c' <> 'b' = " + comparing.compare("c", "b"));
+  }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasslib1/A.java b/src/test/examplesAndroidO/desugaringwithmissingclasslib1/A.java
new file mode 100644
index 0000000..98504bd
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasslib1/A.java
@@ -0,0 +1,10 @@
+// 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 desugaringwithmissingclasslib1;
+
+public interface A {
+  default String foo() {
+    return "A";
+  }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasslib1/A2.java b/src/test/examplesAndroidO/desugaringwithmissingclasslib1/A2.java
new file mode 100644
index 0000000..ff42039
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasslib1/A2.java
@@ -0,0 +1,10 @@
+// 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 desugaringwithmissingclasslib1;
+
+public interface A2 {
+  default String foo() {
+    return "A2";
+  }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasslib2/B.java b/src/test/examplesAndroidO/desugaringwithmissingclasslib2/B.java
new file mode 100644
index 0000000..1ab424e
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasslib2/B.java
@@ -0,0 +1,13 @@
+// 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 desugaringwithmissingclasslib2;
+
+import desugaringwithmissingclasslib1.A;
+
+public interface B extends A {
+  @Override
+  default String foo() {
+    return "B";
+  }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasslib3/C.java b/src/test/examplesAndroidO/desugaringwithmissingclasslib3/C.java
new file mode 100644
index 0000000..029f9f4
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasslib3/C.java
@@ -0,0 +1,9 @@
+// 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 desugaringwithmissingclasslib3;
+
+import desugaringwithmissingclasslib1.A;
+
+public class C implements A {
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasslib4/C2.java b/src/test/examplesAndroidO/desugaringwithmissingclasslib4/C2.java
new file mode 100644
index 0000000..b2aee3b
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasslib4/C2.java
@@ -0,0 +1,13 @@
+// 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 desugaringwithmissingclasslib4;
+
+import desugaringwithmissingclasslib3.C;
+
+public class C2 extends C {
+  public String foo2() {
+    return "C2";
+  }
+}
+
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasstest1/ImplementMethodsWithDefault.java b/src/test/examplesAndroidO/desugaringwithmissingclasstest1/ImplementMethodsWithDefault.java
new file mode 100644
index 0000000..5f3c1c2
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasstest1/ImplementMethodsWithDefault.java
@@ -0,0 +1,10 @@
+// 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 desugaringwithmissingclasstest1;
+
+import desugaringwithmissingclasslib1.A;
+import desugaringwithmissingclasslib2.B;
+
+public class ImplementMethodsWithDefault implements A, B {
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasstest1/Main.java b/src/test/examplesAndroidO/desugaringwithmissingclasstest1/Main.java
new file mode 100644
index 0000000..411a016
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasstest1/Main.java
@@ -0,0 +1,21 @@
+// 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 desugaringwithmissingclasstest1;
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    ImplementMethodsWithDefault instance = new ImplementMethodsWithDefault();
+    try {
+      String foo = instance.foo();
+      if (foo.equals("B")) {
+        System.out.println("OK");
+      } else {
+        System.out.println("NOT OK: " + foo);
+      }
+    } catch (Throwable t) {
+      System.out.println("NOT OK " + t.getClass() + " " + t.getMessage());
+      t.printStackTrace();
+    }
+  }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasstest2/ImplementMethodsWithDefault.java b/src/test/examplesAndroidO/desugaringwithmissingclasstest2/ImplementMethodsWithDefault.java
new file mode 100644
index 0000000..f2ef1b6
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasstest2/ImplementMethodsWithDefault.java
@@ -0,0 +1,10 @@
+// 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 desugaringwithmissingclasstest2;
+
+import desugaringwithmissingclasslib2.B;
+import desugaringwithmissingclasslib3.C;
+
+public class ImplementMethodsWithDefault extends C implements B {
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasstest2/Main.java b/src/test/examplesAndroidO/desugaringwithmissingclasstest2/Main.java
new file mode 100644
index 0000000..c42ac81
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasstest2/Main.java
@@ -0,0 +1,21 @@
+// 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 desugaringwithmissingclasstest2;
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    ImplementMethodsWithDefault instance = new ImplementMethodsWithDefault();
+    try {
+      String foo = instance.foo();
+      if (foo.equals("B")) {
+        System.out.println("OK");
+      } else {
+        System.out.println("NOT OK: " + foo);
+      }
+    } catch (Throwable t) {
+      System.out.println("NOT OK " + t.getClass() + " " + t.getMessage());
+      t.printStackTrace();
+    }
+  }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasstest3/ImplementMethodsWithDefault.java b/src/test/examplesAndroidO/desugaringwithmissingclasstest3/ImplementMethodsWithDefault.java
new file mode 100644
index 0000000..4826237
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasstest3/ImplementMethodsWithDefault.java
@@ -0,0 +1,18 @@
+// 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 desugaringwithmissingclasstest3;
+
+import desugaringwithmissingclasslib2.B;
+import desugaringwithmissingclasslib3.C;
+
+public class ImplementMethodsWithDefault extends C implements B {
+  @Override
+  public String foo() {
+    return "ImplementMethodsWithDefault";
+  }
+
+  public String getB() {
+    return B.super.foo();
+  }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasstest3/Main.java b/src/test/examplesAndroidO/desugaringwithmissingclasstest3/Main.java
new file mode 100644
index 0000000..bde4d09
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasstest3/Main.java
@@ -0,0 +1,32 @@
+// 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 desugaringwithmissingclasstest3;
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    ImplementMethodsWithDefault instance = new ImplementMethodsWithDefault();
+    try {
+      String b = instance.getB();
+      if (b.equals("B")) {
+        System.out.println("OK");
+      } else {
+        System.out.println("NOT OK: " + b);
+      }
+    } catch (Throwable t) {
+      System.out.println("NOT OK " + t.getClass() + " " + t.getMessage());
+      t.printStackTrace();
+    }
+    try {
+      String foo = instance.foo();
+      if (foo.equals("ImplementMethodsWithDefault")) {
+        System.out.println("OK");
+      } else {
+        System.out.println("NOT OK: " + foo);
+      }
+    } catch (Throwable t) {
+      System.out.println("NOT OK " + t.getClass() + " " + t.getMessage());
+      t.printStackTrace();
+    }
+  }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasstest4/C2.java b/src/test/examplesAndroidO/desugaringwithmissingclasstest4/C2.java
new file mode 100644
index 0000000..cea6324
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasstest4/C2.java
@@ -0,0 +1,13 @@
+// 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 desugaringwithmissingclasstest4;
+
+import desugaringwithmissingclasslib3.C;
+
+public class C2 extends C {
+  public String foo2() {
+    return "C2";
+  }
+}
+
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasstest4/ImplementMethodsWithDefault.java b/src/test/examplesAndroidO/desugaringwithmissingclasstest4/ImplementMethodsWithDefault.java
new file mode 100644
index 0000000..c62bc2c
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasstest4/ImplementMethodsWithDefault.java
@@ -0,0 +1,14 @@
+// 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 desugaringwithmissingclasstest4;
+
+import desugaringwithmissingclasslib1.A;
+import desugaringwithmissingclasslib1.A2;
+
+public class ImplementMethodsWithDefault extends C2 implements A, A2 {
+  @Override
+  public String foo() {
+    return "ImplementMethodsWithDefault";
+  }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasstest4/Main.java b/src/test/examplesAndroidO/desugaringwithmissingclasstest4/Main.java
new file mode 100644
index 0000000..e439c5c
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasstest4/Main.java
@@ -0,0 +1,32 @@
+// 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 desugaringwithmissingclasstest4;
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    ImplementMethodsWithDefault instance = new ImplementMethodsWithDefault();
+    try {
+      String foo = instance.foo();
+      if (foo.equals("ImplementMethodsWithDefault")) {
+        System.out.println("OK");
+      } else {
+        System.out.println("NOT OK: " + foo);
+      }
+    } catch (Throwable t) {
+      System.out.println("NOT OK " + t.getClass() + " " + t.getMessage());
+      t.printStackTrace();
+    }
+    try {
+      String foo = instance.foo2();
+      if (foo.equals("C2")) {
+        System.out.println("OK");
+      } else {
+        System.out.println("NOT OK: " + foo);
+      }
+    } catch (Throwable t) {
+      System.out.println("NOT OK " + t.getClass() + " " + t.getMessage());
+      t.printStackTrace();
+    }
+  }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasstest5/ImplementMethodsWithDefault.java b/src/test/examplesAndroidO/desugaringwithmissingclasstest5/ImplementMethodsWithDefault.java
new file mode 100644
index 0000000..f8dfc3b
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasstest5/ImplementMethodsWithDefault.java
@@ -0,0 +1,15 @@
+// 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 desugaringwithmissingclasstest5;
+
+import desugaringwithmissingclasslib1.A;
+import desugaringwithmissingclasslib1.A2;
+import desugaringwithmissingclasslib4.C2;
+
+public class ImplementMethodsWithDefault extends C2 implements A, A2 {
+  @Override
+  public String foo() {
+    return "ImplementMethodsWithDefault";
+  }
+}
diff --git a/src/test/examplesAndroidO/desugaringwithmissingclasstest5/Main.java b/src/test/examplesAndroidO/desugaringwithmissingclasstest5/Main.java
new file mode 100644
index 0000000..d1ee997
--- /dev/null
+++ b/src/test/examplesAndroidO/desugaringwithmissingclasstest5/Main.java
@@ -0,0 +1,32 @@
+// 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 desugaringwithmissingclasstest5;
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    ImplementMethodsWithDefault instance = new ImplementMethodsWithDefault();
+    try {
+      String foo = instance.foo();
+      if (foo.equals("ImplementMethodsWithDefault")) {
+        System.out.println("OK");
+      } else {
+        System.out.println("NOT OK: " + foo);
+      }
+    } catch (Throwable t) {
+      System.out.println("NOT OK " + t.getClass() + " " + t.getMessage());
+      t.printStackTrace();
+    }
+    try {
+      String foo = instance.foo2();
+      if (foo.equals("C2")) {
+        System.out.println("OK");
+      } else {
+        System.out.println("NOT OK: " + foo);
+      }
+    } catch (Throwable t) {
+      System.out.println("NOT OK " + t.getClass() + " " + t.getMessage());
+      t.printStackTrace();
+    }
+  }
+}
diff --git a/src/test/examplesAndroidO/lambdadesugaring/ValueAdjustments.java b/src/test/examplesAndroidO/lambdadesugaring/ValueAdjustments.java
index 18f8d64..3290d86 100644
--- a/src/test/examplesAndroidO/lambdadesugaring/ValueAdjustments.java
+++ b/src/test/examplesAndroidO/lambdadesugaring/ValueAdjustments.java
@@ -21,7 +21,7 @@
     boolean f();
   }
 
-  interface iZ {
+  interface iBoolean {
     Boolean f();
   }
 
@@ -29,15 +29,15 @@
     byte f();
   }
 
-  interface iO {
+  interface iObject {
     Object f();
   }
 
-  interface iN {
+  interface iNumber {
     Number f();
   }
 
-  interface iB {
+  interface iByte {
     Byte f();
   }
 
@@ -45,7 +45,7 @@
     char f();
   }
 
-  interface iC {
+  interface iCharacter {
     Character f();
   }
 
@@ -53,7 +53,7 @@
     short f();
   }
 
-  interface iS {
+  interface iShort {
     Short f();
   }
 
@@ -61,7 +61,7 @@
     int f();
   }
 
-  interface iI {
+  interface iInteger {
     Integer f();
   }
 
@@ -69,7 +69,7 @@
     long f();
   }
 
-  interface iJ {
+  interface iLong {
     Long f();
   }
 
@@ -77,7 +77,7 @@
     float f();
   }
 
-  interface iF {
+  interface iFloat {
     Float f();
   }
 
@@ -85,56 +85,56 @@
     double f();
   }
 
-  interface iD {
+  interface iDouble {
     Double f();
   }
 
   private static void checkObject(StringBuffer builder) {
     builder
-        .append(((iO) ValueAdjustments::z).f()).append(' ')
-        .append(((iO) ValueAdjustments::Z).f()).append(' ')
-        .append(((iO) ValueAdjustments::b).f()).append(' ')
-        .append(((iO) ValueAdjustments::B).f()).append(' ')
-        .append(((iO) ValueAdjustments::c).f()).append(' ')
-        .append(((iO) ValueAdjustments::C).f()).append(' ')
-        .append(((iO) ValueAdjustments::s).f()).append(' ')
-        .append(((iO) ValueAdjustments::S).f()).append(' ')
-        .append(((iO) ValueAdjustments::i).f()).append(' ')
-        .append(((iO) ValueAdjustments::I).f()).append(' ')
-        .append(((iO) ValueAdjustments::j).f()).append(' ')
-        .append(((iO) ValueAdjustments::J).f()).append(' ')
-        .append(((iO) ValueAdjustments::f).f()).append(' ')
-        .append(((iO) ValueAdjustments::F).f()).append(' ')
-        .append(((iO) ValueAdjustments::d).f()).append(' ')
-        .append(((iO) ValueAdjustments::D).f()).append('\n');
+        .append(((iObject) ValueAdjustments::z).f()).append(' ')
+        .append(((iObject) ValueAdjustments::Z).f()).append(' ')
+        .append(((iObject) ValueAdjustments::b).f()).append(' ')
+        .append(((iObject) ValueAdjustments::B).f()).append(' ')
+        .append(((iObject) ValueAdjustments::c).f()).append(' ')
+        .append(((iObject) ValueAdjustments::C).f()).append(' ')
+        .append(((iObject) ValueAdjustments::s).f()).append(' ')
+        .append(((iObject) ValueAdjustments::S).f()).append(' ')
+        .append(((iObject) ValueAdjustments::i).f()).append(' ')
+        .append(((iObject) ValueAdjustments::I).f()).append(' ')
+        .append(((iObject) ValueAdjustments::j).f()).append(' ')
+        .append(((iObject) ValueAdjustments::J).f()).append(' ')
+        .append(((iObject) ValueAdjustments::f).f()).append(' ')
+        .append(((iObject) ValueAdjustments::F).f()).append(' ')
+        .append(((iObject) ValueAdjustments::d).f()).append(' ')
+        .append(((iObject) ValueAdjustments::D).f()).append('\n');
   }
 
   private static void checkNumber(StringBuffer builder) {
     builder
-        .append(((iN) ValueAdjustments::b).f()).append(' ')
-        .append(((iN) ValueAdjustments::B).f()).append(' ')
-        .append(((iN) ValueAdjustments::s).f()).append(' ')
-        .append(((iN) ValueAdjustments::S).f()).append(' ')
-        .append(((iN) ValueAdjustments::i).f()).append(' ')
-        .append(((iN) ValueAdjustments::I).f()).append(' ')
-        .append(((iN) ValueAdjustments::j).f()).append(' ')
-        .append(((iN) ValueAdjustments::J).f()).append(' ')
-        .append(((iN) ValueAdjustments::f).f()).append(' ')
-        .append(((iN) ValueAdjustments::F).f()).append(' ')
-        .append(((iN) ValueAdjustments::d).f()).append(' ')
-        .append(((iN) ValueAdjustments::D).f()).append('\n');
+        .append(((iNumber) ValueAdjustments::b).f()).append(' ')
+        .append(((iNumber) ValueAdjustments::B).f()).append(' ')
+        .append(((iNumber) ValueAdjustments::s).f()).append(' ')
+        .append(((iNumber) ValueAdjustments::S).f()).append(' ')
+        .append(((iNumber) ValueAdjustments::i).f()).append(' ')
+        .append(((iNumber) ValueAdjustments::I).f()).append(' ')
+        .append(((iNumber) ValueAdjustments::j).f()).append(' ')
+        .append(((iNumber) ValueAdjustments::J).f()).append(' ')
+        .append(((iNumber) ValueAdjustments::f).f()).append(' ')
+        .append(((iNumber) ValueAdjustments::F).f()).append(' ')
+        .append(((iNumber) ValueAdjustments::d).f()).append(' ')
+        .append(((iNumber) ValueAdjustments::D).f()).append('\n');
   }
 
   private static void checkBoxes(StringBuffer builder) {
     builder
-        .append(((iZ) ValueAdjustments::z).f()).append(' ')
-        .append(((iB) ValueAdjustments::b).f()).append(' ')
-        .append(((iC) ValueAdjustments::c).f()).append(' ')
-        .append(((iS) ValueAdjustments::s).f()).append(' ')
-        .append(((iI) ValueAdjustments::i).f()).append(' ')
-        .append(((iJ) ValueAdjustments::j).f()).append(' ')
-        .append(((iF) ValueAdjustments::f).f()).append(' ')
-        .append(((iD) ValueAdjustments::d).f()).append('\n');
+        .append(((iBoolean) ValueAdjustments::z).f()).append(' ')
+        .append(((iByte) ValueAdjustments::b).f()).append(' ')
+        .append(((iCharacter) ValueAdjustments::c).f()).append(' ')
+        .append(((iShort) ValueAdjustments::s).f()).append(' ')
+        .append(((iInteger) ValueAdjustments::i).f()).append(' ')
+        .append(((iLong) ValueAdjustments::j).f()).append(' ')
+        .append(((iFloat) ValueAdjustments::f).f()).append(' ')
+        .append(((iDouble) ValueAdjustments::d).f()).append('\n');
   }
 
   private static void checkDouble(StringBuffer builder) {
diff --git a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
index 803d760..2159d6b 100644
--- a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
@@ -38,14 +38,14 @@
 public abstract class D8IncrementalRunExamplesAndroidOTest
     extends RunExamplesAndroidOTest<D8Command.Builder> {
 
-  abstract class D8IncrementalTestRunner extends TestRunner {
+  abstract class D8IncrementalTestRunner extends TestRunner<D8IncrementalTestRunner> {
 
     D8IncrementalTestRunner(String testName, String packageName, String mainClass) {
       super(testName, packageName, mainClass);
     }
 
     @Override
-    TestRunner withMinApiLevel(int minApiLevel) {
+    D8IncrementalTestRunner withMinApiLevel(int minApiLevel) {
       return withBuilderTransformation(builder -> builder.setMinApiLevel(minApiLevel));
     }
 
@@ -154,12 +154,11 @@
         builder = transformation.apply(builder);
       }
       builder = builder.setOutputMode(outputMode);
-      builder = builder.addLibraryFiles(
-          Paths.get(ToolHelper.getAndroidJar(builder.getMinApiLevel())));
       if (output != null) {
         builder = builder.setOutputPath(output);
       }
-      addLibraryReference(builder, Paths.get(ToolHelper.getAndroidJar(builder.getMinApiLevel())));
+      addLibraryReference(builder, Paths.get(ToolHelper.getAndroidJar(
+          androidJarVersion == null ? builder.getMinApiLevel() : androidJarVersion)));
       D8Command command = builder.build();
       try {
         return ToolHelper.runD8(command, this::combinedOptionConsumer);
@@ -288,6 +287,7 @@
     Assert.assertArrayEquals(expectedFileNames, dexFiles);
   }
 
+  @Override
   abstract D8IncrementalTestRunner test(String testName, String packageName, String mainClass);
 
   static byte[] readFromResource(Resource resource) throws IOException {
diff --git a/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
index d707614..c2ac969 100644
--- a/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
@@ -52,6 +52,11 @@
       builder.addLibraryResourceProvider(
           PreloadedClassFileProvider.fromArchive(location));
     }
+
+    @Override
+    D8LazyTestRunner self() {
+      return this;
+    }
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/D8NonLazyRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8NonLazyRunExamplesAndroidOTest.java
index 5b2977c..99fe81c 100644
--- a/src/test/java/com/android/tools/r8/D8NonLazyRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8NonLazyRunExamplesAndroidOTest.java
@@ -23,7 +23,13 @@
 
     @Override
     void addLibraryReference(D8Command.Builder builder, Path location) throws IOException {
-      builder.addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(builder.getMinApiLevel())));
+      builder.addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(
+          androidJarVersion == null ? builder.getMinApiLevel() : androidJarVersion)));
+    }
+
+    @Override
+    D8LazyTestRunner self() {
+      return this;
     }
   }
 
diff --git a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
index 6f51624..7a56e3c 100644
--- a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
@@ -4,33 +4,58 @@
 
 package com.android.tools.r8;
 
+import static com.android.tools.r8.dex.Constants.ANDROID_K_API;
+import static com.android.tools.r8.dex.Constants.ANDROID_O_API;
+
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.InternalCompilerError;
 import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.utils.OffOrAuto;
+import java.io.IOException;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.function.UnaryOperator;
+import org.hamcrest.core.CombinableMatcher;
+import org.hamcrest.core.IsInstanceOf;
+import org.hamcrest.core.StringContains;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.internal.matchers.ThrowableMessageMatcher;
 
 public class D8RunExamplesAndroidOTest extends RunExamplesAndroidOTest<D8Command.Builder> {
 
-  class D8TestRunner extends TestRunner {
+  class D8TestRunner extends TestRunner<D8TestRunner> {
 
     D8TestRunner(String testName, String packageName, String mainClass) {
       super(testName, packageName, mainClass);
     }
 
     @Override
-    TestRunner withMinApiLevel(int minApiLevel) {
+    D8TestRunner withMinApiLevel(int minApiLevel) {
       return withBuilderTransformation(builder -> builder.setMinApiLevel(minApiLevel));
     }
 
+    D8TestRunner withClasspath(Path... classpath) {
+      return withBuilderTransformation(b -> {
+        try {
+          return b.addClasspathFiles(classpath);
+        } catch (IOException e) {
+          throw new AssertionError(e);
+        }
+      });
+    }
+
+
     @Override
     void build(Path inputFile, Path out) throws Throwable {
       D8Command.Builder builder = D8Command.builder();
       for (UnaryOperator<D8Command.Builder> transformation : builderTransformations) {
         builder = transformation.apply(builder);
       }
-      builder.addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(builder.getMinApiLevel())));
+      builder.addLibraryFiles(
+          Paths.get(
+              ToolHelper.getAndroidJar(
+                  androidJarVersion == null ? builder.getMinApiLevel() : androidJarVersion)));
       D8Command command = builder.addProgramFiles(inputFile).setOutputPath(out).build();
       try {
         ToolHelper.runD8(command, this::combinedOptionConsumer);
@@ -40,10 +65,462 @@
         throw re.getCause() == null ? re : re.getCause();
       }
     }
+
+    @Override
+    D8TestRunner self() {
+      return this;
+    }
   }
 
+  @Test
+  public void testDefaultInInterfaceWithoutDesugaring() throws Throwable {
+    // lib1: interface A { default String foo() { return "A"; } }
+    D8TestRunner lib1 =
+        test("testDefaultInInterfaceWithoutDesugaring", "desugaringwithmissingclasslib1", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Off)
+            .withMinApiLevel(ANDROID_K_API);
+    try  {
+      lib1.build();
+
+      // compilation should have failed on CompilationError since A is declaring a default method.
+      Assert.fail();
+    } catch (CompilationError | CompilationException e) {
+      // Expected.
+    }
+  }
+
+  @Test
+  public void testMissingInterfaceDesugared() throws Throwable {
+    // lib1: interface A { default String foo() { return "A"; } }
+    D8TestRunner lib1 =
+        test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withMinApiLevel(ANDROID_K_API);
+    lib1.build();
+
+    // lib2: interface B extends A { default String foo() { return "B"; } }
+    // lib2 is compiled with full classpath
+    D8TestRunner lib2 =
+        test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(ANDROID_K_API);
+    lib2.build();
+
+    // test: class ImplementMethodsWithDefault implements A, B {} should get its foo implementation
+    // from B.
+    // test is compiled with incomplete classpath: lib2 is missing so ImplementMethodsWithDefault is
+    // missing one of it interfaces.
+    D8TestRunner test =
+        test("desugaringwithmissingclasstest1", "desugaringwithmissingclasstest1", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(ANDROID_K_API);
+    test.build();
+
+    // TODO check compilation warnings are correctly reported
+    // B is missing so compiled code makes no sense, no need to test execution.
+  }
+
+  @Test
+  public void testMissingInterfaceDesugared2AndroidK() throws Throwable {
+    int minApi = ANDROID_K_API;
+
+    // lib1: interface A { default String foo() { return "A"; } }
+    D8TestRunner lib1 =
+        test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withMinApiLevel(minApi);
+    Path lib1Dex = lib1.build();
+
+    // lib2: interface B extends A { default String foo() { return "B"; } }
+    // lib2 is compiled with full classpath
+    D8TestRunner lib2 =
+        test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(minApi);
+    Path lib2Dex = lib2.build();
+
+    // lib3:  class C implements A {}
+    // lib3 is compiled with full classpath
+    D8TestRunner lib3 =
+        test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(minApi);
+    Path lib3Dex = lib3.build();
+
+    // test: class ImplementMethodsWithDefault extends C implements B should get its foo
+    // implementation from B.
+    // test is compiled with incomplete classpath: lib2 and lib3 are missing so
+    // ImplementMethodsWithDefault is missing all its hierarchy.
+    D8TestRunner test =
+        test("desugaringwithmissingclasstest2", "desugaringwithmissingclasstest2", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(minApi);
+    Path testDex = test.build();
+    // TODO check compilation warnings are correctly reported
+
+    // Missing interface B is causing the wrong code to be executed.
+    if (ToolHelper.artSupported()) {
+      thrown.expect(AssertionError.class);
+      execute(
+          "testMissingInterfaceDesugared2AndroidK",
+          "desugaringwithmissingclasstest2.Main",
+          new Path[] {
+              lib1.getInputJar(), lib2.getInputJar(), lib3.getInputJar(), test.getInputJar()
+          },
+          new Path[] {lib1Dex, lib2Dex, lib3Dex, testDex});
+    }
+  }
+
+  @Test
+  public void testMissingInterfaceDesugared2AndroidO() throws Throwable {
+    int minApi = ANDROID_O_API;
+    // lib1: interface A { default String foo() { return "A"; } }
+    D8TestRunner lib1 =
+        test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withMinApiLevel(minApi);
+    Path lib1Dex = lib1.build();
+
+    // lib2: interface B extends A { default String foo() { return "B"; } }
+    // lib2 is compiled with full classpath
+    D8TestRunner lib2 =
+        test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(minApi);
+    Path lib2Dex = lib2.build();
+
+    // lib3:  class C implements A {}
+    // lib3 is compiled with full classpath
+    D8TestRunner lib3 =
+        test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(minApi);
+    Path lib3Dex = lib3.build();
+
+    // test: class ImplementMethodsWithDefault extends C implements B should get its foo
+    // implementation from B.
+    // test is compiled with incomplete classpath: lib2 and lib3 are missing so
+    // ImplementMethodsWithDefault is missing all its hierarchy.
+    D8TestRunner test =
+        test("desugaringwithmissingclasstest2", "desugaringwithmissingclasstest2", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(minApi);
+    Path testDex = test.build();
+    execute(
+        "testMissingInterfaceDesugared2AndroidO",
+        "desugaringwithmissingclasstest2.Main",
+        new Path[] {
+          lib1.getInputJar(), lib2.getInputJar(), lib3.getInputJar(), test.getInputJar()
+        },
+        new Path[] {lib1Dex, lib2Dex, lib3Dex, testDex});
+  }
+
+  @Test
+  public void testCallToMissingSuperInterfaceDesugaredAndroidK() throws Throwable {
+
+    int minApi = ANDROID_K_API;
+    // lib1: interface A { default String foo() { return "A"; } }
+    D8TestRunner lib1 =
+        test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withMinApiLevel(minApi);
+    Path lib1Dex = lib1.build();
+
+    // lib2: interface B extends A { default String foo() { return "B"; } }
+    // lib2 is compiled with full classpath
+    D8TestRunner lib2 =
+        test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(minApi);
+    Path lib2Dex = lib2.build();
+
+    // lib3:  class C implements A {}
+    // lib3 is compiled with full classpath
+    D8TestRunner lib3 =
+        test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(minApi);
+    Path lib3Dex = lib3.build();
+
+    // test: class ImplementMethodsWithDefault extends C implements B
+    // { String getB() { return B.super.foo(); }
+    // Should be able to call implementation from B.
+    // test is compiled with incomplete classpath: lib2, i.e. B definition, is missing.
+    D8TestRunner test =
+        test("desugaringwithmissingclasstest3", "desugaringwithmissingclasstest3", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar(), lib3.getInputJar())
+            .withMinApiLevel(minApi);
+    Path testDex = test.build();
+    // TODO check compilation warnings are correctly reported
+
+    // Missing interface B is causing the wrong method to be executed.
+    if (ToolHelper.artSupported()) {
+      thrown.expect(AssertionError.class);
+      execute(
+          "testCallToMissingSuperInterfaceDesugaredAndroidK",
+          "desugaringwithmissingclasstest3.Main",
+          new Path[] {
+              lib1.getInputJar(), lib2.getInputJar(), lib3.getInputJar(), test.getInputJar()
+          },
+          new Path[] {lib1Dex, lib2Dex, lib3Dex, testDex});
+    }
+  }
+
+  @Test
+  public void testCallToMissingSuperInterfaceDesugaredAndroidO() throws Throwable {
+    int minApi = ANDROID_O_API;
+    // lib1: interface A { default String foo() { return "A"; } }
+    D8TestRunner lib1 =
+        test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withMinApiLevel(minApi);
+    Path lib1Dex = lib1.build();
+
+    // lib2: interface B extends A { default String foo() { return "B"; } }
+    // lib2 is compiled with full classpath
+    D8TestRunner lib2 =
+        test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(minApi);
+    Path lib2Dex = lib2.build();
+
+    // lib3:  class C implements A {}
+    // lib3 is compiled with full classpath
+    D8TestRunner lib3 =
+        test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(minApi);
+    Path lib3Dex = lib3.build();
+
+    // test: class ImplementMethodsWithDefault extends C implements B
+    // { String getB() { return B.super.foo(); }
+    // Should be able to call implementation from B.
+    // test is compiled with incomplete classpath: lib2, i.e. B definition, is missing.
+    D8TestRunner test =
+        test("desugaringwithmissingclasstest3", "desugaringwithmissingclasstest3", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar(), lib3.getInputJar())
+            .withMinApiLevel(minApi);
+    Path testDex = test.build();
+    execute(
+        "testCallToMissingSuperInterfaceDesugaredAndroidO",
+        "desugaringwithmissingclasstest3.Main",
+        new Path[] {
+          lib1.getInputJar(), lib2.getInputJar(), lib3.getInputJar(), test.getInputJar()
+        },
+        new Path[] {lib1Dex, lib2Dex, lib3Dex, testDex});
+  }
+
+  @Test
+  public void testMissingSuperDesugaredAndroidK() throws Throwable {
+    int minApi = ANDROID_K_API;
+
+    // lib1: interface A { default String foo() { return "A"; } }
+    D8TestRunner lib1 =
+        test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withMinApiLevel(minApi);
+    lib1.build();
+
+    // lib2: interface B extends A { default String foo() { return "B"; } }
+    // lib2 is compiled with full classpath
+    D8TestRunner lib2 =
+        test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(minApi);
+    lib2.build();
+
+    // lib3:  class C implements A {}
+    // lib3 is compiled with full classpath
+    D8TestRunner lib3 =
+        test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(minApi);
+    lib3.build();
+
+    // test: class ImplementMethodsWithDefault extends C implements B should get its foo
+    // implementation from B.
+    // test is compiled with incomplete classpath: lib3 is missing so
+    // ImplementMethodsWithDefault is missing its super class.
+    D8TestRunner test =
+        test("desugaringwithmissingclasstest2", "desugaringwithmissingclasstest2", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withClasspath(lib2.getInputJar())
+            .withMinApiLevel(minApi);
+    thrown.expect(
+        new CombinableMatcher<CompilationError>(new IsInstanceOf(CompilationError.class))
+        .and(new ThrowableMessageMatcher<CompilationError>(
+            new StringContains("desugaringwithmissingclasstest2.ImplementMethodsWithDefault")))
+        .and(new ThrowableMessageMatcher<CompilationError>(
+            new StringContains("desugaringwithmissingclasslib3.C"))));
+    test.build();
+  }
+
+  @Test
+  public void testMissingSuperDesugaredAndroidO() throws Throwable {
+    int minApi = ANDROID_O_API;
+
+    // lib1: interface A { default String foo() { return "A"; } }
+    D8TestRunner lib1 =
+        test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withMinApiLevel(minApi);
+    Path lib1Dex = lib1.build();
+
+    // lib2: interface B extends A { default String foo() { return "B"; } }
+    // lib2 is compiled with full classpath
+    D8TestRunner lib2 =
+        test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(minApi);
+    Path lib2Dex = lib2.build();
+
+    // lib3:  class C implements A {}
+    // lib3 is compiled with full classpath
+    D8TestRunner lib3 =
+        test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(minApi);
+    Path lib3Dex = lib3.build();
+
+    // test: class ImplementMethodsWithDefault extends C implements B should get its foo
+    // implementation from B.
+    // test is compiled with incomplete classpath: lib3 is missing so
+    // ImplementMethodsWithDefault is missing its super class.
+    D8TestRunner test =
+        test("desugaringwithmissingclasstest2", "desugaringwithmissingclasstest2", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withClasspath(lib2.getInputJar())
+            .withMinApiLevel(minApi);
+    Path testDex = test.build();
+
+    execute(
+        "testMissingSuperDesugaredAndroidO",
+        "desugaringwithmissingclasstest2.Main",
+        new Path[] {
+          lib1.getInputJar(), lib2.getInputJar(), lib3.getInputJar(), test.getInputJar()
+        },
+        new Path[] {lib1Dex, lib2Dex, lib3Dex, testDex});
+  }
+
+  @Test
+  public void testMissingSuperDesugaredWithProgramCrossImplementationAndroidK() throws Throwable {
+    int minApi = ANDROID_K_API;
+
+    // lib1: interface A { default String foo() { return "A"; } }
+    //       interface A2 { default String foo2() { return "A2"; } }
+    D8TestRunner lib1 =
+        test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withMinApiLevel(minApi);
+    Path lib1Dex = lib1.build();
+
+    // lib3: class C { /* content irrelevant }
+    // lib3 is compiled with full classpath
+    D8TestRunner lib3 =
+        test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(minApi);
+    Path lib3Dex = lib3.build();
+
+    // test: class C2 extends C { public String foo2() { return "C2"; } }
+    //       class ImplementMethodsWithDefault extends C2 implements A, A2 {
+    //            public String foo() { return "ImplementMethodsWithDefault"; }
+    //       }
+    // test is compiled with incomplete classpath: lib3 is missing so
+    // C2 is missing its super class. But desugaring should be OK since all
+    // interface methods are explicitly defined in program classes of the hierarchy.
+    D8TestRunner test =
+        test("desugaringwithmissingclasstest4", "desugaringwithmissingclasstest4", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(minApi);
+    Path testDex = test.build();
+
+    execute(
+        "testMissingSuperDesugaredAndroidKWithCrossImplementation",
+        "desugaringwithmissingclasstest4.Main",
+        new Path[] {
+          lib1.getInputJar(), lib3.getInputJar(), test.getInputJar()
+        },
+        new Path[] {lib1Dex, lib3Dex, testDex});
+
+  }
+
+  @Test
+  public void testMissingSuperDesugaredWithClasspathCrossImplementationAndroidK() throws Throwable {
+    int minApi = ANDROID_K_API;
+
+    // lib1: interface A { default String foo() { return "A"; } }
+    //       interface A2 { default String foo2() { return "A2"; } }
+    D8TestRunner lib1 =
+        test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withMinApiLevel(minApi);
+    Path lib1Dex = lib1.build();
+
+    // lib3: class C { /* content irrelevant }
+    // lib3 is compiled with full classpath
+    D8TestRunner lib3 =
+        test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar())
+            .withMinApiLevel(minApi);
+    Path lib3Dex = lib3.build();
+
+    // lib4: class C2 extends C { public String foo2() { return "C2"; } }
+    // lib4 is compiled with full classpath
+    D8TestRunner lib4 =
+        test("desugaringwithmissingclasslib4", "desugaringwithmissingclasslib4", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar(), lib3.getInputJar())
+            .withMinApiLevel(minApi);
+    Path lib4Dex = lib4.build();
+
+    // test: class ImplementMethodsWithDefault extends C2 implements A, A2 {
+    //            public String foo() { return "ImplementMethodsWithDefault"; }
+    //       }
+    // test is compiled with incomplete classpath: lib3 is missing so
+    // C2 is missing its super class. But desugaring should be OK since all
+    // interface methods are explicitly defined in program classes of the hierarchy.
+    D8TestRunner test =
+        test("desugaringwithmissingclasstest4", "desugaringwithmissingclasstest4", "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withClasspath(lib1.getInputJar(), lib4.getInputJar())
+            .withMinApiLevel(minApi);
+    Path testDex = test.build();
+
+    execute(
+        "testMissingSuperDesugaredAndroidKWithCrossImplementation",
+        "desugaringwithmissingclasstest4.Main",
+        new Path[] {
+          lib1.getInputJar(), lib3.getInputJar(), lib4.getInputJar(), test.getInputJar()
+        },
+        new Path[] {lib1Dex, lib3Dex, lib4Dex, testDex});
+
+  }
   @Override
-  TestRunner test(String testName, String packageName, String mainClass) {
+  D8TestRunner test(String testName, String packageName, String mainClass) {
     return new D8TestRunner(testName, packageName, mainClass);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 8ffab3c..7cb47e2 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -142,21 +142,6 @@
           .put("975-iface-private", Constants.ANDROID_N_API)
           .build();
 
-  // Tests requiring interface method desugaring because they explicitly or
-  // implicitly define static or default interface methods and are enabled
-  // on pre-N Android.
-  private static List<String> enableInterfaceMethodDesugaring =
-      ImmutableList.of(
-          "563-checker-invoke-super",
-          "604-hot-static-interface",
-          "961-default-iface-resolution-gen",
-          "963-default-range-smali",
-          "965-default-verify",
-          "967-default-ame",
-          "969-iface-super",
-          "978-virtual-interface"
-      );
-
   // Tests that timeout when run with Art.
   private static final Multimap<String, TestCondition> timeoutOrSkipRunWithArt =
       new ImmutableListMultimap.Builder<String, TestCondition>()
@@ -205,8 +190,9 @@
 
   // Tests that are never compiled or run.
   private static List<String> skipAltogether = ImmutableList.of(
-      // This test contains an invalid type hierarchy, which we cannot currently handle.
+      // Those tests contains an invalid type hierarchy, which we cannot currently handle.
       "065-mismatched-implements",
+      "066-mismatched-super",
       // This test contains invalid dex code that reads uninitialized registers after an
       // an instruction that would in any case throw (implicit via aget null 0).
       "706-jit-skip-compilation",
@@ -1130,7 +1116,12 @@
         Integer minSdkVersion = needMinSdkVersion.get(name);
         if (minSdkVersion != null) {
           builder.setMinApiLevel(minSdkVersion);
+          builder.addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(minSdkVersion)));
+        } else {
+          builder.addLibraryFiles(Paths.get(
+              ToolHelper.getAndroidJar(Constants.DEFAULT_ANDROID_API)));
         }
+
         D8Output output = D8.run(builder.build());
         output.write(Paths.get(resultPath));
         break;
@@ -1153,9 +1144,6 @@
         ToolHelper.runR8(
             builder.build(),
             options -> {
-              if (enableInterfaceMethodDesugaring.contains(name)) {
-                options.interfaceMethodDesugaring = OffOrAuto.Auto;
-              }
               if (disableInlining) {
                 options.inlineAccessors = false;
               }
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index 6c45f40..e63b155 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -46,14 +46,14 @@
         .run();
   }
 
-  class R8TestRunner extends TestRunner {
+  class R8TestRunner extends TestRunner<R8TestRunner> {
 
     R8TestRunner(String testName, String packageName, String mainClass) {
       super(testName, packageName, mainClass);
     }
 
     @Override
-    TestRunner withMinApiLevel(int minApiLevel) {
+    R8TestRunner withMinApiLevel(int minApiLevel) {
       return withBuilderTransformation(builder -> builder.setMinApiLevel(minApiLevel));
     }
 
@@ -64,16 +64,23 @@
         for (UnaryOperator<R8Command.Builder> transformation : builderTransformations) {
           builder = transformation.apply(builder);
         }
+        builder.addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(
+            androidJarVersion == null ? builder.getMinApiLevel() : androidJarVersion)));
         R8Command command = builder.addProgramFiles(inputFile).setOutputPath(out).build();
         ToolHelper.runR8(command, this::combinedOptionConsumer);
       } catch (ExecutionException e) {
         throw e.getCause();
       }
     }
+
+    @Override
+    R8TestRunner self() {
+      return this;
+    }
   }
 
   @Override
-  TestRunner test(String testName, String packageName, String mainClass) {
+  R8TestRunner test(String testName, String packageName, String mainClass) {
     return new R8TestRunner(testName, packageName, mainClass);
   }
 
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidNTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidNTest.java
index 7594b0c..9d745f6 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidNTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidNTest.java
@@ -133,7 +133,8 @@
     thrown.expect(ApiLevelException.class);
     test("staticinterfacemethods-error-due-to-min-sdk", "interfacemethods",
         "StaticInterfaceMethods")
-        .run();
+        .withInterfaceMethodDesugaring(OffOrAuto.Off)
+       .run();
   }
 
   @Test
@@ -149,6 +150,7 @@
     thrown.expect(ApiLevelException.class);
     test("defaultmethods-error-due-to-min-sdk", "interfacemethods",
         "DefaultMethods")
+        .withInterfaceMethodDesugaring(OffOrAuto.Off)
         .run();
   }
 
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
index 0ca84a1..7a0e793 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
@@ -22,29 +22,35 @@
 import com.android.tools.r8.utils.OffOrAuto;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import java.io.IOException;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 import java.util.function.UnaryOperator;
+import java.util.stream.Collectors;
 import org.junit.Assert;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.junit.rules.TemporaryFolder;
 
-public abstract class RunExamplesAndroidOTest<B> {
+public abstract class RunExamplesAndroidOTest
+      <B extends BaseCommand.Builder<? extends BaseCommand, B>> {
   static final String EXAMPLE_DIR = ToolHelper.EXAMPLES_ANDROID_O_BUILD_DIR;
 
-  abstract class TestRunner {
+  abstract class TestRunner<C extends TestRunner<C>> {
     final String testName;
     final String packageName;
     final String mainClass;
 
+    Integer androidJarVersion = null;
+
     final List<Consumer<InternalOptions>> optionConsumers = new ArrayList<>();
     final List<Consumer<DexInspector>> dexInspectorChecks = new ArrayList<>();
     final List<UnaryOperator<B>> builderTransformations = new ArrayList<>();
@@ -55,20 +61,22 @@
       this.mainClass = mainClass;
     }
 
-    TestRunner withDexCheck(Consumer<DexInspector> check) {
+    abstract C self();
+
+    C withDexCheck(Consumer<DexInspector> check) {
       dexInspectorChecks.add(check);
-      return this;
+      return self();
     }
 
-    TestRunner withClassCheck(Consumer<FoundClassSubject> check) {
+    C withClassCheck(Consumer<FoundClassSubject> check) {
       return withDexCheck(inspector -> inspector.forAllClasses(check));
     }
 
-    TestRunner withMethodCheck(Consumer<FoundMethodSubject> check) {
+    C withMethodCheck(Consumer<FoundMethodSubject> check) {
       return withClassCheck(clazz -> clazz.forAllMethods(check));
     }
 
-    <T extends InstructionSubject> TestRunner withInstructionCheck(
+    <T extends InstructionSubject> C withInstructionCheck(
         Predicate<InstructionSubject> filter, Consumer<T> check) {
       return withMethodCheck(method -> {
         if (method.isAbstract()) {
@@ -81,22 +89,22 @@
       });
     }
 
-    TestRunner withOptionConsumer(Consumer<InternalOptions> consumer) {
+    C withOptionConsumer(Consumer<InternalOptions> consumer) {
       optionConsumers.add(consumer);
-      return this;
+      return self();
     }
 
-    TestRunner withInterfaceMethodDesugaring(OffOrAuto behavior) {
+    C withInterfaceMethodDesugaring(OffOrAuto behavior) {
       return withOptionConsumer(o -> o.interfaceMethodDesugaring = behavior);
     }
 
-    TestRunner withTryWithResourcesDesugaring(OffOrAuto behavior) {
+    C withTryWithResourcesDesugaring(OffOrAuto behavior) {
       return withOptionConsumer(o -> o.tryWithResourcesDesugaring = behavior);
     }
 
-    TestRunner withBuilderTransformation(UnaryOperator<B> builderTransformation) {
+    C withBuilderTransformation(UnaryOperator<B> builderTransformation) {
       builderTransformations.add(builderTransformation);
-      return this;
+      return self();
     }
 
     void combinedOptionConsumer(InternalOptions options) {
@@ -105,13 +113,25 @@
       }
     }
 
+    Path build() throws Throwable {
+      Path inputFile = getInputJar();
+      Path out = temp.getRoot().toPath().resolve(testName + ZIP_EXTENSION);
+
+      build(inputFile, out);
+      return out;
+    }
+
+    Path getInputJar() {
+      return Paths.get(EXAMPLE_DIR, packageName + JAR_EXTENSION);
+    }
+
     void run() throws Throwable {
       if (minSdkErrorExpected(testName)) {
         thrown.expect(ApiLevelException.class);
       }
 
       String qualifiedMainClass = packageName + "." + mainClass;
-      Path inputFile = Paths.get(EXAMPLE_DIR, packageName + JAR_EXTENSION);
+      Path inputFile = getInputJar();
       Path out = temp.getRoot().toPath().resolve(testName + ZIP_EXTENSION);
 
       build(inputFile, out);
@@ -120,11 +140,6 @@
         return;
       }
 
-      boolean expectedToFail = expectedToFail(testName);
-      if (expectedToFail) {
-        thrown.expect(Throwable.class);
-      }
-
       if (!dexInspectorChecks.isEmpty()) {
         DexInspector inspector = new DexInspector(out);
         for (Consumer<DexInspector> check : dexInspectorChecks) {
@@ -132,21 +147,16 @@
         }
       }
 
-      String output = ToolHelper.runArtNoVerificationErrors(out.toString(), qualifiedMainClass);
-      if (!expectedToFail) {
-        ToolHelper.ProcessResult javaResult =
-            ToolHelper.runJava(ImmutableList.of(inputFile.toString()), qualifiedMainClass);
-        assertEquals("JVM run failed", javaResult.exitCode, 0);
-        assertTrue(
-            "JVM output does not match art output.\n\tjvm: "
-                + javaResult.stdout
-                + "\n\tart: "
-                + output,
-            output.equals(javaResult.stdout));
-      }
+      execute(testName, qualifiedMainClass, new Path[]{inputFile}, new Path[]{out});
     }
 
-    abstract TestRunner withMinApiLevel(int minApiLevel);
+    abstract C withMinApiLevel(int minApiLevel);
+
+    C withAndroidJar(int androidJarVersion) {
+      assert this.androidJarVersion == null;
+      this.androidJarVersion = androidJarVersion;
+      return self();
+    }
 
     abstract void build(Path inputFile, Path out) throws Throwable;
   }
@@ -164,7 +174,12 @@
               // Dex version not supported
               "invokepolymorphic",
               "invokecustom",
-              "invokecustom2"
+              "invokecustom2",
+              "DefaultMethodInAndroidJar25",
+              "StaticMethodInAndroidJar25",
+              "testMissingInterfaceDesugared2AndroidO",
+              "testCallToMissingSuperInterfaceDesugaredAndroidO",
+              "testMissingSuperDesugaredAndroidO"
           ),
           DexVm.ART_5_1_1, ImmutableList.of(
               // API not supported
@@ -173,7 +188,12 @@
               // Dex version not supported
               "invokepolymorphic",
               "invokecustom",
-              "invokecustom2"
+              "invokecustom2",
+              "DefaultMethodInAndroidJar25",
+              "StaticMethodInAndroidJar25",
+              "testMissingInterfaceDesugared2AndroidO",
+              "testCallToMissingSuperInterfaceDesugaredAndroidO",
+              "testMissingSuperDesugaredAndroidO"
           ),
           DexVm.ART_6_0_1, ImmutableList.of(
               // API not supported
@@ -182,7 +202,12 @@
               // Dex version not supported
               "invokepolymorphic",
               "invokecustom",
-              "invokecustom2"
+              "invokecustom2",
+              "DefaultMethodInAndroidJar25",
+              "StaticMethodInAndroidJar25",
+              "testMissingInterfaceDesugared2AndroidO",
+              "testCallToMissingSuperInterfaceDesugaredAndroidO",
+              "testMissingSuperDesugaredAndroidO"
           ),
           DexVm.ART_7_0_0, ImmutableList.of(
               // API not supported
@@ -190,7 +215,10 @@
               // Dex version not supported
               "invokepolymorphic",
               "invokecustom",
-              "invokecustom2"
+              "invokecustom2",
+              "testMissingInterfaceDesugared2AndroidO",
+              "testCallToMissingSuperInterfaceDesugaredAndroidO",
+              "testMissingSuperDesugaredAndroidO"
           ),
           DexVm.ART_DEFAULT, ImmutableList.of(
           )
@@ -266,6 +294,24 @@
   }
 
   @Test
+  public void desugarDefaultMethodInAndroidJar25() throws Throwable {
+    test("DefaultMethodInAndroidJar25", "desugaringwithandroidjar25", "DefaultMethodInAndroidJar25")
+        .withMinApiLevel(ANDROID_K_API)
+        .withAndroidJar(ANDROID_O_API)
+        .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+        .run();
+  }
+
+  @Test
+  public void desugarStaticMethodInAndroidJar25() throws Throwable {
+    test("StaticMethodInAndroidJar25", "desugaringwithandroidjar25", "StaticMethodInAndroidJar25")
+        .withMinApiLevel(ANDROID_K_API)
+        .withAndroidJar(ANDROID_O_API)
+        .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+        .run();
+  }
+
+  @Test
   public void lambdaDesugaringValueAdjustments() throws Throwable {
     test("lambdadesugaring-value-adjustments", "lambdadesugaring", "ValueAdjustments")
         .withMinApiLevel(ANDROID_K_API)
@@ -315,5 +361,34 @@
         .run();
   }
 
-  abstract TestRunner test(String testName, String packageName, String mainClass);
+  abstract RunExamplesAndroidOTest<B>.TestRunner<?> test(String testName, String packageName, String mainClass);
+
+  void execute(
+      String testName,
+      String qualifiedMainClass, Path[] jars, Path[] dexes)
+      throws IOException {
+
+    boolean expectedToFail = expectedToFail(testName);
+    if (expectedToFail) {
+      thrown.expect(Throwable.class);
+    }
+    String output = ToolHelper.runArtNoVerificationErrors(
+        Arrays.stream(dexes).map(path -> path.toString()).collect(Collectors.toList()),
+        qualifiedMainClass,
+        null);
+    if (!expectedToFail) {
+      ToolHelper.ProcessResult javaResult =
+          ToolHelper.runJava(
+              Arrays.stream(jars).map(path -> path.toString()).collect(Collectors.toList()),
+              qualifiedMainClass);
+      assertEquals("JVM run failed", javaResult.exitCode, 0);
+      assertTrue(
+          "JVM output does not match art output.\n\tjvm: "
+              + javaResult.stdout
+              + "\n\tart: "
+              + output,
+          output.equals(javaResult.stdout));
+    }
+  }
+
 }
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 4f84303..3767286 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -14,15 +14,11 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.shaking.ProguardConfiguration;
 import com.android.tools.r8.shaking.ProguardConfigurationParser;
-import com.android.tools.r8.shaking.ProguardConfigurationRule;
 import com.android.tools.r8.shaking.ProguardRuleParserException;
-import com.android.tools.r8.shaking.RootSetBuilder;
-import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
index fa78fd0..c89e792 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -135,6 +135,7 @@
             .setOutputPath(dexOutputDir)
             .setMinApiLevel(minSdk)
             .setMode(CompilationMode.DEBUG)
+            .addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(minSdk)))
             .build(),
         optionsConsumer);
     return dexOutputDir.resolve("classes.dex");
diff --git a/src/test/java/com/android/tools/r8/debug/LocalsTest.java b/src/test/java/com/android/tools/r8/debug/LocalsTest.java
index 12fd23d..35aeab2 100644
--- a/src/test/java/com/android/tools/r8/debug/LocalsTest.java
+++ b/src/test/java/com/android/tools/r8/debug/LocalsTest.java
@@ -9,6 +9,7 @@
 import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants.Tag;
 import org.apache.harmony.jpda.tests.framework.jdwp.Value;
 import org.junit.Assert;
+import org.junit.Ignore;
 import org.junit.Test;
 
 /**
@@ -19,6 +20,50 @@
   public static final String SOURCE_FILE = "Locals.java";
 
   @Test
+  // TODO(b/65402086) Remove @ignore when debug behavior will be fixed.
+  @Ignore
+  public void testLocalConstantBis() throws Throwable {
+    final String className = "Locals";
+    final String methodName = "localConstantBis";
+    runDebugTest(className,
+        breakpoint(className, methodName),
+        run(),
+        checkLine(SOURCE_FILE, 332),
+        checkNoLocal("result"),
+        stepOver(),
+        checkLine(SOURCE_FILE, 333),
+        checkLocal("result", Value.createInt(0)),
+        stepOver(),
+        checkLine(SOURCE_FILE, 334),
+        checkLocal("result", Value.createInt(0)),
+        stepOver(),
+        checkLine(SOURCE_FILE, 338),
+        checkLocal("result", Value.createInt(1)),
+        run());
+  }
+
+  @Test
+  public void testLocalConstant() throws Throwable {
+    final String className = "Locals";
+    final String methodName = "localConstant";
+    runDebugTest(className,
+        breakpoint(className, methodName),
+        run(),
+        checkLine(SOURCE_FILE, 322),
+        checkNoLocal("result1"),
+        checkNoLocal("result2"),
+        stepOver(),
+        checkLine(SOURCE_FILE, 323),
+        checkNoLocal("result1"),
+        checkNoLocal("result2"),
+        stepOver(),
+        checkLine(SOURCE_FILE, 324),
+        checkLocal("result1"),
+        checkNoLocal("result2"),
+        run());
+  }
+
+  @Test
   public void testNoLocal() throws Throwable {
     final String className = "Locals";
     final String methodName = "noLocals";
diff --git a/src/test/java/com/android/tools/r8/desugar/BasicTestDependenciesDesugaringTest.java b/src/test/java/com/android/tools/r8/desugar/BasicTestDependenciesDesugaringTest.java
index b6debff..39d90d6 100644
--- a/src/test/java/com/android/tools/r8/desugar/BasicTestDependenciesDesugaringTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/BasicTestDependenciesDesugaringTest.java
@@ -45,14 +45,6 @@
   }
 
   private static Set<String> knownIssues = Sets.newHashSet(new String[]{
-      "espresso-core-3.0.0.jar",
-      "hamcrest-integration-1.3.jar",
-      "hamcrest-library-1.3.jar",
-      "junit-4.12.jar",
-      "support-core-ui-25.4.0.jar",
-      "support-media-compat-25.4.0.jar",
-      "support-fragment-25.4.0.jar",
-      "support-compat-25.4.0.jar"
   });
 
   @Rule
@@ -98,4 +90,18 @@
         .build(),
         options -> options.interfaceMethodDesugaring = OffOrAuto.Auto);
   }
+
+  @Test
+  public void testCompileDontDesugarDefault() throws IOException, CompilationException {
+    if (knownIssues.contains(name)) {
+      thrown.expect(CompilationError.class);
+    }
+    ToolHelper.runD8(
+        D8Command.builder().addClasspathFiles(classpath)
+        .addProgramFiles(toCompile)
+        .addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(Constants.ANDROID_K_API)))
+        .setMinApiLevel(Constants.ANDROID_K_API)
+        .build(),
+        options -> options.interfaceMethodDesugaring = OffOrAuto.Off);
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java b/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java
index 531831b..835474b 100644
--- a/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java
+++ b/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.utils.DexInspector.ClassSubject;
 import com.android.tools.r8.utils.DexInspector.MethodSubject;
 import com.google.common.collect.ImmutableList;
+import org.junit.Ignore;
 import org.junit.Test;
 
 public class DebugLocalTests extends JasminTestBase {
@@ -116,6 +117,7 @@
   }
 
   @Test
+  @Ignore("b/65430598")
   public void testNoLocalInfoOnStack() throws Exception {
     JasminBuilder builder = new JasminBuilder();
     JasminBuilder.ClassBuilder clazz = builder.addClass("Test");