Merge "Re-enable "Eliminate redundant field loads.""
diff --git a/LIBRARY-LICENSE b/LIBRARY-LICENSE
index bc69c40..889b9ad 100644
--- a/LIBRARY-LICENSE
+++ b/LIBRARY-LICENSE
@@ -54,6 +54,12 @@
   license: The Apache License, Version 2.0
   licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
   url: https://kotlinlang.org/
+- artifact: org.jetbrains.kotlin:kotlin-stdlib-common:+
+  name: org.jetbrains.kotlin:kotlin-stdlib
+  copyrightHolder: JetBrains s.r.o.
+  license: The Apache License, Version 2.0
+  licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt
+  url: https://kotlinlang.org/
 - artifact: org.jetbrains.kotlinx:kotlinx-metadata-jvm:+
   name: org.jetbrains.kotlinx:kotlinx-metadata-jvm
   copyrightHolder: JetBrains s.r.o.
diff --git a/build.gradle b/build.gradle
index dd401d6..ac3725c 100644
--- a/build.gradle
+++ b/build.gradle
@@ -19,7 +19,7 @@
     gsonVersion = '2.7'
     junitVersion = '4.12'
     kotlinVersion = '1.2.30'
-    kotlinExtMetadataJVMVersion = '0.0.2'
+    kotlinExtMetadataJVMVersion = '0.0.3'
     protobufVersion = '3.0.0'
     smaliVersion = '2.2b4'
 }
@@ -67,7 +67,7 @@
     dependencies {
         classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.1'
         classpath 'com.cookpad.android.licensetools:license-tools-plugin:0.23.0'
-        classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.3'
+        classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.4'
         classpath "net.ltgt.gradle:gradle-errorprone-plugin:0.0.13"
         classpath "net.ltgt.gradle:gradle-apt-plugin:0.12"
         classpath "com.gradle:build-scan-plugin:1.14"
@@ -501,6 +501,12 @@
 }
 
 static configureRelocations(ShadowJar task) {
+    // Everything under META-INF is not included by default.
+    // Should include before 'relocate' so that the service file path and its content
+    // are properly relocated as well.
+    task.mergeServiceFiles {
+        include 'META-INF/services/*'
+    }
     task.relocate('com.google.common', 'com.android.tools.r8.com.google.common')
     task.relocate('com.google.gson', 'com.android.tools.r8.com.google.gson')
     task.relocate('com.google.thirdparty', 'com.android.tools.r8.com.google.thirdparty')
@@ -518,6 +524,7 @@
     configurations = [project.configurations.compile]
     configureRelocations(it)
     exclude { it.getRelativePath().getPathString() == "module-info.class" }
+    exclude { it.getRelativePath().getPathString().startsWith("META-INF/maven/") }
     baseName 'deps'
 }
 
