Fixing few points in Enqueuer related to b/111020945

This is a cumulative fix for few minor problems in Enueuer
related to lambdas and default interface methods. Namely:

  * fixing lookup for super calls targeting default interface
    methods
  * fixing lookup for interface calls targeting default interface
    methods
  * trying to address problem with calls to lambda implementation
    methods from synthesized lambda class by registering calls
    to those methods from lambda-instantiation points
  * tracing lambda instantiation in similar way we trace new
    instances created via new-instance instruction
  * registerting reference to main lambda SAM interface type
    from callsite

Bug: 111020945
Change-Id: I78fd3400a585dfa97beb99b8730db4c7f93bdeb9
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index 69a744f..55c4134 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -9,6 +9,7 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableMap.Builder;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
@@ -125,6 +126,12 @@
     if (resolutionResult.asListOfTargets().isEmpty()) {
       return null;
     }
+    // According to
+    // https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.invokespecial, use
+    // the "symbolic reference" if the "symbolic reference" does not name a class.
+    if (definitionFor(method.holder).isInterface()) {
+      return resolveMethodOnInterface(method.holder, method).asSingleTarget();
+    }
     // Then, resume on the search, but this time, starting from the holder of the caller.
     DexClass contextClass = definitionFor(invocationContext);
     if (contextClass == null || contextClass.superType == null) {
@@ -536,14 +543,14 @@
 
   private static class MultiResultBuilder {
 
-    private ImmutableList.Builder<DexEncodedMethod> builder;
+    private ImmutableSet.Builder<DexEncodedMethod> builder;
     private DexEncodedMethod singleResult;
 
     void add(DexEncodedMethod result) {
       if (builder != null) {
         builder.add(result);
-      } else if (singleResult != null) {
-        builder = ImmutableList.builder();
+      } else if (singleResult != null && !singleResult.equals(result)) {
+        builder = ImmutableSet.builder();
         builder.add(singleResult, result);
         singleResult = null;
       } else {
@@ -553,7 +560,7 @@
 
     ResolutionResult build() {
       if (builder != null) {
-        return new MultiResult(builder.build());
+        return new MultiResult(builder.build().asList());
       } else {
         return singleResult;
       }
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
index a8bda9e..ce361e8 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
@@ -169,6 +169,10 @@
     return super.lookupSuperTarget(method, invocationContext);
   }
 
+  protected boolean hasAnyInstantiatedLambdas(DexType type) {
+    return true; // Don't know, there might be.
+  }
+
   // For mapping invoke interface instruction to target methods.
   public Set<DexEncodedMethod> lookupInterfaceTargets(DexMethod method) {
     // First check that there is a target for this invoke-interface to hit. If there is none,
@@ -177,11 +181,38 @@
     if (topTarget.asResultOfResolve() == null) {
       return null;
     }
-    Set<DexType> set = subtypes(method.holder);
-    if (set.isEmpty()) {
-      return Collections.emptySet();
-    }
+
     Set<DexEncodedMethod> result = new HashSet<>();
+    if (topTarget.hasSingleTarget()) {
+      // Add default interface methods to the list of targets.
+      //
+      // This helps to make sure we take into account synthesized lambda classes
+      // that we are not aware of. Like in the following example, we know that all
+      // classes, XX in this case, override B::bar(), but there are also synthesized
+      // classes for lambda which don't, so we still need default method to be live.
+      //
+      //   public static void main(String[] args) {
+      //     X x = () -> {};
+      //     x.bar();
+      //   }
+      //
+      //   interface X {
+      //     void foo();
+      //     default void bar() { }
+      //   }
+      //
+      //   class XX implements X {
+      //     public void foo() { }
+      //     public void bar() { }
+      //   }
+      //
+      DexEncodedMethod singleTarget = topTarget.asSingleTarget();
+      if (singleTarget.getCode() != null && hasAnyInstantiatedLambdas(singleTarget.method.holder)) {
+        result.add(singleTarget);
+      }
+    }
+
+    Set<DexType> set = subtypes(method.holder);
     for (DexType type : set) {
       DexClass clazz = definitionFor(type);
       // Default methods are looked up when looking at a specific subtype that does not
@@ -212,8 +243,7 @@
    */
   public Set<DexEncodedMethod> lookupLambdaImplementedMethods(
       DexCallSite callSite, Reporter reporter) {
-    List<DexType> callSiteInterfaces =
-        LambdaDescriptor.getInterfaces(callSite, this, dexItemFactory);
+    List<DexType> callSiteInterfaces = LambdaDescriptor.getInterfaces(callSite, this);
     if (callSiteInterfaces == null) {
       if (!isStringConcat(callSite.bootstrapMethod)) {
         Diagnostic message =
diff --git a/src/main/java/com/android/tools/r8/graph/UseRegistry.java b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
index 49145fa..f8608d6 100644
--- a/src/main/java/com/android/tools/r8/graph/UseRegistry.java
+++ b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
@@ -86,6 +86,10 @@
   public void registerCallSite(DexCallSite callSite) {
     registerMethodHandle(callSite.bootstrapMethod);
 
+    // Lambda metafactory will use this type as the main SAM
+    // interface for the dynamically created lambda class.
+    registerTypeReference(callSite.methodProto.returnType);
+
     // Register bootstrap method arguments.
     // Only Type, MethodHandle, and MethodType need to be registered.
     for (DexValue arg : callSite.bootstrapArgs) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
index 94bf56c..9d16497 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
@@ -37,7 +37,7 @@
   final DexString name;
   final DexProto erasedProto;
   final DexProto enforcedProto;
-  final DexMethodHandle implHandle;
+  public final DexMethodHandle implHandle;
 
   final List<DexType> interfaces = new ArrayList<>();
   final Set<DexProto> bridges = Sets.newIdentityHashSet();
@@ -173,7 +173,6 @@
       return false;
     }
 
-
     boolean staticTarget = implHandle.type.isInvokeStatic();
     boolean instanceTarget = implHandle.type.isInvokeInstance() || implHandle.type.isInvokeDirect();
     boolean initTarget = implHandle.type.isInvokeConstructor();
@@ -222,13 +221,23 @@
    * Matches call site for lambda metafactory invocation pattern and
    * returns extracted match information, or null if match failed.
    */
-  static LambdaDescriptor infer(DexCallSite callSite, AppInfo appInfo, DexItemFactory factory) {
+  public static LambdaDescriptor tryInfer(DexCallSite callSite, AppInfo appInfo) {
+    LambdaDescriptor descriptor = infer(callSite, appInfo);
+    return descriptor == MATCH_FAILED ? null : descriptor;
+  }
+
+  /**
+   * Matches call site for lambda metafactory invocation pattern and
+   * returns extracted match information, or MATCH_FAILED if match failed.
+   */
+  static LambdaDescriptor infer(DexCallSite callSite, AppInfo appInfo) {
     // We expect bootstrap method to be either `metafactory` or `altMetafactory` method
     // of `java.lang.invoke.LambdaMetafactory` class. Both methods are static.
     if (!callSite.bootstrapMethod.type.isInvokeStatic()) {
       return LambdaDescriptor.MATCH_FAILED;
     }
 
+    DexItemFactory factory = appInfo.dexItemFactory;
     DexMethod bootstrapMethod = callSite.bootstrapMethod.asMethod();
     boolean isMetafactoryMethod = bootstrapMethod == factory.metafactoryMethod;
     boolean isAltMetafactoryMethod = bootstrapMethod == factory.metafactoryAltMethod;
@@ -335,13 +344,11 @@
     }
   }
 
-  public static List<DexType> getInterfaces(
-      DexCallSite callSite, AppInfo appInfo, DexItemFactory factory) {
-    LambdaDescriptor descriptor = infer(callSite, appInfo, factory);
+  public static List<DexType> getInterfaces(DexCallSite callSite, AppInfo appInfo) {
+    LambdaDescriptor descriptor = infer(callSite, appInfo);
     if (descriptor == LambdaDescriptor.MATCH_FAILED) {
       return null;
     }
-    assert descriptor.interfaces != null;
     return descriptor.interfaces;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index d6b99a7..e7686db 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -194,7 +194,7 @@
         : putIfAbsent(
             knownCallSites,
             callSite,
-            LambdaDescriptor.infer(callSite, this.converter.appInfo, this.factory));
+            LambdaDescriptor.infer(callSite, this.converter.appInfo));
   }
 
   private boolean isInMainDexList(DexType type) {
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 8335cba..494eda8 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -39,6 +39,7 @@
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.desugar.LambdaDescriptor;
 import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.RootSetBuilder.ConsequentRootSet;
@@ -65,6 +66,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.IdentityHashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Objects;
@@ -296,6 +298,10 @@
 
     @Override
     public boolean registerInvokeVirtual(DexMethod method) {
+      return registerInvokeVirtual(method, KeepReason.invokedFrom(currentMethod));
+    }
+
+    boolean registerInvokeVirtual(DexMethod method, KeepReason keepReason) {
       if (appInfo.dexItemFactory.classMethods.isReflectiveMemberLookup(method)) {
         if (forceProguardCompatibility) {
           // TODO(b/76181966): whether or not add this rule in normal mode.
@@ -311,24 +317,32 @@
       if (Log.ENABLED) {
         Log.verbose(getClass(), "Register invokeVirtual `%s`.", method);
       }
-      workList.add(Action.markReachableVirtual(method, KeepReason.invokedFrom(currentMethod)));
+      workList.add(Action.markReachableVirtual(method, keepReason));
       return true;
     }
 
     @Override
     public boolean registerInvokeDirect(DexMethod method) {
+      return registerInvokeDirect(method, KeepReason.invokedFrom(currentMethod));
+    }
+
+    boolean registerInvokeDirect(DexMethod method, KeepReason keepReason) {
       if (!registerItemWithTarget(directInvokes, method)) {
         return false;
       }
       if (Log.ENABLED) {
         Log.verbose(getClass(), "Register invokeDirect `%s`.", method);
       }
-      handleInvokeOfDirectTarget(method, KeepReason.invokedFrom(currentMethod));
+      handleInvokeOfDirectTarget(method, keepReason);
       return true;
     }
 
     @Override
     public boolean registerInvokeStatic(DexMethod method) {
+      return registerInvokeStatic(method, KeepReason.invokedFrom(currentMethod));
+    }
+
+    boolean registerInvokeStatic(DexMethod method, KeepReason keepReason) {
       if (method == appInfo.dexItemFactory.classMethods.forName
           || appInfo.dexItemFactory.atomicFieldUpdaterMethods.isFieldUpdater(method)) {
         if (forceProguardCompatibility) {
@@ -345,19 +359,23 @@
       if (Log.ENABLED) {
         Log.verbose(getClass(), "Register invokeStatic `%s`.", method);
       }
-      handleInvokeOfStaticTarget(method, KeepReason.invokedFrom(currentMethod));
+      handleInvokeOfStaticTarget(method, keepReason);
       return true;
     }
 
     @Override
     public boolean registerInvokeInterface(DexMethod method) {
+      return registerInvokeInterface(method, KeepReason.invokedFrom(currentMethod));
+    }
+
+    boolean registerInvokeInterface(DexMethod method, KeepReason keepReason) {
       if (!registerItemWithTarget(interfaceInvokes, method)) {
         return false;
       }
       if (Log.ENABLED) {
         Log.verbose(getClass(), "Register invokeInterface `%s`.", method);
       }
-      workList.add(Action.markReachableInterface(method, KeepReason.invokedFrom(currentMethod)));
+      workList.add(Action.markReachableInterface(method, keepReason));
       return true;
     }
 
@@ -403,7 +421,11 @@
 
     @Override
     public boolean registerNewInstance(DexType type) {
-      markInstantiated(type, currentMethod);
+      return registerNewInstance(type, KeepReason.instantiatedIn(currentMethod));
+    }
+
+    public boolean registerNewInstance(DexType type, KeepReason keepReason) {
+      markInstantiated(type, keepReason);
       return true;
     }
 
@@ -459,6 +481,88 @@
           appInfo.lookupLambdaImplementedMethods(callSite, options.reporter)) {
         markLambdaInstantiated(method.method.holder, currentMethod);
       }
+
+      LambdaDescriptor descriptor = LambdaDescriptor.tryInfer(callSite, appInfo);
+      if (descriptor == null) {
+        return;
+      }
+
+      // For call sites representing a lambda, we link the targeted method
+      // or field as if it were referenced from the current method.
+
+      DexMethodHandle implHandle = descriptor.implHandle;
+      assert implHandle != null;
+      switch (implHandle.type) {
+        case INVOKE_STATIC:
+          registerInvokeStatic(implHandle.asMethod(),
+              KeepReason.invokedFromLambdaCreatedIn(currentMethod));
+          break;
+        case INVOKE_INTERFACE:
+          registerInvokeInterface(implHandle.asMethod(),
+              KeepReason.invokedFromLambdaCreatedIn(currentMethod));
+          break;
+        case INVOKE_INSTANCE:
+          registerInvokeVirtual(implHandle.asMethod(),
+              KeepReason.invokedFromLambdaCreatedIn(currentMethod));
+          break;
+        case INVOKE_DIRECT:
+          registerInvokeDirect(implHandle.asMethod(),
+              KeepReason.invokedFromLambdaCreatedIn(currentMethod));
+          break;
+        case INVOKE_CONSTRUCTOR:
+          registerNewInstance(implHandle.asMethod().holder,
+              KeepReason.invokedFromLambdaCreatedIn(currentMethod));
+          break;
+        default:
+          throw new Unreachable();
+      }
+
+      // In similar way as what transitionMethodsForInstantiatedClass does for existing
+      // classes we need to process classes dynamically created by runtime for lambdas.
+      // We make an assumption that such classes are inherited directly from java.lang.Object
+      // and implement all lambda interfaces.
+
+      ScopedDexMethodSet seen = new ScopedDexMethodSet();
+      List<DexType> directInterfaces = LambdaDescriptor.getInterfaces(callSite, appInfo);
+      if (directInterfaces == null) {
+        return;
+      }
+
+      Set<DexType> allInterfaces = Sets.newHashSet(directInterfaces);
+      DexType instantiatedType = appInfo.dexItemFactory.objectType;
+      DexClass clazz = appInfo.definitionFor(instantiatedType);
+      if (clazz == null) {
+        reportMissingClass(instantiatedType);
+        return;
+      }
+
+      // We only have to look at virtual methods here, as only those can actually be executed at
+      // runtime. Illegal dispatch situations and the corresponding exceptions are already handled
+      // by the reachability logic.
+      SetWithReason<DexEncodedMethod> reachableMethods =
+          reachableVirtualMethods.get(instantiatedType);
+      if (reachableMethods != null) {
+        transitionNonAbstractMethodsToLiveAndShadow(
+            reachableMethods.getItems(), instantiatedType, seen);
+      }
+      Collections.addAll(allInterfaces, clazz.interfaces.values);
+
+      // The set now contains all virtual methods on the type and its supertype that are reachable.
+      // In a second step, we now look at interfaces. We have to do this in this order due to JVM
+      // semantics for default methods. A default method is only reachable if it is not overridden
+      // in any superclass. Also, it is not defined which default method is chosen if multiple
+      // interfaces define the same default method. Hence, for every interface (direct or indirect),
+      // we have to look at the interface chain and mark default methods as reachable, not taking
+      // the shadowing of other interface chains into account.
+      // See https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3.3
+      for (DexType iface : allInterfaces) {
+        DexClass ifaceClazz = appInfo.definitionFor(iface);
+        if (ifaceClazz == null) {
+          reportMissingClass(iface);
+          return;
+        }
+        transitionDefaultMethodsForInstantiatedClass(iface, instantiatedType, seen);
+      }
     }
 
     private boolean registerConstClassOrCheckCast(DexType type) {
@@ -483,9 +587,13 @@
   }
 
   private DexMethod getInvokeSuperTarget(DexMethod method, DexEncodedMethod currentMethod) {
+    DexClass methodHolderClass = appInfo.definitionFor(method.getHolder());
+    if (methodHolderClass != null && methodHolderClass.isInterface()) {
+      return method;
+    }
     DexClass holderClass = appInfo.definitionFor(currentMethod.method.getHolder());
-    if (holderClass == null || holderClass.superType == null) {
-      // We do not know better.
+    if (holderClass == null || holderClass.superType == null || holderClass.isInterface()) {
+      // We do not know better or this call is made from an interface.
       return method;
     }
     // Return the invoked method on the supertype.
@@ -836,7 +944,7 @@
     enqueueRootItems(rootSet.getDependentItems(field));
   }
 
-  private void markInstantiated(DexType type, DexEncodedMethod method) {
+  private void markInstantiated(DexType type, KeepReason keepReason) {
     if (instantiatedTypes.contains(type)) {
       return;
     }
@@ -848,7 +956,7 @@
     if (Log.ENABLED) {
       Log.verbose(getClass(), "Register new instantiation of `%s`.", clazz);
     }
-    workList.add(Action.markInstantiated(clazz, KeepReason.instantiatedIn(method)));
+    workList.add(Action.markInstantiated(clazz, keepReason));
   }
 
   private void markLambdaInstantiated(DexType itf, DexEncodedMethod method) {
@@ -1040,17 +1148,17 @@
     if (target.accessFlags.isPrivate()) {
       brokenSuperInvokes.add(method);
     }
-    assert !superInvokeDependencies.containsKey(from) || !superInvokeDependencies.get(from)
-        .contains(target);
     if (Log.ENABLED) {
       Log.verbose(getClass(), "Adding super constraint from `%s` to `%s`", from.method,
           target.method);
     }
-    superInvokeDependencies.computeIfAbsent(from, ignore -> Sets.newIdentityHashSet()).add(target);
-    if (liveMethods.contains(from)) {
-      markMethodAsTargeted(target, KeepReason.invokedViaSuperFrom(from));
-      if (!target.accessFlags.isAbstract()) {
-        markVirtualMethodAsLive(target, KeepReason.invokedViaSuperFrom(from));
+    if (superInvokeDependencies.computeIfAbsent(
+        from, ignore -> Sets.newIdentityHashSet()).add(target)) {
+      if (liveMethods.contains(from)) {
+        markMethodAsTargeted(target, KeepReason.invokedViaSuperFrom(from));
+        if (!target.accessFlags.isAbstract()) {
+          markVirtualMethodAsLive(target, KeepReason.invokedViaSuperFrom(from));
+        }
       }
     }
   }
@@ -1837,6 +1945,11 @@
       return builder.build();
     }
 
+    @Override
+    protected boolean hasAnyInstantiatedLambdas(DexType type) {
+      return instantiatedLambdas.contains(type);
+    }
+
     private static <T extends PresortedComparable<T>> ImmutableSortedSet<T> rewriteItems(
         Set<T> original, Function<T, T> rewrite) {
       ImmutableSortedSet.Builder<T> builder =
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepReason.java b/src/main/java/com/android/tools/r8/shaking/KeepReason.java
index 78ef9e9..97b14b5 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepReason.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepReason.java
@@ -35,6 +35,10 @@
     return new InvokedFrom(method);
   }
 
+  public static KeepReason invokedFromLambdaCreatedIn(DexEncodedMethod method) {
+    return new InvokedFromLambdaCreatedIn(method);
+  }
+
   public static KeepReason isLibraryMethod() {
     return new IsLibraryMethod();
   }
@@ -167,6 +171,18 @@
     }
   }
 
+  private static class InvokedFromLambdaCreatedIn extends BasedOnOtherMethod {
+
+    private InvokedFromLambdaCreatedIn(DexEncodedMethod method) {
+      super(method);
+    }
+
+    @Override
+    String getKind() {
+      return "invoked from lambda created in";
+    }
+  }
+
   private static class ReferenedFrom extends BasedOnOtherMethod {
 
     private ReferenedFrom(DexEncodedMethod method) {
diff --git a/src/test/examplesAndroidO/lambdadesugaring/LambdaDesugaring.java b/src/test/examplesAndroidO/lambdadesugaring/LambdaDesugaring.java
index 4b7ebd9..b54be6b 100644
--- a/src/test/examplesAndroidO/lambdadesugaring/LambdaDesugaring.java
+++ b/src/test/examplesAndroidO/lambdadesugaring/LambdaDesugaring.java
@@ -231,7 +231,7 @@
         System.out.println(PrivateInit.testPrivate());
         System.out.println(g(PrivateInit::new));
 
-        System.out.println(p1(D[]::new));
+        System.out.println(p1(LambdaDesugaring[]::new));
         System.out.println(p1(Integer::new));
         System.out.println(p1(B::staticArray));
 
diff --git a/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java b/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java
index 46cd555..ed42134 100644
--- a/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java
+++ b/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java
@@ -327,6 +327,7 @@
 
       @SomeAnnotation(4)
       static void annotatedStaticMethod() {
+        synchronized (AnnotatedInterface.class) { } // Do not inline
       }
     }
 
@@ -365,6 +366,9 @@
         System.out.println("Check 3: NOT OK");
       }
 
+      // I don't know how to keep this method moved to the companion class
+      // without the direct call.
+      AnnotatedInterface.annotatedStaticMethod();
       if (checkAnnotationValue(
           getCompanionClassOrInterface().getMethod("annotatedStaticMethod").getAnnotations(), 4)) {
         System.out.println("Check 4: OK");
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index b6b9ba8..f20335b 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -6,35 +6,66 @@
 
 import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.VmTestRunner.IgnoreIfVmOlderThan;
+import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.OffOrAuto;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.function.UnaryOperator;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
+@RunWith(VmTestRunner.class)
 public class R8RunExamplesAndroidOTest extends RunExamplesAndroidOTest<R8Command.Builder> {
+  private static final ArrayList<String> PROGUARD_OPTIONS = Lists.newArrayList(
+      "-keepclasseswithmembers public class * {",
+      "    public static void main(java.lang.String[]);",
+      "}",
+      "",
+      "-dontobfuscate",
+      "-allowaccessmodification"
+  );
+
+
+  private static final ArrayList<String> PROGUARD_OPTIONS_N_PLUS = Lists.newArrayList(
+      "-keepclasseswithmembers public class * {",
+      "    public static void main(java.lang.String[]);",
+      "}",
+      "",
+      "-keepclasseswithmembers interface lambdadesugaringnplus."
+          + "LambdasWithStaticAndDefaultMethods$B38302860$AnnotatedInterface{",
+      "    *;",
+      "} ",
+      "",
+      "-keepattributes *Annotation*",
+      "-dontobfuscate",
+      "-allowaccessmodification"
+  );
 
   private static Map<DexVm.Version, List<String>> alsoFailsOn =
       ImmutableMap.<DexVm.Version, List<String>>builder()
           .put(Version.V4_0_4,
               ImmutableList.of(
-              "invokecustom-with-shrinking"
+                  "invokecustom-with-shrinking"
               ))
           .put(Version.V4_4_4,
               ImmutableList.of(
-              "invokecustom-with-shrinking"
+                  "invokecustom-with-shrinking"
               ))
           .put(Version.V5_1_1,
               ImmutableList.of(
-              "invokecustom-with-shrinking"
+                  "invokecustom-with-shrinking"
               ))
           .put(Version.V6_0_1,
               ImmutableList.of(
-              "invokecustom-with-shrinking"
+                  "invokecustom-with-shrinking"
               ))
           .put(Version.V7_0_0,
               ImmutableList.of(
@@ -55,6 +86,82 @@
         .run();
   }
 
+  @Override
+  @Test
+  public void lambdaDesugaring() throws Throwable {
+    test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
+        .withMinApiLevel(AndroidApiLevel.K)
+        .withOptionConsumer(opts -> opts.enableClassInlining = false)
+        .withBuilderTransformation(
+            b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
+        .run();
+
+    test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
+        .withMinApiLevel(AndroidApiLevel.K)
+        .withOptionConsumer(opts -> opts.enableClassInlining = true)
+        .withBuilderTransformation(
+            b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
+        .run();
+  }
+
+  @Test
+  @IgnoreIfVmOlderThan(Version.V7_0_0)
+  public void lambdaDesugaringWithDefaultMethods() throws Throwable {
+    test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
+        .withMinApiLevel(AndroidApiLevel.N)
+        .withOptionConsumer(opts -> opts.enableClassInlining = false)
+        .withBuilderTransformation(
+            b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
+        .run();
+
+    test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
+        .withMinApiLevel(AndroidApiLevel.N)
+        .withOptionConsumer(opts -> opts.enableClassInlining = true)
+        .withBuilderTransformation(
+            b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
+        .run();
+  }
+
+  @Override
+  @Test
+  public void lambdaDesugaringNPlus() throws Throwable {
+    test("lambdadesugaringnplus", "lambdadesugaringnplus", "LambdasWithStaticAndDefaultMethods")
+        .withMinApiLevel(AndroidApiLevel.K)
+        .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+        .withOptionConsumer(opts -> opts.enableClassInlining = false)
+        .withBuilderTransformation(
+            b -> b.addProguardConfiguration(PROGUARD_OPTIONS_N_PLUS, Origin.unknown()))
+        .run();
+
+    test("lambdadesugaringnplus", "lambdadesugaringnplus", "LambdasWithStaticAndDefaultMethods")
+        .withMinApiLevel(AndroidApiLevel.K)
+        .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+        .withOptionConsumer(opts -> opts.enableClassInlining = true)
+        .withBuilderTransformation(
+            b -> b.addProguardConfiguration(PROGUARD_OPTIONS_N_PLUS, Origin.unknown()))
+        .run();
+  }
+
+  @Test
+  @IgnoreIfVmOlderThan(Version.V7_0_0)
+  public void lambdaDesugaringNPlusWithDefaultMethods() throws Throwable {
+    test("lambdadesugaringnplus", "lambdadesugaringnplus", "LambdasWithStaticAndDefaultMethods")
+        .withMinApiLevel(AndroidApiLevel.N)
+        .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+        .withOptionConsumer(opts -> opts.enableClassInlining = false)
+        .withBuilderTransformation(
+            b -> b.addProguardConfiguration(PROGUARD_OPTIONS_N_PLUS, Origin.unknown()))
+        .run();
+
+    test("lambdadesugaringnplus", "lambdadesugaringnplus", "LambdasWithStaticAndDefaultMethods")
+        .withMinApiLevel(AndroidApiLevel.N)
+        .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+        .withOptionConsumer(opts -> opts.enableClassInlining = true)
+        .withBuilderTransformation(
+            b -> b.addProguardConfiguration(PROGUARD_OPTIONS_N_PLUS, Origin.unknown()))
+        .run();
+  }
+
   class R8TestRunner extends TestRunner<R8TestRunner> {
 
     R8TestRunner(String testName, String packageName, String mainClass) {
diff --git a/src/test/java/com/android/tools/r8/shaking/defaultmethods/DefaultMethodsTest.java b/src/test/java/com/android/tools/r8/shaking/defaultmethods/DefaultMethodsTest.java
index e543d46..46feb00 100644
--- a/src/test/java/com/android/tools/r8/shaking/defaultmethods/DefaultMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/defaultmethods/DefaultMethodsTest.java
@@ -104,6 +104,6 @@
             "}",
             // Prevent InterfaceWithDefaultMethods from being merged into ClassImplementingInterface
             "-keep class " + InterfaceWithDefaultMethods.class.getCanonicalName()),
-        this::defaultMethodAbstract);
+        this::defaultMethodKept);
   }
 }