@@ -529,9 +536,6 @@
 
 task R8(type: ShadowJar) {
     from consolidatedLicense.outputs.files
-    exclude { path ->
-      path.getRelativePath().getPathString().startsWith("META-INF")
-    }
     baseName 'r8'
     classifier = null
     version = null
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/code/CanonicalPositions.java b/src/main/java/com/android/tools/r8/ir/code/CanonicalPositions.java
new file mode 100644
index 0000000..8db07f6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/CanonicalPositions.java
@@ -0,0 +1,75 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.graph.DexMethod;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Maintains a set of canonical positions. Also supports appending a new caller at the end of the
+ * caller chain of a Position.
+ */
+public class CanonicalPositions {
+  private final Position callerPosition;
+  private final boolean preserveCaller;
+  private final Map<Position, Position> canonicalPositions;
+  private final Position preamblePosition;
+
+  /**
+   * For callerPosition and preserveCaller see canonicalizeCallerPosition. initialCapacity will be
+   * passed to the HashMap constructor.
+   */
+  public CanonicalPositions(
+      Position callerPosition, boolean preserveCaller, int initialCapacity, DexMethod method) {
+    canonicalPositions = new HashMap<>(initialCapacity);
+    this.preserveCaller = preserveCaller;
+    this.callerPosition = callerPosition;
+    if (callerPosition != null) {
+      canonicalPositions.put(callerPosition, callerPosition);
+    }
+    preamblePosition =
+        callerPosition == null
+            ? Position.synthetic(0, method, null)
+            : new Position(0, null, method, callerPosition);
+    canonicalPositions.put(preamblePosition, preamblePosition);
+  }
+
+  public Position getPreamblePosition() {
+    return preamblePosition;
+  }
+
+  /**
+   * Update the internal set if this is the first occurence of the position's value and return
+   * canonical instance of position.
+   */
+  public Position getCanonical(Position position) {
+    Position canonical = canonicalPositions.putIfAbsent(position, position);
+    return canonical != null ? canonical : position;
+  }
+
+  /**
+   * Append callerPosition (supplied in constructor) to the end of caller's caller chain and return
+   * the canonical instance. Always returns null if preserveCaller (also supplied in constructor) is
+   * false.
+   */
+  public Position canonicalizeCallerPosition(Position caller) {
+    if (!preserveCaller) {
+      return null;
+    }
+
+    if (caller == null) {
+      return callerPosition;
+    }
+    if (caller.callerPosition == null && callerPosition == null) {
+      return getCanonical(caller);
+    }
+    Position callerOfCaller = canonicalizeCallerPosition(caller.callerPosition);
+    return getCanonical(
+        caller.isNone()
+            ? Position.noneWithMethod(caller.method, callerOfCaller)
+            : new Position(caller.line, caller.file, caller.method, callerOfCaller));
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
index b59794b..e0c47da 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
@@ -40,6 +40,7 @@
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.ir.code.CanonicalPositions;
 import com.android.tools.r8.ir.code.CatchHandlers;
 import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.ir.code.ValueType;
@@ -68,25 +69,20 @@
   private Instruction currentDexInstruction = null;
 
   private Position currentPosition = null;
-  private Map<Position, Position> canonicalPositions = null;
+  private final CanonicalPositions canonicalPositions;
 
   private final List<ValueType> argumentTypes;
 
   private List<DexDebugEntry> debugEntries = null;
   // In case of inlining the position of the invoke in the caller.
-  private final Position callerPosition;
   private final DexMethod method;
-  private final boolean preserveCaller;
-  private final Position preamblePosition;
 
   public DexSourceCode(
       DexCode code, DexEncodedMethod method, Position callerPosition, boolean preserveCaller) {
     this.code = code;
     this.proto = method.method.proto;
     this.accessFlags = method.accessFlags;
-    this.callerPosition = callerPosition;
     this.method = method.method;
-    this.preserveCaller = preserveCaller;
 
     argumentTypes = computeArgumentTypes();
     DexDebugInfo info = code.getDebugInfo();
@@ -94,18 +90,11 @@
       debugEntries = info.computeEntries(method.method);
     }
     canonicalPositions =
-        new HashMap<>(
-            1
-                + (callerPosition == null ? 0 : 1)
-                + (debugEntries == null ? 0 : debugEntries.size()));
-    if (callerPosition != null) {
-      canonicalPositions.put(callerPosition, callerPosition);
-    }
-    preamblePosition =
-        callerPosition == null
-            ? Position.synthetic(0, this.method, null)
-            : new Position(0, null, this.method, callerPosition);
-    canonicalPositions.put(preamblePosition, preamblePosition);
+        new CanonicalPositions(
+            callerPosition,
+            preserveCaller,
+            1 + (callerPosition == null ? 0 : 1) + (debugEntries == null ? 0 : debugEntries.size()),
+            this.method);
   }
 
   @Override
@@ -148,7 +137,7 @@
 
   @Override
   public void buildPrelude(IRBuilder builder) {
-    currentPosition = preamblePosition;
+    currentPosition = canonicalPositions.getPreamblePosition();
     if (code.incomingRegisterSize == 0) {
       return;
     }
@@ -197,7 +186,9 @@
   @Override
   public Position getDebugPositionAtOffset(int offset) {
     DexDebugEntry entry = getDebugEntryAtOffset(offset);
-    return entry == null ? preamblePosition : getCanonicalPositionAppendCaller(entry);
+    return entry == null
+        ? canonicalPositions.getPreamblePosition()
+        : getCanonicalPositionAppendCaller(entry);
   }
 
   @Override
@@ -250,7 +241,7 @@
     int offset = instructionOffset(instructionIndex);
     DexDebugEntry entry = getDebugEntryAtOffset(offset);
     if (entry == null) {
-      currentPosition = preamblePosition;
+      currentPosition = canonicalPositions.getPreamblePosition();
     } else {
       currentPosition = getCanonicalPositionAppendCaller(entry);
       if (entry.lineEntry && entry.address == offset) {
@@ -259,39 +250,17 @@
     }
   }
 
-  private Position getCanonicalPosition(Position position) {
-    Position canonical = canonicalPositions.putIfAbsent(position, position);
-    return canonical != null ? canonical : position;
-  }
-
-  private Position canonicalizeCallerPosition(Position caller) {
-    // We are not supposed to get here from getCanonicalPositionAppendCaller if !preserveCaller.
-    assert preserveCaller;
-
-    if (caller == null) {
-      return callerPosition;
-    }
-    if (caller.callerPosition == null && callerPosition == null) {
-      return getCanonicalPosition(caller);
-    }
-    Position callerOfCaller = canonicalizeCallerPosition(caller.callerPosition);
-    return getCanonicalPosition(
-        caller.isNone()
-            ? Position.noneWithMethod(caller.method, callerOfCaller)
-            : new Position(caller.line, caller.file, caller.method, callerOfCaller));
-  }
-
   private Position getCanonicalPositionAppendCaller(DexDebugEntry entry) {
     // If this instruction has already been inlined then this.method must be the outermost caller.
     assert entry.callerPosition == null
         || entry.callerPosition.getOutermostCaller().method == method;
 
-    return getCanonicalPosition(
+    return canonicalPositions.getCanonical(
         new Position(
             entry.line,
             entry.sourceFile,
             entry.method,
-            preserveCaller ? canonicalizeCallerPosition(entry.callerPosition) : null));
+            canonicalPositions.canonicalizeCallerPosition(entry.callerPosition)));
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 555ddf0..d5f923e 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -744,7 +744,7 @@
       assert options.enableInlining && inliner != null;
       TypeEnvironment effectivelyFinalTypeEnvironment = typeEnvironment;
       classInliner.processMethodCode(
-          appInfo.withLiveness(), method, code, isProcessedConcurrently,
+          appInfo.withLiveness(), codeRewriter, method, code, isProcessedConcurrently,
           methodsToInline -> inliner.performForcedInlining(method, code, methodsToInline),
           Suppliers.memoize(() -> inliner.createDefaultOracle(
               method, code, effectivelyFinalTypeEnvironment,
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/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 121f47d..94e8147 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -2362,7 +2362,7 @@
             && (theIf.getType() == Type.EQ || theIf.getType() == Type.NE)) {
           if (inValues.get(0).isNeverNull()) {
             simplifyIfWithKnownCondition(code, block, theIf, 1);
-          } else {
+          } else if (typeEnvironment != null) {
             // TODO(b/72693244): annotate type lattice to value
             TypeLatticeElement l = typeEnvironment.getLatticeElement(inValues.get(0));
             if (!l.isPrimitive() && !l.isNullable()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
index 219b5b1..5bf2c74 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.optimize.CodeRewriter;
 import com.android.tools.r8.ir.optimize.Inliner.InliningInfo;
 import com.android.tools.r8.ir.optimize.InliningOracle;
 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
@@ -117,6 +118,7 @@
   //
   public final void processMethodCode(
       AppInfoWithLiveness appInfo,
+      CodeRewriter codeRewriter,
       DexEncodedMethod method,
       IRCode code,
       Predicate<DexEncodedMethod> isProcessedConcurrently,
@@ -164,11 +166,14 @@
         }
 
         // Inline the class instance.
-        processor.processInlining(code, inliner);
+        boolean anyInlinedMethods = processor.processInlining(code, inliner);
 
         // Restore normality.
         code.removeAllTrivialPhis();
         assert code.isConsistentSSA();
+        if (anyInlinedMethods) {
+          codeRewriter.simplifyIf(code, null);
+        }
         rootsIterator.remove();
         repeat = true;
       }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index b3bde90..57ce42f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -271,14 +271,16 @@
   //  * remove field writes
   //  * remove root instruction
   //
-  void processInlining(IRCode code, InlinerAction inliner) {
+  // Returns `true` if at least one method was inlined.
+  boolean processInlining(IRCode code, InlinerAction inliner) {
     replaceUsagesAsUnusedArgument(code);
-    forceInlineExtraMethodInvocations(inliner);
-    forceInlineDirectMethodInvocations(inliner);
+    boolean anyInlinedMethods = forceInlineExtraMethodInvocations(inliner);
+    anyInlinedMethods |= forceInlineDirectMethodInvocations(inliner);
     removeMiscUsages(code);
     removeFieldReads(code);
     removeFieldWrites();
     removeInstruction(root);
+    return anyInlinedMethods;
   }
 
   private void replaceUsagesAsUnusedArgument(IRCode code) {
@@ -298,9 +300,9 @@
     unusedArguments.clear();
   }
 
-  private void forceInlineExtraMethodInvocations(InlinerAction inliner) {
+  private boolean forceInlineExtraMethodInvocations(InlinerAction inliner) {
     if (extraMethodCalls.isEmpty()) {
-      return;
+      return false;
     }
 
     // Inline extra methods.
@@ -320,12 +322,15 @@
     }
     assert extraMethodCalls.isEmpty();
     assert unusedArguments.isEmpty();
+    return true;
   }
 
-  private void forceInlineDirectMethodInvocations(InlinerAction inliner) {
-    if (!methodCallsOnInstance.isEmpty()) {
-      inliner.inline(methodCallsOnInstance);
+  private boolean forceInlineDirectMethodInvocations(InlinerAction inliner) {
+    if (methodCallsOnInstance.isEmpty()) {
+      return false;
     }
+    inliner.inline(methodCallsOnInstance);
+    return true;
   }
 
   // Remove miscellaneous users before handling field reads.
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
index 581ac8a..b0f4dc0 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
@@ -6,7 +6,7 @@
 import static com.android.tools.r8.naming.IdentifierNameStringUtils.identifyIdentiferNameString;
 import static com.android.tools.r8.naming.IdentifierNameStringUtils.inferMemberOrTypeFromNameString;
 import static com.android.tools.r8.naming.IdentifierNameStringUtils.isReflectionMethod;
-import static com.android.tools.r8.naming.IdentifierNameStringUtils.warnUndeterminedIdentifier;
+import static com.android.tools.r8.naming.IdentifierNameStringUtils.warnUndeterminedIdentifierIfNecessary;
 
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.DexEncodedField;
@@ -32,7 +32,6 @@
 import com.android.tools.r8.ir.code.InvokeMethod;
 import com.android.tools.r8.ir.code.StaticPut;
 import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.Streams;
@@ -82,7 +81,7 @@
   }
 
   public void decoupleIdentifierNameStringsInMethod(DexEncodedMethod encodedMethod, IRCode code) {
-    Origin origin = appInfo.originFor(code.method.method.getHolder());
+    DexType originHolder = code.method.method.getHolder();
     ListIterator<BasicBlock> blocks = code.listIterator();
     while (blocks.hasNext()) {
       BasicBlock block = blocks.next();
@@ -110,17 +109,15 @@
               ? instruction.asStaticPut().inValue()
               : instruction.asInstancePut().value();
           if (!in.isConstString()) {
-            if (options.proguardConfiguration.isObfuscating()) {
-              warnUndeterminedIdentifier(options.reporter, field, origin, instruction, null);
-            }
+            warnUndeterminedIdentifierIfNecessary(
+                appInfo, options, field, originHolder, instruction, null);
             continue;
           }
           DexString original = in.getConstInstruction().asConstString().getValue();
           DexItemBasedString itemBasedString = inferMemberOrTypeFromNameString(appInfo, original);
           if (itemBasedString == null) {
-            if (options.proguardConfiguration.isObfuscating()) {
-              warnUndeterminedIdentifier(options.reporter, field, origin, instruction, original);
-            }
+            warnUndeterminedIdentifierIfNecessary(
+                appInfo, options, field, originHolder, instruction, original);
             continue;
           }
           // Move the cursor back to $fieldPut
@@ -174,10 +171,8 @@
           if (isReflectionMethod(dexItemFactory, invokedMethod)) {
             DexItemBasedString itemBasedString = identifyIdentiferNameString(appInfo, invoke);
             if (itemBasedString == null) {
-              if (options.proguardConfiguration.isObfuscating()) {
-                warnUndeterminedIdentifier(
-                    options.reporter, invokedMethod, origin, instruction, null);
-              }
+              warnUndeterminedIdentifierIfNecessary(
+                  appInfo, options, invokedMethod, originHolder, instruction, null);
               continue;
             }
             DexType returnType = invoke.getReturnType();
@@ -222,20 +217,16 @@
             for (int i = 0; i < ins.size(); i++) {
               Value in = ins.get(i);
               if (!in.isConstString()) {
-                if (options.proguardConfiguration.isObfuscating()) {
-                  warnUndeterminedIdentifier(
-                      options.reporter, invokedMethod, origin, instruction, null);
-                }
+                warnUndeterminedIdentifierIfNecessary(
+                    appInfo, options, invokedMethod, originHolder, instruction, null);
                 continue;
               }
               DexString original = in.getConstInstruction().asConstString().getValue();
               DexItemBasedString itemBasedString =
                   inferMemberOrTypeFromNameString(appInfo, original);
               if (itemBasedString == null) {
-                if (options.proguardConfiguration.isObfuscating()) {
-                  warnUndeterminedIdentifier(
-                      options.reporter, invokedMethod, origin, instruction, original);
-                }
+                warnUndeterminedIdentifierIfNecessary(
+                    appInfo, options, invokedMethod, originHolder, instruction, original);
                 continue;
               }
               // Move the cursor back to $invoke
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java
index 471b3f8..bf23f59 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java
@@ -26,7 +26,7 @@
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.position.TextPosition;
-import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.StringDiagnostic;
 import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
 import java.util.List;
@@ -372,13 +372,24 @@
     return new DexTypeList(types);
   }
 
-  public static void warnUndeterminedIdentifier(
-      Reporter reporter,
+  public static void warnUndeterminedIdentifierIfNecessary(
+      AppInfo appInfo,
+      InternalOptions options,
       DexItem member,
-      Origin origin,
+      DexType originHolder,
       Instruction instruction,
       DexString original) {
     assert member instanceof DexField || member instanceof DexMethod;
+    DexClass originClass = appInfo.definitionFor(originHolder);
+    // If the origin is a library class, it is out of developers' control.
+    if (originClass != null && originClass.isLibraryClass()) {
+      return;
+    }
+    // Undetermined identifiers matter only if minification is enabled.
+    if (!options.proguardConfiguration.isObfuscating()) {
+      return;
+    }
+    Origin origin = appInfo.originFor(originHolder);
     String kind = member instanceof DexField ? "field" : "method";
     String originalMessage = original == null ? "what identifier string flows to "
         : "what '" + original.toString() + "' refers to, which flows to ";
@@ -392,6 +403,6 @@
             ? new StringDiagnostic(message, origin,
                 new TextPosition(0L, instruction.getPosition().line, 1))
             : new StringDiagnostic(message, origin);
-    reporter.warning(diagnostic);
+    options.reporter.warning(diagnostic);
   }
 }
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 061b9bf..6a12b60 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -5,7 +5,7 @@
 
 import static com.android.tools.r8.naming.IdentifierNameStringUtils.identifyIdentiferNameString;
 import static com.android.tools.r8.naming.IdentifierNameStringUtils.isReflectionMethod;
-import static com.android.tools.r8.naming.IdentifierNameStringUtils.warnUndeterminedIdentifier;
+import static com.android.tools.r8.naming.IdentifierNameStringUtils.warnUndeterminedIdentifierIfNecessary;
 import static com.android.tools.r8.shaking.ProguardConfigurationUtils.buildIdentifierNameStringRule;
 
 import com.android.tools.r8.Diagnostic;
@@ -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;
@@ -153,6 +155,8 @@
    * its implementation may be removed and it may be marked abstract.
    */
   private final SetWithReason<DexEncodedMethod> targetedMethods = new SetWithReason<>();
+  /** Set of virtual methods that are the immediate target of an invoke-direct. */
+  private final Set<DexMethod> virtualMethodsTargetedByInvokeDirect = Sets.newIdentityHashSet();
   /**
    * Set of methods that belong to live classes and can be reached by invokes. These need to be
    * kept.
@@ -296,6 +300,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 +319,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 +361,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 +423,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 +483,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 +589,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.
@@ -601,6 +711,13 @@
     DexEncodedMethod target = appInfo.dispatchDirectInvoke(resolutionResult);
     if (target != null) {
       markDirectStaticOrConstructorMethodAsLive(target, reason);
+
+      // It is valid to have an invoke-direct instruction in a default interface method that
+      // targets another default method in the same interface (see testInvokeSpecialToDefault-
+      // Method). In a class, that would lead to a verification error.
+      if (target.isVirtualMethod()) {
+        virtualMethodsTargetedByInvokeDirect.add(target.method);
+      }
     }
   }
 
@@ -836,7 +953,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 +965,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 +1157,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));
+        }
       }
     }
   }
@@ -1359,13 +1476,14 @@
   }
 
   private void handleProguardReflectiveBehavior(DexEncodedMethod method) {
-    Origin origin = appInfo.originFor(method.method.holder);
+    DexType originHolder = method.method.holder;
+    Origin origin = appInfo.originFor(originHolder);
     IRCode code = method.buildIR(appInfo, options, origin);
     code.instructionIterator().forEachRemaining(instr ->
-        handleProguardReflectiveBehavior(instr, origin));
+        handleProguardReflectiveBehavior(instr, originHolder));
   }
 
-  private void handleProguardReflectiveBehavior(Instruction instruction, Origin origin) {
+  private void handleProguardReflectiveBehavior(Instruction instruction, DexType originHolder) {
     if (!instruction.isInvokeMethod()) {
       return;
     }
@@ -1376,9 +1494,8 @@
     }
     DexItemBasedString itemBasedString = identifyIdentiferNameString(appInfo, invoke);
     if (itemBasedString == null) {
-      if (options.proguardConfiguration.isObfuscating()) {
-        warnUndeterminedIdentifier(options.reporter, invokedMethod, origin, instruction, null);
-      }
+      warnUndeterminedIdentifierIfNecessary(
+          appInfo, options, invokedMethod, originHolder, instruction, null);
       return;
     }
     if (itemBasedString.basedOn instanceof DexType) {
@@ -1479,6 +1596,8 @@
      * removed.
      */
     final SortedSet<DexMethod> targetedMethods;
+    /** Set of virtual methods that are the immediate target of an invoke-direct. */
+    final SortedSet<DexMethod> virtualMethodsTargetedByInvokeDirect;
     /**
      * Set of methods that belong to live classes and can be reached by invokes. These need to be
      * kept.
@@ -1596,6 +1715,9 @@
           ImmutableSortedSet.copyOf(
               PresortedComparable<DexType>::slowCompareTo, enqueuer.instantiatedLambdas.getItems());
       this.targetedMethods = toSortedDescriptorSet(enqueuer.targetedMethods.getItems());
+      this.virtualMethodsTargetedByInvokeDirect =
+          ImmutableSortedSet.copyOf(
+              DexMethod::slowCompareTo, enqueuer.virtualMethodsTargetedByInvokeDirect);
       this.liveMethods = toSortedDescriptorSet(enqueuer.liveMethods.getItems());
       this.liveFields = toSortedDescriptorSet(enqueuer.liveFields.getItems());
       this.instanceFieldReads = enqueuer.collectInstanceFieldsRead();
@@ -1636,6 +1758,7 @@
       this.instantiatedTypes = previous.instantiatedTypes;
       this.instantiatedLambdas = previous.instantiatedLambdas;
       this.targetedMethods = previous.targetedMethods;
+      this.virtualMethodsTargetedByInvokeDirect = previous.virtualMethodsTargetedByInvokeDirect;
       this.liveMethods = previous.liveMethods;
       this.liveFields = previous.liveFields;
       this.instanceFieldReads = previous.instanceFieldReads;
@@ -1675,6 +1798,8 @@
       this.instantiatedTypes = rewriteItems(previous.instantiatedTypes, lense::lookupType);
       this.instantiatedLambdas = rewriteItems(previous.instantiatedLambdas, lense::lookupType);
       this.targetedMethods = rewriteMethodsConservatively(previous.targetedMethods, lense);
+      this.virtualMethodsTargetedByInvokeDirect =
+          rewriteMethodsConservatively(previous.virtualMethodsTargetedByInvokeDirect, lense);
       this.liveMethods = rewriteMethodsConservatively(previous.liveMethods, lense);
       this.liveFields = rewriteItems(previous.liveFields, lense::lookupField);
       this.instanceFieldReads =
@@ -1726,6 +1851,7 @@
       this.instantiatedTypes = previous.instantiatedTypes;
       this.instantiatedLambdas = previous.instantiatedLambdas;
       this.targetedMethods = previous.targetedMethods;
+      this.virtualMethodsTargetedByInvokeDirect = previous.virtualMethodsTargetedByInvokeDirect;
       this.liveMethods = previous.liveMethods;
       this.liveFields = previous.liveFields;
       this.instanceFieldReads = previous.instanceFieldReads;
@@ -1837,6 +1963,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/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 0840d91..38473f3 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -81,6 +81,8 @@
     RESOLUTION_FOR_FIELDS_MAY_CHANGE,
     RESOLUTION_FOR_METHODS_MAY_CHANGE,
     STATIC_INITIALIZERS,
+    UNHANDLED_INVOKE_DIRECT,
+    UNHANDLED_INVOKE_SUPER,
     UNSAFE_INLINING,
     UNSUPPORTED_ATTRIBUTES;
 
@@ -121,6 +123,12 @@
         case STATIC_INITIALIZERS:
           message = "merging of static initializers are not supported";
           break;
+        case UNHANDLED_INVOKE_DIRECT:
+          message = "a virtual method is targeted by an invoke-direct instruction";
+          break;
+        case UNHANDLED_INVOKE_SUPER:
+          message = "it may change the semantics of an invoke-super instruction";
+          break;
         case UNSAFE_INLINING:
           message = "force-inlining might fail";
           break;
@@ -217,10 +225,15 @@
     //     }
     //   }
     for (DexMethod signature : appInfo.brokenSuperInvokes) {
-      DexClass targetClass = appInfo.definitionFor(signature.holder);
-      if (targetClass != null && targetClass.isProgramClass()) {
-        pinnedTypes.add(signature.holder);
-      }
+      markTypeAsPinned(signature.holder, AbortReason.UNHANDLED_INVOKE_SUPER);
+    }
+
+    // It is valid to have an invoke-direct instruction in a default interface method that targets
+    // another default method in the same interface (see InterfaceMethodDesugaringTests.testInvoke-
+    // SpecialToDefaultMethod). However, in a class, that would lead to a verification error.
+    // Therefore, we disallow merging such interfaces into their subtypes.
+    for (DexMethod signature : appInfo.virtualMethodsTargetedByInvokeDirect) {
+      markTypeAsPinned(signature.holder, AbortReason.UNHANDLED_INVOKE_DIRECT);
     }
   }
 
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/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 5200a09..8adddfd 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -56,6 +56,8 @@
 
   // Actually running Proguard should only be during development.
   private static final boolean RUN_PROGUARD = System.getProperty("run_proguard") != null;
+  // Actually running r8.jar in a forked process.
+  private static final boolean RUN_R8_JAR = System.getProperty("run_r8_jar") != null;
 
   @Rule
   public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
@@ -71,6 +73,13 @@
   }
 
   /**
+   * Check if tests should run R8 in a forked process when applicable.
+   */
+  protected boolean isRunR8Jar() {
+    return RUN_R8_JAR;
+  }
+
+  /**
    * Write lines of text to a temporary file.
    */
   protected Path writeTextToTempFile(String... lines) throws IOException {
@@ -157,6 +166,17 @@
   }
 
   /**
+   * Copy test classes to the specified directory.
+   */
+  protected void copyTestClasses(Path dest, Class... classes) throws IOException {
+    for (Class clazz : classes) {
+      Path path = dest.resolve(clazz.getCanonicalName().replace('.', '/') + ".class");
+      Files.createDirectories(path.getParent());
+      Files.copy(ToolHelper.getClassFileForTestClass(clazz), path);
+    }
+  }
+
+  /**
    * Create a temporary JAR file containing the specified test classes.
    */
   protected Path jarTestClasses(Class... classes) throws IOException {
@@ -509,7 +529,11 @@
   }
 
   protected ProcessResult runOnJavaRaw(String main, byte[]... classes) throws IOException {
-    Path file = writeToZip(Arrays.asList(classes));
+    return runOnJavaRaw(main, Arrays.asList(classes));
+  }
+
+  protected ProcessResult runOnJavaRaw(String main, List<byte[]> classes) throws IOException {
+    Path file = writeToZip(classes);
     return ToolHelper.runJavaNoVerify(file, main);
   }
 
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 10f62a1..2667ce2 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -70,6 +70,7 @@
 public class ToolHelper {
 
   public static final String BUILD_DIR = "build/";
+  public static final String LIBS_DIR = BUILD_DIR + "libs/";
   public static final String TESTS_DIR = "src/test/";
   public static final String EXAMPLES_DIR = TESTS_DIR + "examples/";
   public static final String EXAMPLES_ANDROID_O_DIR = TESTS_DIR + "examplesAndroidO/";
@@ -78,6 +79,8 @@
   public static final String TESTS_BUILD_DIR = BUILD_DIR + "test/";
   public static final String EXAMPLES_BUILD_DIR = TESTS_BUILD_DIR + "examples/";
   public static final String EXAMPLES_KOTLIN_BUILD_DIR = TESTS_BUILD_DIR + "examplesKotlin/";
+  public static final String EXAMPLES_KOTLIN_RESOURCE_DIR =
+      TESTS_BUILD_DIR + "kotlinR8TestResources/";
   public static final String EXAMPLES_ANDROID_N_BUILD_DIR = TESTS_BUILD_DIR + "examplesAndroidN/";
   public static final String EXAMPLES_ANDROID_O_BUILD_DIR = TESTS_BUILD_DIR + "examplesAndroidO/";
   public static final String EXAMPLES_ANDROID_P_BUILD_DIR = TESTS_BUILD_DIR + "examplesAndroidP/";
@@ -742,10 +745,6 @@
     return parts;
   }
 
-  public static Path getPackageDirectoryForTestClass(Class clazz) {
-    return getPackageDirectoryForTestPackage(clazz.getPackage());
-  }
-
   public static List<Path> getClassFilesForTestPackage(Package pkg) throws IOException {
     Path dir = ToolHelper.getPackageDirectoryForTestPackage(pkg);
     return Files.walk(dir)
@@ -759,6 +758,11 @@
         Paths.get("", parts.toArray(new String[parts.size() - 1])));
   }
 
+  public static Path getFileNameForTestClass(Class clazz) {
+    List<String> parts = getNamePartsForTestClass(clazz);
+    return Paths.get("", parts.toArray(new String[parts.size() - 1]));
+  }
+
   public static String getJarEntryForTestClass(Class clazz) {
     List<String> parts = getNamePartsForTestClass(clazz);
     return String.join("/", parts);
@@ -979,6 +983,12 @@
     return forkJava(dir, R8.class, args);
   }
 
+  public static ProcessResult forkR8Jar(Path dir, String... args)
+      throws IOException, InterruptedException {
+    String r8Jar = Paths.get(LIBS_DIR,  "r8.jar").toAbsolutePath().toString();
+    return forkJavaWithJar(dir, r8Jar, Arrays.asList(args));
+  }
+
   public static ProcessResult forkGenerateMainDexList(Path dir, List<String> args1, String... args2)
       throws IOException, InterruptedException {
     List<String> args = new ArrayList<>();
@@ -997,8 +1007,18 @@
     return forkJava(dir, clazz, Arrays.asList(args));
   }
 
+  private static ProcessResult forkJavaWithJar(Path dir, String jarPath, List<String> args)
+      throws IOException {
+    List<String> command = new ImmutableList.Builder<String>()
+        .add(getJavaExecutable())
+        .add("-jar").add(jarPath)
+        .addAll(args)
+        .build();
+    return runProcess(new ProcessBuilder(command).directory(dir.toFile()));
+  }
+
   private static ProcessResult forkJava(Path dir, Class clazz, List<String> args)
-      throws IOException, InterruptedException {
+      throws IOException {
     List<String> command = new ImmutableList.Builder<String>()
         .add(getJavaExecutable())
         .add("-cp").add(System.getProperty("java.class.path"))
diff --git a/src/test/java/com/android/tools/r8/debug/ArrayDimensionGreaterThanSevenTest.java b/src/test/java/com/android/tools/r8/debug/ArrayDimensionGreaterThanSevenTest.java
new file mode 100644
index 0000000..68277dd
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/ArrayDimensionGreaterThanSevenTest.java
@@ -0,0 +1,41 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debug;
+
+public class ArrayDimensionGreaterThanSevenTest {
+
+  public static float foo(int x) {
+    try {
+      float[] fs1 = new float[] {42f};
+      float[][] fs2 = new float[][] {fs1};
+      float[][][] fs3 = new float[][][] {fs2};
+      float[][][][] fs4 = new float[][][][] {fs3};
+      float[][][][][] fs5 = new float[][][][][] {fs4};
+      float[][][][][][] fs6 = new float[][][][][][] {fs5};
+      float[][][][][][][] fs7 = new float[][][][][][][] {fs6};
+      float[][][][][][][][] fs8 = new float[][][][][][][][] {fs7};
+      while (x-- > 0) {
+        try {
+          fs8 = x == 0 ? fs8 : null;
+          fs7 = x == 1 ? fs8[1] : fs8[0];
+          fs6 = x == 2 ? fs7[1] : fs7[0];
+          fs5 = x == 3 ? fs6[1] : fs6[0];
+          fs4 = x == 4 ? fs5[1] : fs5[0];
+          fs3 = x == 5 ? fs4[1] : fs4[0];
+          fs2 = x == 6 ? fs3[1] : fs3[0];
+          fs1 = x == 7 ? fs2[1] : fs2[0];
+        } catch (NullPointerException e) {
+          System.out.println("null pointer");
+        }
+      }
+    } catch (RuntimeException e) {
+      return -1f;
+    }
+    return 42;
+  }
+
+  public static void main(String[] args) {
+    System.out.println(foo(args.length + 1));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/ArrayDimensionGreaterThanSevenTestDump.java b/src/test/java/com/android/tools/r8/debug/ArrayDimensionGreaterThanSevenTestDump.java
new file mode 100644
index 0000000..5cd652d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/ArrayDimensionGreaterThanSevenTestDump.java
@@ -0,0 +1,590 @@
+package com.android.tools.r8.debug;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+public class ArrayDimensionGreaterThanSevenTestDump implements Opcodes {
+
+  public static byte[] dump() throws Exception {
+
+    ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
+    FieldVisitor fv;
+    MethodVisitor mv;
+    AnnotationVisitor av0;
+
+    cw.visit(
+        V1_8,
+        ACC_PUBLIC + ACC_SUPER,
+        "com/android/tools/r8/debug/ArrayDimensionGreaterThanSevenTest",
+        null,
+        "java/lang/Object",
+        null);
+
+    {
+      mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+      mv.visitCode();
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+      mv.visitInsn(RETURN);
+      mv.visitMaxs(1, 1);
+      mv.visitEnd();
+    }
+    {
+      mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "foo", "(I)F", null, null);
+      mv.visitCode();
+      Label l0 = new Label();
+      Label l1 = new Label();
+      Label l2 = new Label();
+      mv.visitTryCatchBlock(l0, l1, l2, "java/lang/NullPointerException");
+      Label l3 = new Label();
+      Label l4 = new Label();
+      Label l5 = new Label();
+      mv.visitTryCatchBlock(l3, l4, l5, "java/lang/RuntimeException");
+      mv.visitLabel(l3);
+      mv.visitInsn(ICONST_1);
+      mv.visitIntInsn(NEWARRAY, T_FLOAT);
+      mv.visitInsn(DUP);
+      mv.visitInsn(ICONST_0);
+      mv.visitLdcInsn(new Float("42.0"));
+      mv.visitInsn(FASTORE);
+      mv.visitVarInsn(ASTORE, 1);
+      mv.visitInsn(ICONST_1);
+      mv.visitTypeInsn(ANEWARRAY, "[F");
+      mv.visitInsn(DUP);
+      mv.visitInsn(ICONST_0);
+      mv.visitVarInsn(ALOAD, 1);
+      mv.visitInsn(AASTORE);
+      mv.visitVarInsn(ASTORE, 2);
+      mv.visitInsn(ICONST_1);
+      mv.visitTypeInsn(ANEWARRAY, "[[F");
+      mv.visitInsn(DUP);
+      mv.visitInsn(ICONST_0);
+      mv.visitVarInsn(ALOAD, 2);
+      mv.visitInsn(AASTORE);
+      mv.visitVarInsn(ASTORE, 3);
+      mv.visitInsn(ICONST_1);
+      mv.visitTypeInsn(ANEWARRAY, "[[[F");
+      mv.visitInsn(DUP);
+      mv.visitInsn(ICONST_0);
+      mv.visitVarInsn(ALOAD, 3);
+      mv.visitInsn(AASTORE);
+      mv.visitVarInsn(ASTORE, 4);
+      mv.visitInsn(ICONST_1);
+      mv.visitTypeInsn(ANEWARRAY, "[[[[F");
+      mv.visitInsn(DUP);
+      mv.visitInsn(ICONST_0);
+      mv.visitVarInsn(ALOAD, 4);
+      mv.visitInsn(AASTORE);
+      mv.visitVarInsn(ASTORE, 5);
+      mv.visitInsn(ICONST_1);
+      mv.visitTypeInsn(ANEWARRAY, "[[[[[F");
+      mv.visitInsn(DUP);
+      mv.visitInsn(ICONST_0);
+      mv.visitVarInsn(ALOAD, 5);
+      mv.visitInsn(AASTORE);
+      mv.visitVarInsn(ASTORE, 6);
+      mv.visitInsn(ICONST_1);
+      mv.visitTypeInsn(ANEWARRAY, "[[[[[[F");
+      mv.visitInsn(DUP);
+      mv.visitInsn(ICONST_0);
+      mv.visitVarInsn(ALOAD, 6);
+      mv.visitInsn(AASTORE);
+      mv.visitVarInsn(ASTORE, 7);
+      mv.visitInsn(ICONST_1);
+      mv.visitTypeInsn(ANEWARRAY, "[[[[[[[F");
+      mv.visitInsn(DUP);
+      mv.visitInsn(ICONST_0);
+      mv.visitVarInsn(ALOAD, 7);
+      mv.visitInsn(AASTORE);
+      mv.visitVarInsn(ASTORE, 8);
+      Label l6 = new Label();
+      mv.visitLabel(l6);
+      // mv.visitFrame(Opcodes.F_FULL, 9, new Object[] {Opcodes.INTEGER, "[F", "[[F", "[[[F",
+      // "[[[[F", "[[[[[F", "[[[[[[F", "[[[[[[[F", "[[[[[[[[F"}, 0, new Object[] {});
+      mv.visitFrame(
+          Opcodes.F_NEW,
+          9,
+          new Object[] {
+            Opcodes.INTEGER,
+            "[F",
+            "[[F",
+            "[[[F",
+            "[[[[F",
+            "[[[[[F",
+            "[[[[[[F",
+            "[[[[[[[F",
+            "[[[[[[[[F"
+          },
+          0,
+          new Object[] {});
+      mv.visitVarInsn(ILOAD, 0);
+      mv.visitIincInsn(0, -1);
+      mv.visitJumpInsn(IFLE, l4);
+      mv.visitLabel(l0);
+      mv.visitVarInsn(ILOAD, 0);
+      Label l7 = new Label();
+      mv.visitJumpInsn(IFNE, l7);
+      mv.visitVarInsn(ALOAD, 8);
+      Label l8 = new Label();
+      mv.visitJumpInsn(GOTO, l8);
+      mv.visitLabel(l7);
+      // mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+      mv.visitFrame(
+          Opcodes.F_NEW,
+          9,
+          new Object[] {
+            Opcodes.INTEGER,
+            "[F",
+            "[[F",
+            "[[[F",
+            "[[[[F",
+            "[[[[[F",
+            "[[[[[[F",
+            "[[[[[[[F",
+            "[[[[[[[[F"
+          },
+          0,
+          new Object[] {});
+      mv.visitInsn(ACONST_NULL);
+      mv.visitTypeInsn(CHECKCAST, "[[[[[[[[F");
+      mv.visitLabel(l8);
+      // mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {"[[[[[[[[F"});
+      mv.visitFrame(
+          Opcodes.F_NEW,
+          9,
+          new Object[] {
+            Opcodes.INTEGER,
+            "[F",
+            "[[F",
+            "[[[F",
+            "[[[[F",
+            "[[[[[F",
+            "[[[[[[F",
+            "[[[[[[[F",
+            "[[[[[[[[F"
+          },
+          1,
+          new Object[] {"[[[[[[[[F"});
+      mv.visitVarInsn(ASTORE, 8);
+      mv.visitVarInsn(ILOAD, 0);
+      mv.visitInsn(ICONST_1);
+      Label l9 = new Label();
+      mv.visitJumpInsn(IF_ICMPNE, l9);
+      mv.visitVarInsn(ALOAD, 8);
+      mv.visitInsn(ICONST_1);
+      mv.visitInsn(AALOAD);
+      Label l10 = new Label();
+      mv.visitJumpInsn(GOTO, l10);
+      mv.visitLabel(l9);
+      // mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+      mv.visitFrame(
+          Opcodes.F_NEW,
+          9,
+          new Object[] {
+            Opcodes.INTEGER,
+            "[F",
+            "[[F",
+            "[[[F",
+            "[[[[F",
+            "[[[[[F",
+            "[[[[[[F",
+            "[[[[[[[F",
+            "[[[[[[[[F"
+          },
+          0,
+          new Object[] {});
+      mv.visitVarInsn(ALOAD, 8);
+      mv.visitInsn(ICONST_0);
+      mv.visitInsn(AALOAD);
+      mv.visitLabel(l10);
+      // mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {"[[[[[[[F"});
+      mv.visitFrame(
+          Opcodes.F_NEW,
+          9,
+          new Object[] {
+            Opcodes.INTEGER,
+            "[F",
+            "[[F",
+            "[[[F",
+            "[[[[F",
+            "[[[[[F",
+            "[[[[[[F",
+            "[[[[[[[F",
+            "[[[[[[[[F"
+          },
+          1,
+          new Object[] {"[[[[[[[F"});
+      mv.visitVarInsn(ASTORE, 7);
+      mv.visitVarInsn(ILOAD, 0);
+      mv.visitInsn(ICONST_2);
+      Label l11 = new Label();
+      mv.visitJumpInsn(IF_ICMPNE, l11);
+      mv.visitVarInsn(ALOAD, 7);
+      mv.visitInsn(ICONST_1);
+      mv.visitInsn(AALOAD);
+      Label l12 = new Label();
+      mv.visitJumpInsn(GOTO, l12);
+      mv.visitLabel(l11);
+      // mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+      mv.visitFrame(
+          Opcodes.F_NEW,
+          9,
+          new Object[] {
+            Opcodes.INTEGER,
+            "[F",
+            "[[F",
+            "[[[F",
+            "[[[[F",
+            "[[[[[F",
+            "[[[[[[F",
+            "[[[[[[[F",
+            "[[[[[[[[F"
+          },
+          0,
+          new Object[] {});
+      mv.visitVarInsn(ALOAD, 7);
+      mv.visitInsn(ICONST_0);
+      mv.visitInsn(AALOAD);
+      mv.visitLabel(l12);
+      // mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {"[[[[[[F"});
+      mv.visitFrame(
+          Opcodes.F_NEW,
+          9,
+          new Object[] {
+            Opcodes.INTEGER,
+            "[F",
+            "[[F",
+            "[[[F",
+            "[[[[F",
+            "[[[[[F",
+            "[[[[[[F",
+            "[[[[[[[F",
+            "[[[[[[[[F"
+          },
+          1,
+          new Object[] {"[[[[[[F"});
+      mv.visitVarInsn(ASTORE, 6);
+      mv.visitVarInsn(ILOAD, 0);
+      mv.visitInsn(ICONST_3);
+      Label l13 = new Label();
+      mv.visitJumpInsn(IF_ICMPNE, l13);
+      mv.visitVarInsn(ALOAD, 6);
+      mv.visitInsn(ICONST_1);
+      mv.visitInsn(AALOAD);
+      Label l14 = new Label();
+      mv.visitJumpInsn(GOTO, l14);
+      mv.visitLabel(l13);
+      // mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+      mv.visitFrame(
+          Opcodes.F_NEW,
+          9,
+          new Object[] {
+            Opcodes.INTEGER,
+            "[F",
+            "[[F",
+            "[[[F",
+            "[[[[F",
+            "[[[[[F",
+            "[[[[[[F",
+            "[[[[[[[F",
+            "[[[[[[[[F"
+          },
+          0,
+          new Object[] {});
+      mv.visitVarInsn(ALOAD, 6);
+      mv.visitInsn(ICONST_0);
+      mv.visitInsn(AALOAD);
+      mv.visitLabel(l14);
+      // mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {"[[[[[F"});
+      mv.visitFrame(
+          Opcodes.F_NEW,
+          9,
+          new Object[] {
+            Opcodes.INTEGER,
+            "[F",
+            "[[F",
+            "[[[F",
+            "[[[[F",
+            "[[[[[F",
+            "[[[[[[F",
+            "[[[[[[[F",
+            "[[[[[[[[F"
+          },
+          1,
+          new Object[] {"[[[[[F"});
+      mv.visitVarInsn(ASTORE, 5);
+      mv.visitVarInsn(ILOAD, 0);
+      mv.visitInsn(ICONST_4);
+      Label l15 = new Label();
+      mv.visitJumpInsn(IF_ICMPNE, l15);
+      mv.visitVarInsn(ALOAD, 5);
+      mv.visitInsn(ICONST_1);
+      mv.visitInsn(AALOAD);
+      Label l16 = new Label();
+      mv.visitJumpInsn(GOTO, l16);
+      mv.visitLabel(l15);
+      // mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+      mv.visitFrame(
+          Opcodes.F_NEW,
+          9,
+          new Object[] {
+            Opcodes.INTEGER,
+            "[F",
+            "[[F",
+            "[[[F",
+            "[[[[F",
+            "[[[[[F",
+            "[[[[[[F",
+            "[[[[[[[F",
+            "[[[[[[[[F"
+          },
+          0,
+          new Object[] {});
+      mv.visitVarInsn(ALOAD, 5);
+      mv.visitInsn(ICONST_0);
+      mv.visitInsn(AALOAD);
+      mv.visitLabel(l16);
+      // mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {"[[[[F"});
+      mv.visitFrame(
+          Opcodes.F_NEW,
+          9,
+          new Object[] {
+            Opcodes.INTEGER,
+            "[F",
+            "[[F",
+            "[[[F",
+            "[[[[F",
+            "[[[[[F",
+            "[[[[[[F",
+            "[[[[[[[F",
+            "[[[[[[[[F"
+          },
+          1,
+          new Object[] {"[[[[F"});
+      mv.visitVarInsn(ASTORE, 4);
+      mv.visitVarInsn(ILOAD, 0);
+      mv.visitInsn(ICONST_5);
+      Label l17 = new Label();
+      mv.visitJumpInsn(IF_ICMPNE, l17);
+      mv.visitVarInsn(ALOAD, 4);
+      mv.visitInsn(ICONST_1);
+      mv.visitInsn(AALOAD);
+      Label l18 = new Label();
+      mv.visitJumpInsn(GOTO, l18);
+      mv.visitLabel(l17);
+      // mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+      mv.visitFrame(
+          Opcodes.F_NEW,
+          9,
+          new Object[] {
+            Opcodes.INTEGER,
+            "[F",
+            "[[F",
+            "[[[F",
+            "[[[[F",
+            "[[[[[F",
+            "[[[[[[F",
+            "[[[[[[[F",
+            "[[[[[[[[F"
+          },
+          0,
+          new Object[] {});
+      mv.visitVarInsn(ALOAD, 4);
+      mv.visitInsn(ICONST_0);
+      mv.visitInsn(AALOAD);
+      mv.visitLabel(l18);
+      // mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {"[[[F"});
+      mv.visitFrame(
+          Opcodes.F_NEW,
+          9,
+          new Object[] {
+            Opcodes.INTEGER,
+            "[F",
+            "[[F",
+            "[[[F",
+            "[[[[F",
+            "[[[[[F",
+            "[[[[[[F",
+            "[[[[[[[F",
+            "[[[[[[[[F"
+          },
+          1,
+          new Object[] {"[[[F"});
+      mv.visitVarInsn(ASTORE, 3);
+      mv.visitVarInsn(ILOAD, 0);
+      mv.visitIntInsn(BIPUSH, 6);
+      Label l19 = new Label();
+      mv.visitJumpInsn(IF_ICMPNE, l19);
+      mv.visitVarInsn(ALOAD, 3);
+      mv.visitInsn(ICONST_1);
+      mv.visitInsn(AALOAD);
+      Label l20 = new Label();
+      mv.visitJumpInsn(GOTO, l20);
+      mv.visitLabel(l19);
+      // mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+      mv.visitFrame(
+          Opcodes.F_NEW,
+          9,
+          new Object[] {
+            Opcodes.INTEGER,
+            "[F",
+            "[[F",
+            "[[[F",
+            "[[[[F",
+            "[[[[[F",
+            "[[[[[[F",
+            "[[[[[[[F",
+            "[[[[[[[[F"
+          },
+          0,
+          new Object[] {});
+      mv.visitVarInsn(ALOAD, 3);
+      mv.visitInsn(ICONST_0);
+      mv.visitInsn(AALOAD);
+      mv.visitLabel(l20);
+      // mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {"[[F"});
+      mv.visitFrame(
+          Opcodes.F_NEW,
+          9,
+          new Object[] {
+            Opcodes.INTEGER,
+            "[F",
+            "[[F",
+            "[[[F",
+            "[[[[F",
+            "[[[[[F",
+            "[[[[[[F",
+            "[[[[[[[F",
+            "[[[[[[[[F"
+          },
+          1,
+          new Object[] {"[[F"});
+      mv.visitVarInsn(ASTORE, 2);
+      mv.visitVarInsn(ILOAD, 0);
+      mv.visitIntInsn(BIPUSH, 7);
+      Label l21 = new Label();
+      mv.visitJumpInsn(IF_ICMPNE, l21);
+      mv.visitVarInsn(ALOAD, 2);
+      mv.visitInsn(ICONST_1);
+      mv.visitInsn(AALOAD);
+      Label l22 = new Label();
+      mv.visitJumpInsn(GOTO, l22);
+      mv.visitLabel(l21);
+      // mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+      mv.visitFrame(
+          Opcodes.F_NEW,
+          9,
+          new Object[] {
+            Opcodes.INTEGER,
+            "[F",
+            "[[F",
+            "[[[F",
+            "[[[[F",
+            "[[[[[F",
+            "[[[[[[F",
+            "[[[[[[[F",
+            "[[[[[[[[F"
+          },
+          0,
+          new Object[] {});
+      mv.visitVarInsn(ALOAD, 2);
+      mv.visitInsn(ICONST_0);
+      mv.visitInsn(AALOAD);
+      mv.visitLabel(l22);
+      // mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {"[F"});
+      mv.visitFrame(
+          Opcodes.F_NEW,
+          9,
+          new Object[] {
+            Opcodes.INTEGER,
+            "[F",
+            "[[F",
+            "[[[F",
+            "[[[[F",
+            "[[[[[F",
+            "[[[[[[F",
+            "[[[[[[[F",
+            "[[[[[[[[F"
+          },
+          1,
+          new Object[] {"[F"});
+      mv.visitVarInsn(ASTORE, 1);
+      mv.visitLabel(l1);
+      mv.visitJumpInsn(GOTO, l6);
+      mv.visitLabel(l2);
+      // mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[]
+      // {"java/lang/NullPointerException"});
+      mv.visitFrame(
+          Opcodes.F_NEW,
+          9,
+          new Object[] {
+            Opcodes.INTEGER,
+            "[F",
+            "[[F",
+            "[[[F",
+            "[[[[F",
+            "[[[[[F",
+            "[[[[[[F",
+            "[[[[[[[F",
+            "[[[[[[[[F"
+          },
+          1,
+          new Object[] {"java/lang/NullPointerException"});
+      mv.visitVarInsn(ASTORE, 9);
+      mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+      mv.visitLdcInsn("null pointer");
+      mv.visitMethodInsn(
+          INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
+      mv.visitJumpInsn(GOTO, l6);
+      mv.visitLabel(l4);
+      // mv.visitFrame(Opcodes.F_FULL, 1, new Object[] {Opcodes.INTEGER}, 0, new Object[] {});
+      mv.visitFrame(Opcodes.F_NEW, 1, new Object[] {Opcodes.INTEGER}, 0, new Object[] {});
+      Label l23 = new Label();
+      mv.visitJumpInsn(GOTO, l23);
+      mv.visitLabel(l5);
+      // mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {"java/lang/RuntimeException"});
+      mv.visitFrame(
+          Opcodes.F_NEW,
+          1,
+          new Object[] {Opcodes.INTEGER},
+          1,
+          new Object[] {"java/lang/RuntimeException"});
+      mv.visitVarInsn(ASTORE, 1);
+      mv.visitLdcInsn(new Float("-1.0"));
+      mv.visitInsn(FRETURN);
+      mv.visitLabel(l23);
+      // mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+      mv.visitFrame(Opcodes.F_NEW, 1, new Object[] {Opcodes.INTEGER}, 0, new Object[] {});
+      mv.visitLdcInsn(new Float("42.0"));
+      mv.visitInsn(FRETURN);
+      mv.visitMaxs(4, 10);
+      mv.visitEnd();
+    }
+    {
+      mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
+      mv.visitCode();
+      mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitInsn(ARRAYLENGTH);
+      mv.visitInsn(ICONST_1);
+      mv.visitInsn(IADD);
+      mv.visitMethodInsn(
+          INVOKESTATIC,
+          "com/android/tools/r8/debug/ArrayDimensionGreaterThanSevenTest",
+          "foo",
+          "(I)F",
+          false);
+      mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(F)V", false);
+      mv.visitInsn(RETURN);
+      mv.visitMaxs(3, 1);
+      mv.visitEnd();
+    }
+    cw.visitEnd();
+
+    return cw.toByteArray();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/ArrayDimensionGreaterThanSevenTestRunner.java b/src/test/java/com/android/tools/r8/debug/ArrayDimensionGreaterThanSevenTestRunner.java
new file mode 100644
index 0000000..7866e1b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/ArrayDimensionGreaterThanSevenTestRunner.java
@@ -0,0 +1,82 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debug;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.DebuggeeState;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class ArrayDimensionGreaterThanSevenTestRunner extends DebugTestBase {
+
+  private static final Class CLASS = ArrayDimensionGreaterThanSevenTest.class;
+  private static final String NAME = CLASS.getCanonicalName();
+
+  private DebugTestConfig getR8CfConfig(String s, Consumer<InternalOptions> optionsConsumer)
+      throws IOException, com.android.tools.r8.CompilationFailedException {
+    Path cfOut = temp.getRoot().toPath().resolve(s);
+    ToolHelper.runR8(
+        R8Command.builder()
+            .addClassProgramData(ToolHelper.getClassAsBytes(CLASS), Origin.unknown())
+            .setMode(CompilationMode.DEBUG)
+            .setOutput(cfOut, OutputMode.ClassFile)
+            .build(),
+        optionsConsumer);
+    return new CfDebugTestConfig(cfOut);
+  }
+
+  private Stream<DebuggeeState> createStream(DebugTestConfig config) throws Exception {
+    return streamDebugTest(config, NAME, ANDROID_FILTER);
+  }
+
+  @Test
+  @Ignore("b/111296969")
+  // Once R8 does not use expanded frames this can be enabled again.
+  public void test() throws Exception {
+    DebugTestConfig cfConfig = new CfDebugTestConfig().addPaths(ToolHelper.getClassPathForTests());
+    DebugTestConfig d8Config = new D8DebugTestConfig().compileAndAddClasses(temp, CLASS);
+    DebugTestConfig r8JarConfig =
+        getR8CfConfig("r8jar.jar", options -> options.enableCfFrontend = false);
+    DebugTestConfig r8CfConfig =
+        getR8CfConfig("r8cf.jar", options -> options.enableCfFrontend = true);
+    new DebugStreamComparator()
+        .add("CF", createStream(cfConfig))
+        .add("R8/CF", createStream(r8CfConfig))
+        .add("R8/Jar", createStream(r8JarConfig))
+        .add("D8", createStream(d8Config))
+        .compare();
+  }
+
+  @Test
+  // Verify that ASM fails when using expanded frames directly.
+  // See b/111296969
+  public void runTestOnAsmDump() throws Exception {
+    Path out = temp.getRoot().toPath().resolve("out.jar");
+    ArchiveConsumer consumer = new ArchiveConsumer(out);
+    consumer.accept(
+        ArrayDimensionGreaterThanSevenTestDump.dump(),
+        DescriptorUtils.javaTypeToDescriptor(NAME),
+        null);
+    consumer.finished(null);
+    ProcessResult result = ToolHelper.runJava(out, NAME);
+    assertEquals("Expected ASM to fail when using visitFrame(F_NEW, ...)", 1, result.exitCode);
+    assertThat(result.stderr, containsString("java.lang.NoClassDefFoundError: F"));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/DebugStreamComparator.java b/src/test/java/com/android/tools/r8/debug/DebugStreamComparator.java
index eb85a7c..3a83e30 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugStreamComparator.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugStreamComparator.java
@@ -214,7 +214,8 @@
       try {
         if (done) {
           assertTrue(
-              "Not all streams completed at the same time",
+              "Not all streams completed at the same time. "
+                  + "Set 'DebugTestBase.DEBUG_TEST = true' to aid in diagnosing the issue.",
               states.stream().allMatch(Objects::isNull));
           return;
         } else {
diff --git a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
index bda9ac5..6877617 100644
--- a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
@@ -10,11 +10,22 @@
 
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.graph.invokesuper2.C0;
+import com.android.tools.r8.graph.invokesuper2.C1;
+import com.android.tools.r8.graph.invokesuper2.C2;
+import com.android.tools.r8.graph.invokesuper2.I0;
+import com.android.tools.r8.graph.invokesuper2.I1;
+import com.android.tools.r8.graph.invokesuper2.I2;
+import com.android.tools.r8.graph.invokesuper2.I3;
+import com.android.tools.r8.graph.invokesuper2.I4;
+import com.android.tools.r8.graph.invokesuper2.Main;
 import com.android.tools.r8.smali.SmaliBuilder;
 import com.android.tools.r8.smali.SmaliTestBase;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.DexInspector;
 import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
 import java.util.Collections;
 import org.junit.Test;
 
@@ -200,6 +211,63 @@
     AndroidApp processedApp = processApplication(application);
     assertEquals("42", runArt(processedApp));
   }
+
+  @Test
+  public void testLookupSuperTarget() throws Exception {
+    String pkg = Main.class.getPackage().getName().replace('.', '/');
+
+    AndroidApp.Builder builder = AndroidApp.builder();
+    for (Class clazz : new Class[]{
+        I0.class, I1.class, I2.class, I3.class, I4.class,
+        C0.class, C1.class, C2.class,
+        Main.class}) {
+      builder.addProgramFiles(ToolHelper.getClassFileForTestClass(clazz));
+      // At least java.lang.Object is needed as interface method lookup have special handling
+      // of methods on java.lang.Object.
+      builder.addLibraryFiles(ToolHelper.getDefaultAndroidJar());
+    }
+    AndroidApp application = builder.build();
+    AppInfo appInfo = getAppInfo(application);
+    DexItemFactory factory = appInfo.dexItemFactory;
+
+    DexType i0 = factory.createType("L" + pkg + "/I0;");
+    DexType i1 = factory.createType("L" + pkg + "/I1;");
+    DexType i2 = factory.createType("L" + pkg + "/I2;");
+    DexType i3 = factory.createType("L" + pkg + "/I3;");
+    DexType i4 = factory.createType("L" + pkg + "/I4;");
+    DexType c0 = factory.createType("L" + pkg + "/C0;");
+    DexType c1 = factory.createType("L" + pkg + "/C1;");
+    DexType c2 = factory.createType("L" + pkg + "/C2;");
+
+    DexProto mProto = factory.createProto(factory.intType);
+    DexString m = factory.createString("m");
+    DexMethod mOnC0 = factory.createMethod(c0, mProto, m);
+    DexMethod mOnC1 = factory.createMethod(c1, mProto, m);
+    DexMethod mOnI0 = factory.createMethod(i0, mProto, m);
+    DexMethod mOnI1 = factory.createMethod(i1, mProto, m);
+    DexMethod mOnI2 = factory.createMethod(i2, mProto, m);
+    DexMethod mOnI3 = factory.createMethod(i3, mProto, m);
+    DexMethod mOnI4 = factory.createMethod(i4, mProto, m);
+
+    assertEquals(mOnI0, appInfo.lookupSuperTarget(mOnC0, c1).method);
+    assertEquals(mOnI1, appInfo.lookupSuperTarget(mOnI1, c1).method);
+    assertEquals(mOnI2, appInfo.lookupSuperTarget(mOnI2, c1).method);
+
+    assertEquals(mOnI0, appInfo.lookupSuperTarget(mOnC1, c2).method);
+    assertEquals(mOnI1, appInfo.lookupSuperTarget(mOnI3, c2).method);
+    assertEquals(mOnI2, appInfo.lookupSuperTarget(mOnI4, c2).method);
+
+    // Copy classes to run on the Java VM.
+    Path out = temp.newFolder().toPath();
+    copyTestClasses(out, I0.class, I1.class, I2.class, I3.class, I4.class);
+    copyTestClasses(out, C0.class, C1.class, C2.class, Main.class);
+    ProcessResult result = ToolHelper.runJava(out, Main.class.getCanonicalName());
+    assertEquals(0, result.exitCode);
+
+    // Process the application and expect the same result on Art.
+    AndroidApp processedApp = processApplication(application);
+    assertEquals(result.stdout, runArt(processedApp, Main.class.getCanonicalName()));
+  }
 }
 
 
diff --git a/src/test/java/com/android/tools/r8/graph/invokesuper2/C0.java b/src/test/java/com/android/tools/r8/graph/invokesuper2/C0.java
new file mode 100644
index 0000000..8e92fb4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/invokesuper2/C0.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph.invokesuper2;
+
+public class C0  implements I0 {
+}
diff --git a/src/test/java/com/android/tools/r8/graph/invokesuper2/C1.java b/src/test/java/com/android/tools/r8/graph/invokesuper2/C1.java
new file mode 100644
index 0000000..ff98da9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/invokesuper2/C1.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph.invokesuper2;
+
+public class C1 extends C0 implements I1, I2 {
+  public int m() {
+    // super.m() becomes: invokespecial com/android/tools/r8/graph/invokesuper2/C0.m()I
+    System.out.println(super.m());
+    // I1.super.m() becomes: invokespecial com/android/tools/r8/graph/invokesuper2/I1.m:()I
+    System.out.println(I1.super.m());
+    // I2.super.m() becomes: invokespecial com/android/tools/r8/graph/invokesuper2/I2.m:()I
+    System.out.println(I2.super.m());
+    return 3;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/graph/invokesuper2/C2.java b/src/test/java/com/android/tools/r8/graph/invokesuper2/C2.java
new file mode 100644
index 0000000..abdf7ff
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/invokesuper2/C2.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph.invokesuper2;
+
+public class C2 extends C0 implements I3, I4 {
+  public int m() {
+    // super.m() becomes: invokespecial com/android/tools/r8/graph/invokesuper2/C0.m()I
+    System.out.println(super.m());
+    // I1.super.m() becomes: invokespecial com/android/tools/r8/graph/invokesuper2/I3.m:()I
+    System.out.println(I3.super.m());
+    // I2.super.m() becomes: invokespecial com/android/tools/r8/graph/invokesuper2/I4.m:()I
+    System.out.println(I4.super.m());
+    return 3;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/graph/invokesuper2/I0.java b/src/test/java/com/android/tools/r8/graph/invokesuper2/I0.java
new file mode 100644
index 0000000..02b59a6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/invokesuper2/I0.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph.invokesuper2;
+
+public interface I0 {
+  default int m() {
+    return 0;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/graph/invokesuper2/I1.java b/src/test/java/com/android/tools/r8/graph/invokesuper2/I1.java
new file mode 100644
index 0000000..0593d90
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/invokesuper2/I1.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph.invokesuper2;
+
+public interface I1 extends I0 {
+  default int m() {
+    return 1;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/graph/invokesuper2/I2.java b/src/test/java/com/android/tools/r8/graph/invokesuper2/I2.java
new file mode 100644
index 0000000..22956c3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/invokesuper2/I2.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph.invokesuper2;
+
+public interface I2 extends I0 {
+  default int m() {
+    return 2;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/graph/invokesuper2/I3.java b/src/test/java/com/android/tools/r8/graph/invokesuper2/I3.java
new file mode 100644
index 0000000..e64154e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/invokesuper2/I3.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph.invokesuper2;
+
+public interface I3 extends I1 {
+}
diff --git a/src/test/java/com/android/tools/r8/graph/invokesuper2/I4.java b/src/test/java/com/android/tools/r8/graph/invokesuper2/I4.java
new file mode 100644
index 0000000..ddc5ad0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/invokesuper2/I4.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph.invokesuper2;
+
+public interface I4 extends I2 {
+}
diff --git a/src/test/java/com/android/tools/r8/graph/invokesuper2/Main.java b/src/test/java/com/android/tools/r8/graph/invokesuper2/Main.java
new file mode 100644
index 0000000..6a2a3b9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/invokesuper2/Main.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph.invokesuper2;
+
+public class Main {
+
+  public static void main(String[] args) {
+    System.out.println(new C1().m());
+    System.out.println(new C2().m());
+  }
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java b/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
index 82381dd..9c15ce7 100644
--- a/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
+++ b/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
@@ -353,12 +353,16 @@
     return out.toByteArray();
   }
 
-  public List<byte[]> buildClasses() throws Exception {
-    List<byte[]> result = new ArrayList<>();
+  public ImmutableList.Builder<byte[]> buildClasses(ImmutableList.Builder<byte[]> builder)
+      throws Exception {
     for (ClassBuilder clazz : classes) {
-      result.add(compile(clazz));
+      builder.add(compile(clazz));
     }
-    return result;
+    return builder;
+  }
+
+  public List<byte[]> buildClasses() throws Exception {
+    return buildClasses(ImmutableList.builder()).build();
   }
 
   public AndroidApp build() throws Exception {
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
index 81e12a1..dc2774a 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -10,6 +10,7 @@
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.code.InvokeStatic;
 import com.android.tools.r8.code.NewInstance;
 import com.android.tools.r8.code.SgetObject;
 import com.android.tools.r8.graph.DexClass;
@@ -19,8 +20,10 @@
 import com.android.tools.r8.utils.DexInspector;
 import com.android.tools.r8.utils.DexInspector.ClassSubject;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 import java.util.Collection;
+import java.util.List;
 import java.util.Set;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
@@ -80,25 +83,25 @@
 
       assertEquals(
           Sets.newHashSet(),
-          collectAccessedLambdaTypes(lambdaCheck, clazz, "testStateless"));
+          collectAccessedTypes(lambdaCheck, clazz, "testStateless"));
 
       assertEquals(
           Sets.newHashSet(),
-          collectAccessedLambdaTypes(lambdaCheck, clazz, "testStateful"));
+          collectAccessedTypes(lambdaCheck, clazz, "testStateful"));
 
       assertFalse(
           inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateful$1").isPresent());
 
       assertEquals(
           Sets.newHashSet(),
-          collectAccessedLambdaTypes(lambdaCheck, clazz, "testStateful2"));
+          collectAccessedTypes(lambdaCheck, clazz, "testStateful2"));
 
       assertFalse(
           inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateful2$1").isPresent());
 
       assertEquals(
           Sets.newHashSet(),
-          collectAccessedLambdaTypes(lambdaCheck, clazz, "testStateful3"));
+          collectAccessedTypes(lambdaCheck, clazz, "testStateful3"));
 
       assertFalse(
           inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateful3$1").isPresent());
@@ -138,7 +141,7 @@
 
       assertEquals(
           Sets.newHashSet(),
-          collectAccessedLambdaTypes(lambdaCheck, clazz,
+          collectAccessedTypes(lambdaCheck, clazz,
               "testKotlinSequencesStateless", "kotlin.sequences.Sequence"));
 
       assertFalse(inspector.clazz(
@@ -146,7 +149,7 @@
 
       assertEquals(
           Sets.newHashSet(),
-          collectAccessedLambdaTypes(lambdaCheck, clazz,
+          collectAccessedTypes(lambdaCheck, clazz,
               "testKotlinSequencesStateful", "int", "int", "kotlin.sequences.Sequence"));
 
       assertFalse(inspector.clazz(
@@ -154,7 +157,7 @@
 
       assertEquals(
           Sets.newHashSet(),
-          collectAccessedLambdaTypes(lambdaCheck, clazz, "testBigExtraMethod"));
+          collectAccessedTypes(lambdaCheck, clazz, "testBigExtraMethod"));
 
       assertFalse(inspector.clazz(
           "class_inliner_lambda_k_style.MainKt$testBigExtraMethod$1").isPresent());
@@ -165,7 +168,7 @@
 
       assertEquals(
           Sets.newHashSet(),
-          collectAccessedLambdaTypes(lambdaCheck, clazz, "testBigExtraMethodReturningLambda"));
+          collectAccessedTypes(lambdaCheck, clazz, "testBigExtraMethodReturningLambda"));
 
       assertFalse(inspector.clazz(
           "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda$1")
@@ -179,8 +182,25 @@
     });
   }
 
-  private Set<String> collectAccessedLambdaTypes(
-      Predicate<DexType> isLambdaType, ClassSubject clazz, String methodName, String... params) {
+  @Test
+  public void testDataClass() throws Exception {
+    final String mainClassName = "class_inliner_data_class.MainKt";
+    runTest("class_inliner_data_class", mainClassName, true, (app) -> {
+      DexInspector inspector = new DexInspector(app);
+      ClassSubject clazz = inspector.clazz(mainClassName);
+      assertTrue(collectAccessedTypes(
+          type -> !type.toSourceString().startsWith("java."),
+          clazz, "main", String[].class.getCanonicalName()).isEmpty());
+      assertEquals(
+          Lists.newArrayList(
+              "void kotlin.jvm.internal.Intrinsics.throwParameterIsNullException(java.lang.String)"
+          ),
+          collectStaticCalls(clazz, "main", String[].class.getCanonicalName()));
+    });
+  }
+
+  private Set<String> collectAccessedTypes(Predicate<DexType> isTypeOfInterest,
+      ClassSubject clazz, String methodName, String... params) {
     assertNotNull(clazz);
     MethodSignature signature = new MethodSignature(methodName, "void", params);
     DexCode code = clazz.method(signature).getMethod().getCode().asDexCode();
@@ -190,7 +210,7 @@
         filterInstructionKind(code, SgetObject.class)
             .map(insn -> insn.getField().getHolder())
     )
-        .filter(isLambdaType)
+        .filter(isTypeOfInterest)
         .map(DexType::toSourceString)
         .collect(Collectors.toSet());
   }
@@ -205,4 +225,14 @@
           options.enableLambdaMerging = false;
         }, inspector);
   }
+
+  private List<String> collectStaticCalls(ClassSubject clazz, String methodName, String... params) {
+    assertNotNull(clazz);
+    MethodSignature signature = new MethodSignature(methodName, "void", params);
+    DexCode code = clazz.method(signature).getMethod().getCode().asDexCode();
+    return filterInstructionKind(code, InvokeStatic.class)
+        .map(insn -> insn.getMethod().toSourceString())
+        .sorted()
+        .collect(Collectors.toList());
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java
index e1431a6..062095c 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java
@@ -43,7 +43,7 @@
     abstract boolean match(DexClass clazz);
   }
 
-  private static class Group extends LambdaOrGroup {
+  static class Group extends LambdaOrGroup {
     final String pkg;
     final String capture;
     final int arity;
@@ -83,31 +83,32 @@
     }
   }
 
-  private Group kstyleImpl(String pkg, String capture, int arity, int singletons) {
+  private static Group kstyleImpl(String pkg, String capture, int arity, int singletons) {
     assertEquals(capture.isEmpty(), singletons != 0);
     return new Group(pkg, capture, arity, KOTLIN_FUNCTION_IFACE_STR + arity, singletons);
   }
 
-  private Group kstyle(String pkg, int arity, int singletons) {
+  static Group kstyle(String pkg, int arity, int singletons) {
     assertTrue(singletons != 0);
     return kstyleImpl(pkg, "", arity, singletons);
   }
 
-  private Group kstyle(String pkg, String capture, int arity) {
+  private static Group kstyle(String pkg, String capture, int arity) {
     assertFalse(capture.isEmpty());
     return kstyleImpl(pkg, capture, arity, 0);
   }
 
-  private Group jstyleImpl(String pkg, String capture, int arity, String sam, int singletons) {
+  private static Group jstyleImpl(
+      String pkg, String capture, int arity, String sam, int singletons) {
     assertTrue(capture.isEmpty() || singletons == 0);
     return new Group(pkg, capture, arity, sam, singletons);
   }
 
-  private Group jstyle(String pkg, String capture, int arity, String sam) {
+  private static Group jstyle(String pkg, String capture, int arity, String sam) {
     return jstyleImpl(pkg, capture, arity, sam, 0);
   }
 
-  private Group jstyle(String pkg, int arity, String sam, int singletons) {
+  private static Group jstyle(String pkg, int arity, String sam, int singletons) {
     return jstyleImpl(pkg, "", arity, sam, singletons);
   }
 
@@ -116,7 +117,7 @@
     final String name;
     final int arity;
 
-    private Lambda(String pkg, String name, int arity) {
+    Lambda(String pkg, String name, int arity) {
       this.pkg = pkg;
       this.name = name;
       this.arity = arity;
@@ -143,7 +144,15 @@
     final List<DexClass> groups = new ArrayList<>();
 
     Verifier(AndroidApp app) throws IOException, ExecutionException {
-      this.dexInspector = new DexInspector(app);
+      this(new DexInspector(app));
+    }
+
+    Verifier(DexInspector dexInspector) {
+      this.dexInspector = dexInspector;
+      initGroupsAndLambdas();
+    }
+
+    private void initGroupsAndLambdas() {
       dexInspector.forAllClasses(clazz -> {
         DexClass dexClass = clazz.getDexClass();
         if (isLambdaOrGroup(dexClass)) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinxMetadataExtensionsServiceTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinxMetadataExtensionsServiceTest.java
new file mode 100644
index 0000000..2beb777
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinxMetadataExtensionsServiceTest.java
@@ -0,0 +1,108 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.kotlin;
+
+import static com.android.tools.r8.ToolHelper.EXAMPLES_KOTLIN_RESOURCE_DIR;
+import static com.android.tools.r8.kotlin.KotlinLambdaMergingTest.kstyle;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.kotlin.KotlinLambdaMergingTest.Group;
+import com.android.tools.r8.kotlin.KotlinLambdaMergingTest.Lambda;
+import com.android.tools.r8.kotlin.KotlinLambdaMergingTest.Verifier;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.FileUtils;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import org.junit.Test;
+
+public class KotlinxMetadataExtensionsServiceTest extends TestBase {
+
+  private void forkR8_kstyle_trivial(boolean allowAccessModification) throws Exception {
+    if  (!isRunR8Jar()) {
+      return;
+    }
+    Path working = temp.getRoot().toPath();
+    Path kotlinJar =
+        Paths.get(EXAMPLES_KOTLIN_RESOURCE_DIR, "JAVA_8", "lambdas_kstyle_trivial.jar")
+            .toAbsolutePath();
+    Path output = working.resolve("classes.dex");
+    assertFalse(Files.exists(output));
+    Path proguardConfiguration = temp.newFile("test.conf").toPath();
+    List<String> lines = ImmutableList.of(
+        "-keepattributes Signature,InnerClasses,EnclosingMethod",
+        "-keep class **MainKt {",
+        "  public static void main(...);",
+        "}",
+        "-printmapping",
+        "-dontobfuscate",
+        allowAccessModification ? "-allowaccessmodification" : ""
+    );
+    FileUtils.writeTextFile(proguardConfiguration, lines);
+    ProcessResult result = ToolHelper.forkR8Jar(working,
+        "--pg-conf", proguardConfiguration.toString(),
+        "--lib", ToolHelper.getAndroidJar(AndroidApiLevel.O).toAbsolutePath().toString(),
+        kotlinJar.toString());
+    assertEquals(0, result.exitCode);
+    assertThat(result.stderr, not(containsString(
+        "No MetadataExtensions instances found in the classpath")));
+    assertTrue(Files.exists(output));
+
+    DexInspector inspector = new DexInspector(output);
+    Verifier verifier = new Verifier(inspector);
+    String pkg = "lambdas_kstyle_trivial";
+    verifier.assertLambdaGroups(
+        allowAccessModification ?
+            new Group[]{
+                kstyle("", 0, 4),
+                kstyle("", 1, 8),
+                kstyle("", 2, 2), // -\
+                kstyle("", 2, 5), // - 3 groups different by main method
+                kstyle("", 2, 4), // -/
+                kstyle("", 3, 2),
+                kstyle("", 22, 2)} :
+            new Group[]{
+                kstyle(pkg, 0, 2),
+                kstyle(pkg, 1, 4),
+                kstyle(pkg, 2, 5), // - 2 groups different by main method
+                kstyle(pkg, 2, 4), // -/
+                kstyle(pkg, 3, 2),
+                kstyle(pkg, 22, 2),
+                kstyle(pkg + "/inner", 0, 2),
+                kstyle(pkg + "/inner", 1, 4)}
+    );
+
+    verifier.assertLambdas(
+        allowAccessModification ?
+            new Lambda[]{
+                new Lambda(pkg, "MainKt$testStateless$6", 1) /* Banned for limited inlining */} :
+            new Lambda[]{
+                new Lambda(pkg, "MainKt$testStateless$6", 1), /* Banned for limited inlining */
+                new Lambda(pkg, "MainKt$testStateless$8", 2),
+                new Lambda(pkg + "/inner", "InnerKt$testInnerStateless$7", 2)}
+    );
+  }
+
+  @Test
+  public void testTrivialKs_allowAccessModification() throws Exception {
+    forkR8_kstyle_trivial(true);
+  }
+
+  @Test
+  public void testTrivialKs_notAllowAccessModification() throws Exception {
+    forkR8_kstyle_trivial(false);
+  }
+
+}
diff --git a/src/test/java/com/android/tools/r8/naming/b72391662/B72391662.java b/src/test/java/com/android/tools/r8/naming/b72391662/B72391662.java
index 9a9ca34..ec27895 100644
--- a/src/test/java/com/android/tools/r8/naming/b72391662/B72391662.java
+++ b/src/test/java/com/android/tools/r8/naming/b72391662/B72391662.java
@@ -21,8 +21,8 @@
 import com.android.tools.r8.utils.DexInspector.ClassSubject;
 import com.android.tools.r8.utils.DexInspector.MethodSubject;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
 import java.util.List;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -46,7 +46,7 @@
         allowAccessModification ? "-allowaccessmodification" : "",
         !minify ? "-dontobfuscate" : "",
         "-keep class " + mainClass.getCanonicalName() + " {",
-        "  public void main(java.lang.String[]);",
+        "  public static void main(java.lang.String[]);",
         "}",
         keep + " class " + TestClass.class.getCanonicalName() + " {",
         "  *;",
@@ -97,7 +97,6 @@
     doTest_keepAll(Shrinker.R8, null, false, false);
   }
 
-  @Ignore("b/92236970")
   @Test
   @IgnoreIfVmOlderThan(Version.V7_0_0)
   public void test_keepAll_R8Compat() throws Exception {
@@ -137,7 +136,7 @@
         allowAccessModification ? "-allowaccessmodification" : "",
         !minify ? "-dontobfuscate" : "",
         "-keep class " + mainClass.getCanonicalName() + " {",
-        "  public void main(java.lang.String[]);",
+        "  public static void main(java.lang.String[]);",
         "}",
         keep + " class " + TestClass.class.getCanonicalName() + " {",
         "  !public <methods>;",
@@ -188,7 +187,6 @@
     doTest_keepNonPublic(Shrinker.R8, null, false, false);
   }
 
-  @Ignore("b/92236970")
   @Test
   @IgnoreIfVmOlderThan(Version.V7_0_0)
   public void test_keepNonPublic_R8Compat() throws Exception {
@@ -222,13 +220,13 @@
       boolean minify) throws Exception {
     Class mainClass = TestMain.class;
     String keep = !minify ? "-keep" : "-keep,allowobfuscation";
-    List<String> config = ImmutableList.of(
+    Iterable<String> config = ImmutableList.of(
         "-printmapping",
         repackagePrefix != null ? "-repackageclasses '" + repackagePrefix + "'" : "",
         allowAccessModification ? "-allowaccessmodification" : "",
         !minify ? "-dontobfuscate" : "",
         "-keep class " + mainClass.getCanonicalName() + " {",
-        "  public void main(java.lang.String[]);",
+        "  public static void main(java.lang.String[]);",
         "}",
         keep + " class " + TestClass.class.getCanonicalName() + " {",
         "  public <methods>;",
@@ -238,6 +236,13 @@
         "}",
         "-dontwarn java.lang.invoke.*"
     );
+    if (isR8(shrinker)) {
+      config = Iterables.concat(config, ImmutableList.of(
+          "-neverinline class " + TestClass.class.getCanonicalName() + " {",
+          "  * staticMethod();",
+          "}"
+      ));
+    }
 
     AndroidApp app = runShrinkerRaw(shrinker, CLASSES, config);
     assertEquals("123451234567\nABC\n", runOnArt(app, mainClass.getCanonicalName()));
@@ -253,15 +258,11 @@
 
     // Test an indirectly referred method.
     staticMethod = testClass.method("java.lang.String", "staticMethod", ImmutableList.of());
-    if (isR8(shrinker)) {
-      // Inlined.
-      assertThat(staticMethod, not(isPresent()));
-    } else {
-      assertThat(staticMethod, isPresent());
-      assertEquals(minify, staticMethod.isRenamed());
-      boolean publicizeCondition = minify && repackagePrefix != null && allowAccessModification;
-      assertEquals(publicizeCondition, staticMethod.getMethod().accessFlags.isPublic());
-    }
+    assertThat(staticMethod, isPresent());
+    assertEquals(minify, staticMethod.isRenamed());
+    boolean publicizeCondition = isR8(shrinker) ? allowAccessModification
+        : minify && repackagePrefix != null && allowAccessModification;
+    assertEquals(publicizeCondition, staticMethod.getMethod().accessFlags.isPublic());
   }
 
   @Test
@@ -277,7 +278,6 @@
     doTest_keepPublic(Shrinker.R8, null, false, false);
   }
 
-  @Ignore("b/92236970")
   @Test
   @IgnoreIfVmOlderThan(Version.V7_0_0)
   public void test_keepPublic_R8Compat() throws Exception {
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);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ProguardCompatabilityTestBase.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ProguardCompatabilityTestBase.java
index 693f02d..e3bffc6 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ProguardCompatabilityTestBase.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ProguardCompatabilityTestBase.java
@@ -44,7 +44,7 @@
   }
 
   protected AndroidApp runShrinkerRaw(
-      Shrinker mode, List<Class> programClasses, List<String> proguadConfigs) throws Exception {
+      Shrinker mode, List<Class> programClasses, Iterable<String> proguadConfigs) throws Exception {
     return runShrinkerRaw(
         mode, programClasses, String.join(System.lineSeparator(), proguadConfigs));
   }
@@ -111,10 +111,11 @@
   protected AndroidApp runR8CompatRaw(
       List<Class> programClasses, String proguardConfig) throws Exception {
     CompatProguardCommandBuilder builder = new CompatProguardCommandBuilder(true);
+    ToolHelper.allowTestProguardOptions(builder);
     builder.addProguardConfiguration(ImmutableList.of(proguardConfig), Origin.unknown());
     programClasses.forEach(
         clazz -> builder.addProgramFiles(ToolHelper.getClassFileForTestClass(clazz)));
-    builder.addLibraryFiles(ToolHelper.getDefaultAndroidJar());
+    builder.addLibraryFiles(ToolHelper.getAndroidJar(ToolHelper.getMinApiLevelForDexVm()));
     builder.setProgramConsumer(DexIndexedConsumer.emptyConsumer());
     return ToolHelper.runR8(builder.build());
   }
diff --git a/src/test/kotlinR8TestResources/class_inliner_data_class/main.kt b/src/test/kotlinR8TestResources/class_inliner_data_class/main.kt
new file mode 100644
index 0000000..c8ef79a
--- /dev/null
+++ b/src/test/kotlinR8TestResources/class_inliner_data_class/main.kt
@@ -0,0 +1,21 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package class_inliner_data_class
+
+fun main(args: Array<String>) {
+    val alpha = Alpha("", "m", "")
+    alpha.right = "l"
+    alpha.left = "r"
+    alpha.rotate()
+    println("result: ${alpha.toString()}")
+}
+
+data class Alpha(var left: String, val middle: String, var right: String) {
+    fun rotate() {
+        val t = left
+        left = right
+        right = t
+    }
+}