Merge commit '369a21ca5fd3338017bc72e1814a67dc7d34eed9' into dev-release
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index a34b8ac..d8fc991 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -119,6 +119,12 @@
       return super.addClasspathResourceProvider(provider);
     }
 
+    /** Set input proguard map used for distribution of classes in multi-dex. */
+    public Builder setProguardInputMapFile(Path proguardInputMap) {
+      getAppBuilder().setProguardMapInputData(proguardInputMap);
+      return self();
+    }
+
     /**
      * Indicate if compilation is to intermediate results, i.e., intended for later merging.
      *
diff --git a/src/main/java/com/android/tools/r8/D8CommandParser.java b/src/main/java/com/android/tools/r8/D8CommandParser.java
index ac56a10..93d9058 100644
--- a/src/main/java/com/android/tools/r8/D8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/D8CommandParser.java
@@ -29,6 +29,7 @@
           "--output",
           "--lib",
           "--classpath",
+          "--pg-map",
           MIN_API_FLAG,
           "--main-dex-list",
           "--main-dex-list-output",
@@ -128,6 +129,7 @@
                       + "# Minimum Android API level compatibility, default: "
                       + AndroidApiLevel.getDefault().getLevel()
                       + ".",
+                  "  --pg-map <file>         # Use <file> as a mapping file for distribution.",
                   "  --intermediate          # Compile an intermediate result intended for later",
                   "                          # merging.",
                   "  --file-per-class        # Produce a separate dex file per class.",
@@ -217,6 +219,8 @@
         outputMode = OutputMode.DexFilePerClassFile;
       } else if (arg.equals("--classfile")) {
         outputMode = OutputMode.ClassFile;
+      } else if (arg.equals("--pg-map")) {
+        builder.setProguardInputMapFile(Paths.get(nextArg));
       } else if (arg.equals("--output")) {
         if (outputPath != null) {
           builder.error(
diff --git a/src/main/java/com/android/tools/r8/cf/CfPrinter.java b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
index 87c4b30..656c8f8 100644
--- a/src/main/java/com/android/tools/r8/cf/CfPrinter.java
+++ b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
@@ -52,6 +52,7 @@
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.CfCode.LocalVariableInfo;
 import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -389,10 +390,15 @@
 
   public void print(CfInvokeDynamic invoke) {
     indent();
+    DexCallSite callSite = invoke.getCallSite();
+    DexMethodHandle bootstrapMethod = callSite.bootstrapMethod;
     builder.append(opcodeName(Opcodes.INVOKEDYNAMIC)).append(' ');
-    builder.append(invoke.getCallSite().methodName);
-    builder.append(invoke.getCallSite().methodProto.toDescriptorString());
-    DexMethodHandle bootstrapMethod = invoke.getCallSite().bootstrapMethod;
+    builder.append(callSite.methodName);
+    builder.append(callSite.methodProto.toDescriptorString());
+    DexMethodHandle handle = callSite.bootstrapArgs.get(1).asDexValueMethodHandle().getValue();
+    builder.append(", handle:");
+    builder.append(handle.toSourceString());
+    builder.append(", itf: ").append(handle.isInterface);
     builder.append(", bsm:");
     appendMethod(bootstrapMethod.asMethod());
   }
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index ca70ab0..5190adb 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -89,7 +89,9 @@
 
   public final LazyLoadedDexApplication read(ExecutorService executorService) throws IOException {
     return read(
-        null, executorService, ProgramClassCollection.defaultConflictResolver(options.reporter));
+        inputApp.getProguardMapInputData(),
+        executorService,
+        ProgramClassCollection.defaultConflictResolver(options.reporter));
   }
 
   public final LazyLoadedDexApplication read(
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index 7d2dbdc..a419f4a 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -219,6 +219,10 @@
     return transaction.getNumberOfFields();
   }
 
+  public int getNumberOfClasses() {
+    return transaction.getNumberOfClasses();
+  }
+
   void throwIfFull(boolean hasMainDexList, Reporter reporter) {
     if (!isFull()) {
       return;
@@ -458,7 +462,8 @@
                 fillStrategy,
                 0,
                 writer.initClassLens,
-                writer.namingLens)
+                writer.namingLens,
+                options)
             .call();
       }
     }
@@ -523,7 +528,8 @@
                 fillStrategy,
                 fileIndexOffset,
                 writer.initClassLens,
-                writer.namingLens)
+                writer.namingLens,
+                options)
             .call();
       }
       addFeatureSplitFiles(featureSplitClasses, fillStrategy);
@@ -750,6 +756,10 @@
       return methods.size() + base.getNumberOfMethods();
     }
 
+    int getNumberOfClasses() {
+      return classes.size() + base.classes.size();
+    }
+
     int getNumberOfFields() {
       return fields.size() + base.getNumberOfFields();
     }
@@ -787,9 +797,6 @@
           && types.isEmpty() && strings.isEmpty();
     }
 
-    int getNumberOfClasses() {
-      return classes.size() + base.classes.size();
-    }
   }
 
   /**
@@ -919,6 +926,7 @@
     private final Map<DexProgramClass, String> originalNames;
     private final DexItemFactory dexItemFactory;
     private final FillStrategy fillStrategy;
+    private final InternalOptions options;
     private final VirtualFileCycler cycler;
 
     PackageSplitPopulator(
@@ -929,11 +937,13 @@
         FillStrategy fillStrategy,
         int fileIndexOffset,
         InitClassLens initClassLens,
-        NamingLens namingLens) {
+        NamingLens namingLens,
+        InternalOptions options) {
       this.classes = new ArrayList<>(classes);
       this.originalNames = originalNames;
       this.dexItemFactory = dexItemFactory;
       this.fillStrategy = fillStrategy;
+      this.options = options;
       this.cycler = new VirtualFileCycler(files, initClassLens, namingLens, fileIndexOffset);
     }
 
@@ -1005,7 +1015,7 @@
           nonPackageClasses.add(clazz);
           continue;
         }
-        if (current.isFilledEnough(fillStrategy) || current.isFull()) {
+        if (isFullEnough(current, options)) {
           current.abortTransaction();
           // We allow for a final rollback that has at most 20% of classes in it.
           // This is a somewhat random number that was empirically chosen.
@@ -1049,6 +1059,14 @@
       return newPackageAssignments;
     }
 
+    private boolean isFullEnough(VirtualFile current, InternalOptions options) {
+      if (options.testing.limitNumberOfClassesPerDex > 0
+          && current.getNumberOfClasses() > options.testing.limitNumberOfClassesPerDex) {
+        return true;
+      }
+      return current.isFilledEnough(fillStrategy) || current.isFull();
+    }
+
     private void addNonPackageClasses(
         VirtualFileCycler cycler, List<DexProgramClass> nonPackageClasses) {
       cycler.restart();
@@ -1076,9 +1094,8 @@
 
     private VirtualFile getVirtualFile(VirtualFileCycler cycler) {
       VirtualFile current = null;
-      while (cycler.hasNext()
-          && (current = cycler.next()).isFilledEnough(fillStrategy)) {}
-      if (current == null || current.isFilledEnough(fillStrategy)) {
+      while (cycler.hasNext() && isFullEnough(current = cycler.next(), options)) {}
+      if (current == null || isFullEnough(current, options)) {
         current = cycler.addFile();
       }
       return current;
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
index bb3405a..f166e3c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
@@ -189,11 +189,12 @@
         return;
       }
       startLine = position.line;
-      emittedPosition = new Position(position.line, null, method.method, null);
+      emittedPosition =
+          new Position(position.line, null, position.getOutermostCaller().method, null);
     }
     assert emittedPc != pc;
     int previousPc = emittedPc == NO_PC_INFO ? 0 : emittedPc;
-    emitAdvancementEvents(previousPc, emittedPosition, pc, position, events, factory);
+    emitAdvancementEvents(previousPc, emittedPosition, pc, position, events, factory, false);
     emittedPc = pc;
     emittedPosition = position;
     if (localsChanged()) {
@@ -225,7 +226,8 @@
       int nextPc,
       Position nextPosition,
       List<DexDebugEvent> events,
-      DexItemFactory factory) {
+      DexItemFactory factory,
+      boolean optimizingLineNumbers) {
     assert previousPc >= 0;
     int pcDelta = nextPc - previousPc;
     assert !previousPosition.isNone() || nextPosition.isNone();
@@ -235,8 +237,14 @@
     if (nextPosition.file != previousPosition.file) {
       events.add(factory.createSetFile(nextPosition.file));
     }
-    if (nextPosition.method != previousPosition.method
-        || nextPosition.callerPosition != previousPosition.callerPosition) {
+    // The LineNumberOptimizer maps new positions based on the outer most caller with
+    // callerPosition == null.
+    assert null != nextPosition.callerPosition
+        || null != previousPosition.callerPosition
+        || nextPosition.method == previousPosition.method
+        || optimizingLineNumbers;
+    if (nextPosition.callerPosition != previousPosition.callerPosition
+        || nextPosition.method != previousPosition.method) {
       events.add(factory.createSetInlineFrame(nextPosition.method, nextPosition.callerPosition));
     }
     if (lineDelta < Constants.DBG_LINE_BASE
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index b087d77..ff7e38c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -36,7 +36,6 @@
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.errors.InternalCompilerError;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.DexDebugEvent.SetInlineFrame;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Invoke;
 import com.android.tools.r8.ir.code.ValueType;
@@ -873,37 +872,7 @@
       cfCode.addFakeThisParameter(appView.dexItemFactory());
     }
   }
-
-  public static void setOriginalMethodPosition(Code code, DexMethod originalMethod) {
-    if (code.isDexCode()) {
-      DexCode dexCode = code.asDexCode();
-      DexDebugInfo debugInfo = dexCode.getDebugInfo();
-      if (debugInfo == null) {
-        return;
-      }
-      for (DexDebugEvent event : debugInfo.events) {
-        if (event.isSetInlineFrame() && event.asSetInlineFrame().hasOuterPosition(originalMethod)) {
-          return;
-        }
-      }
-      DexDebugEvent[] newEvents = new DexDebugEvent[debugInfo.events.length + 1];
-      newEvents[0] = new SetInlineFrame(originalMethod, null);
-      System.arraycopy(debugInfo.events, 0, newEvents, 1, debugInfo.events.length);
-      dexCode.setDebugInfo(new DexDebugInfo(debugInfo.startLine, debugInfo.parameters, newEvents));
-    } else {
-      assert code.isCfCode();
-      CfCode cfCode = code.asCfCode();
-      for (CfInstruction instruction : cfCode.instructions) {
-        if (instruction.isPosition()) {
-          assert instruction.asPosition().getPosition().getOutermostCaller().method
-              == originalMethod;
-          return;
-        }
-      }
-      assert false : "The original position should be present in the CF code.";
-    }
-  }
-
+  
   private DexEncodedMethod toMethodThatLogsErrorDexCode(DexItemFactory itemFactory) {
     checkIfObsolete();
     Signature signature = MethodSignature.fromDexMethod(method);
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index aa1d5a9..2cf3328 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -51,6 +51,7 @@
 import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Consumer;
@@ -1760,6 +1761,71 @@
     return canonicalize(strings, new DexString(source));
   }
 
+  public static String escapeMemberString(String str) {
+    return str.replace('.', '$');
+  }
+
+  public DexString createFreshMemberString(String baseName, DexType holder, int index) {
+    StringBuilder sb =
+        new StringBuilder()
+            .append(baseName)
+            .append('$')
+            .append(escapeMemberString(holder.toSourceString()));
+
+    if (index > 0) {
+      sb.append("$").append(index);
+    }
+
+    return createString(sb.toString());
+  }
+
+  /**
+   * Find a fresh method name that is not used by any other method. The method name takes the form
+   * "basename$holdername$index".
+   *
+   * @param tryString callback to check if the method name is in use.
+   */
+  public <T extends DexMember<?, ?>> T createFreshMember(
+      String baseName, DexType holder, Function<DexString, Optional<T>> tryString) {
+    int index = 0;
+    while (true) {
+      DexString name = createFreshMemberString(baseName, holder, index++);
+      Optional<T> result = tryString.apply(name);
+      if (result.isPresent()) {
+        return result.get();
+      }
+    }
+  }
+
+  /**
+   * Tries to find a method name for insertion into the class {@code target} of the form
+   * baseName$holder$n, where {@code baseName} and {@code holder} are supplied by the user, and
+   * {@code n} is picked to be the first number so that {@code isFresh.apply(method)} returns {@code
+   * true}.
+   *
+   * @param holder indicates where the method originates from.
+   */
+  public DexMethod createFreshMethodName(
+      String baseName,
+      DexType holder,
+      DexProto proto,
+      DexType target,
+      Predicate<DexMethod> isFresh) {
+    DexMethod method =
+        createFreshMember(
+            baseName,
+            holder,
+            name -> {
+              DexMethod tryMethod = createMethod(target, proto, name);
+              if (isFresh.test(tryMethod)) {
+                return Optional.of(tryMethod);
+              } else {
+                return Optional.empty();
+              }
+            });
+    return method;
+  }
+
   public DexString lookupString(int size, byte[] content) {
     return strings.get(new DexString(size, content));
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
index a4be032..6c37bc9 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
@@ -206,10 +206,7 @@
       MethodHandleType type,
       DexMember<? extends DexItem, ? extends DexMember<?, ?>> fieldOrMethod,
       boolean isInterface) {
-    this.type = type;
-    this.fieldOrMethod = fieldOrMethod;
-    this.isInterface = isInterface;
-    this.rewrittenTarget = null;
+    this(type, fieldOrMethod, isInterface, null);
   }
 
   public DexMethodHandle(
diff --git a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
index 08e5190..d803a42 100644
--- a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
@@ -73,12 +73,13 @@
     return classpathClasses;
   }
 
-  public DexDefinitionSupplier getDefinitionsSupplier(SyntheticItems syntheticItems) {
+  public DexDefinitionSupplier getDefinitionsSupplier(
+      SyntheticDefinitionsProvider syntheticDefinitionsProvider) {
     DirectMappedDexApplication self = this;
     return new DexDefinitionSupplier() {
       @Override
       public DexClass definitionFor(DexType type) {
-        return syntheticItems.definitionFor(type, self::definitionFor);
+        return syntheticDefinitionsProvider.definitionFor(type, self::definitionFor);
       }
 
       @Override
diff --git a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
index 2c50bf8..2828097 100644
--- a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
+++ b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
@@ -7,12 +7,18 @@
 import com.android.tools.r8.ir.code.ConstInstruction;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.ir.conversion.ExtraParameter;
+import com.android.tools.r8.ir.conversion.ExtraUnusedNullParameter;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.google.common.collect.Ordering;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceRBTreeMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
 import it.unimi.dsi.fastutil.ints.IntBidirectionalIterator;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
 import java.util.function.Consumer;
 
 public class RewrittenPrototypeDescription {
@@ -325,20 +331,20 @@
 
   private static final RewrittenPrototypeDescription none = new RewrittenPrototypeDescription();
 
-  private final int extraUnusedNullParameters;
+  private final List<ExtraParameter> extraParameters;
   private final ArgumentInfoCollection argumentInfoCollection;
   private final RewrittenTypeInfo rewrittenReturnInfo;
 
   private RewrittenPrototypeDescription() {
-    this(0, null, ArgumentInfoCollection.empty());
+    this(Collections.emptyList(), null, ArgumentInfoCollection.empty());
   }
 
   private RewrittenPrototypeDescription(
-      int extraUnusedNullParameters,
+      List<ExtraParameter> extraParameters,
       RewrittenTypeInfo rewrittenReturnInfo,
       ArgumentInfoCollection argumentsInfo) {
     assert argumentsInfo != null;
-    this.extraUnusedNullParameters = extraUnusedNullParameters;
+    this.extraParameters = extraParameters;
     this.rewrittenReturnInfo = rewrittenReturnInfo;
     this.argumentInfoCollection = argumentsInfo;
   }
@@ -350,12 +356,14 @@
     DexType returnType = method.proto.returnType;
     RewrittenTypeInfo returnInfo =
         returnType.isAlwaysNull(appView) ? RewrittenTypeInfo.toVoid(returnType, appView) : null;
-    return new RewrittenPrototypeDescription(0, returnInfo, removedArgumentsInfo);
+    return new RewrittenPrototypeDescription(
+        Collections.emptyList(), returnInfo, removedArgumentsInfo);
   }
 
   public static RewrittenPrototypeDescription createForRewrittenTypes(
       RewrittenTypeInfo returnInfo, ArgumentInfoCollection rewrittenArgumentsInfo) {
-    return new RewrittenPrototypeDescription(0, returnInfo, rewrittenArgumentsInfo);
+    return new RewrittenPrototypeDescription(
+        Collections.emptyList(), returnInfo, rewrittenArgumentsInfo);
   }
 
   public static RewrittenPrototypeDescription none() {
@@ -363,13 +371,17 @@
   }
 
   public boolean isEmpty() {
-    return extraUnusedNullParameters == 0
+    return extraParameters.isEmpty()
         && rewrittenReturnInfo == null
         && argumentInfoCollection.isEmpty();
   }
 
-  public int numberOfExtraUnusedNullParameters() {
-    return extraUnusedNullParameters;
+  public Collection<ExtraParameter> getExtraParameters() {
+    return extraParameters;
+  }
+
+  public int numberOfExtraParameters() {
+    return extraParameters.size();
   }
 
   public boolean hasBeenChangedToReturnVoid(AppView<?> appView) {
@@ -420,7 +432,7 @@
     assert rewrittenReturnInfo == null;
     return !hasBeenChangedToReturnVoid(appView)
         ? new RewrittenPrototypeDescription(
-            extraUnusedNullParameters,
+            extraParameters,
             RewrittenTypeInfo.toVoid(oldReturnType, appView),
             argumentInfoCollection)
         : this;
@@ -428,7 +440,7 @@
 
   public RewrittenPrototypeDescription withRemovedArguments(ArgumentInfoCollection other) {
     return new RewrittenPrototypeDescription(
-        extraUnusedNullParameters, rewrittenReturnInfo, argumentInfoCollection.combine(other));
+        extraParameters, rewrittenReturnInfo, argumentInfoCollection.combine(other));
   }
 
   public RewrittenPrototypeDescription withExtraUnusedNullParameter() {
@@ -437,12 +449,23 @@
 
   public RewrittenPrototypeDescription withExtraUnusedNullParameters(
       int numberOfExtraUnusedNullParameters) {
-    if (numberOfExtraUnusedNullParameters == 0) {
+    List<ExtraParameter> parameters =
+        Collections.nCopies(numberOfExtraUnusedNullParameters, new ExtraUnusedNullParameter());
+    return withExtraParameters(parameters);
+  }
+
+  public RewrittenPrototypeDescription withExtraParameter(ExtraParameter parameter) {
+    return withExtraParameters(Collections.singletonList(parameter));
+  }
+
+  public RewrittenPrototypeDescription withExtraParameters(List<ExtraParameter> parameters) {
+    if (parameters.isEmpty()) {
       return this;
     }
+    List<ExtraParameter> newExtraParameters = new ArrayList<>();
+    newExtraParameters.addAll(extraParameters);
+    newExtraParameters.addAll(parameters);
     return new RewrittenPrototypeDescription(
-        extraUnusedNullParameters + numberOfExtraUnusedNullParameters,
-        rewrittenReturnInfo,
-        argumentInfoCollection);
+        newExtraParameters, rewrittenReturnInfo, argumentInfoCollection);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/SyntheticDefinitionsProvider.java b/src/main/java/com/android/tools/r8/graph/SyntheticDefinitionsProvider.java
new file mode 100644
index 0000000..82e37ab
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/SyntheticDefinitionsProvider.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import java.util.function.Function;
+
+public interface SyntheticDefinitionsProvider {
+  DexClass definitionFor(DexType type, Function<DexType, DexClass> baseDefinitionFor);
+}
diff --git a/src/main/java/com/android/tools/r8/graph/SyntheticItems.java b/src/main/java/com/android/tools/r8/graph/SyntheticItems.java
index 20c073a..3df048f 100644
--- a/src/main/java/com/android/tools/r8/graph/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/graph/SyntheticItems.java
@@ -11,9 +11,9 @@
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Function;
 
-public class SyntheticItems {
+public class SyntheticItems implements SyntheticDefinitionsProvider {
 
-  public static class CommittedItems {
+  public static class CommittedItems implements SyntheticDefinitionsProvider {
     // Set of all types that represent synthesized items.
     private final ImmutableSet<DexType> syntheticTypes;
 
@@ -24,6 +24,11 @@
     SyntheticItems toSyntheticItems() {
       return new SyntheticItems(syntheticTypes);
     }
+
+    @Override
+    public DexClass definitionFor(DexType type, Function<DexType, DexClass> baseDefinitionFor) {
+      return baseDefinitionFor.apply(type);
+    }
   }
 
   // Thread safe collection of synthesized classes that are not yet committed to the application.
@@ -48,6 +53,7 @@
     return Collections.unmodifiableCollection(pendingClasses.values());
   }
 
+  @Override
   public DexClass definitionFor(DexType type, Function<DexType, DexClass> baseDefinitionFor) {
     DexProgramClass pending = pendingClasses.get(type);
     if (pending != null) {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorEntryPoint.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorEntryPoint.java
new file mode 100644
index 0000000..b182afb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorEntryPoint.java
@@ -0,0 +1,89 @@
+// Copyright (c) 2020, 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.horizontalclassmerging;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.ir.synthetic.SyntheticSourceCode;
+import com.android.tools.r8.utils.IntBox;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Generate code of the form: <code>
+ *   MyClass(int constructorId, [args]) {
+ *     switch (constructorId) {
+ *       case 0:
+ *         this.Constructor$A([args]);
+ *         return;
+ *       case 1:
+ *         this.Constructor$B([args]);
+ *         return;
+ *       ...
+ *       default:
+ *         throw null;
+ *     }
+ *   }
+ * </code>
+ */
+public class ConstructorEntryPoint extends SyntheticSourceCode {
+  private final Collection<DexMethod> typeConstructors;
+
+  public ConstructorEntryPoint(
+      Collection<DexMethod> typeConstructors, DexMethod method, Position callerPosition) {
+    super(method.holder, method, callerPosition);
+
+    this.typeConstructors = typeConstructors;
+  }
+
+  @Override
+  protected void prepareInstructions() {
+    int typeConstructorCount = typeConstructors.size();
+    int idRegister = getParamRegister(0);
+
+    int[] keys = new int[typeConstructorCount];
+    int[] offsets = new int[typeConstructorCount];
+    IntBox fallthrough = new IntBox();
+    int switchIndex = lastInstructionIndex();
+    add(
+        builder -> builder.addSwitch(idRegister, keys, fallthrough.get(), offsets),
+        builder -> endsSwitch(builder, switchIndex, fallthrough.get(), offsets));
+
+    fallthrough.set(nextInstructionIndex());
+    int nullRegister = nextRegister(ValueType.OBJECT);
+    add(builder -> builder.addNullConst(nullRegister));
+    add(builder -> builder.addThrow(nullRegister), endsBlock);
+
+    int index = 0;
+    for (DexMethod typeConstructor : typeConstructors) {
+      keys[index] = index;
+      offsets[index] = nextInstructionIndex();
+
+      add(
+          builder -> {
+            List<Value> arguments = new ArrayList<>(typeConstructor.getArity());
+            arguments.add(builder.getReceiverValue());
+            int paramIndex = 0;
+            for (Value argument : builder.getArgumentValues()) {
+              if (paramIndex++ >= typeConstructor.getArity()) {
+                break;
+              }
+              arguments.add(argument);
+            }
+            builder.addInvoke(
+                Type.DIRECT, typeConstructor, typeConstructor.proto, arguments, false);
+          });
+
+      add(IRBuilder::addReturn, endsBlock);
+
+      index++;
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
new file mode 100644
index 0000000..d7e942d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
@@ -0,0 +1,104 @@
+// Copyright (c) 2020, 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.horizontalclassmerging;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+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.graph.ParameterAnnotationsList;
+import com.android.tools.r8.ir.synthetic.SynthesizedCode;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ConstructorMerger {
+  private final AppView<?> appView;
+  private final DexProgramClass target;
+  private final Collection<DexEncodedMethod> constructors;
+  private final DexItemFactory dexItemFactory;
+
+  ConstructorMerger(
+      AppView<?> appView, DexProgramClass target, Collection<DexEncodedMethod> constructors) {
+    this.appView = appView;
+    this.target = target;
+    this.constructors = constructors;
+
+    // Constructors should not be empty and all constructors should have the same prototype.
+    assert !constructors.isEmpty();
+    assert constructors.stream().map(constructor -> constructor.proto()).distinct().count() == 1;
+
+    this.dexItemFactory = appView.dexItemFactory();
+  }
+
+  private DexMethod moveConstructor(DexEncodedMethod constructor) {
+    DexMethod method =
+        dexItemFactory.createFreshMethodName(
+            "constructor",
+            constructor.holder(),
+            constructor.proto(),
+            target.type,
+            tryMethod -> target.lookupMethod(tryMethod) == null);
+
+    if (constructor.holder() == target.type) {
+      target.removeMethod(constructor.toReference());
+    }
+
+    target.addDirectMethod(constructor.toTypeSubstitutedMethod(method));
+    return method;
+  }
+
+  private DexProto getNewConstructorProto() {
+    DexEncodedMethod firstConstructor = constructors.stream().findFirst().get();
+    DexProto oldProto = firstConstructor.getProto();
+
+    List<DexType> parameters = new ArrayList<>();
+    Collections.addAll(parameters, oldProto.parameters.values);
+    parameters.add(dexItemFactory.intType);
+    // TODO(b/165783587): add synthesised class to prevent constructor merge conflict
+    return dexItemFactory.createProto(oldProto.returnType, parameters);
+  }
+
+  private MethodAccessFlags getAccessFlags() {
+    // TODO(b/164998929): ensure this behaviour is correct, should probably calculate upper bound
+    return MethodAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC, true);
+  }
+
+  public void merge() {
+    Map<DexType, DexMethod> typeConstructors = new IdentityHashMap<>();
+
+    for (DexEncodedMethod constructor : constructors) {
+      typeConstructors.put(constructor.holder(), moveConstructor(constructor));
+    }
+
+    DexProto newProto = getNewConstructorProto();
+
+    DexMethod newConstructor =
+        appView.dexItemFactory().createMethod(target.type, newProto, dexItemFactory.initMethodName);
+    SynthesizedCode synthesizedCode =
+        new SynthesizedCode(
+            callerPosition ->
+                new ConstructorEntryPoint(
+                    typeConstructors.values(), newConstructor, callerPosition));
+    DexEncodedMethod newMethod =
+        new DexEncodedMethod(
+            newConstructor,
+            getAccessFlags(),
+            DexAnnotationSet.empty(),
+            ParameterAnnotationsList.empty(),
+            synthesizedCode);
+
+    target.addDirectMethod(newMethod);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValueFactory.java b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValueFactory.java
index 6583c9b..d242202 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValueFactory.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValueFactory.java
@@ -31,6 +31,10 @@
     return singleNumberValues.computeIfAbsent(value, SingleNumberValue::new);
   }
 
+  public SingleNumberValue createNullValue() {
+    return createSingleNumberValue(0);
+  }
+
   public SingleStringValue createSingleStringValue(DexString string) {
     return singleStringValues.computeIfAbsent(string, SingleStringValue::new);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
index 68447db..e3fa34e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
@@ -371,8 +371,10 @@
               }
             });
       } else {
-        // We replaced a dead, non-throwing instruction by a throwing instruction. Since this is
-        // dead code, we don't need to worry about the catch handlers of the `throwBlock`.
+        // This is dead code and does not need to be guarded by catch handlers, except in the case
+        // of locking, which should be statically verifiable. Currently we copy over the handlers of
+        // the block in any case which is sound in all cases.
+        throwBlock.copyCatchHandlers(code, blockIterator, block, appView.options());
       }
     }
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index b287f84..1334a46 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -86,6 +86,12 @@
     this.position = position;
   }
 
+  public void forceOverwritePosition(Position position) {
+    assert position != null;
+    assert this.position != null;
+    this.position = position;
+  }
+
   public String getPositionAsString() {
     return position == null ? "???" : position.toString();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ExtraConstantIntParameter.java b/src/main/java/com/android/tools/r8/ir/conversion/ExtraConstantIntParameter.java
new file mode 100644
index 0000000..d698e7a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/ExtraConstantIntParameter.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2020, 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.conversion;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.analysis.value.SingleNumberValue;
+
+public class ExtraConstantIntParameter extends ExtraParameter {
+  long value;
+
+  public ExtraConstantIntParameter(long value) {
+    this.value = value;
+  }
+
+  @Override
+  public TypeElement getTypeElement(AppView<?> appView, DexType argType) {
+    assert argType == appView.dexItemFactory().intType;
+    return TypeElement.getInt();
+  }
+
+  @Override
+  public SingleNumberValue getValue(AppView<?> appView) {
+    return appView.abstractValueFactory().createSingleNumberValue(value);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ExtraParameter.java b/src/main/java/com/android/tools/r8/ir/conversion/ExtraParameter.java
new file mode 100644
index 0000000..93f52df
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/ExtraParameter.java
@@ -0,0 +1,16 @@
+// Copyright (c) 2020, 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.conversion;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.analysis.value.SingleNumberValue;
+
+public abstract class ExtraParameter {
+  public abstract TypeElement getTypeElement(AppView<?> appView, DexType argType);
+
+  public abstract SingleNumberValue getValue(AppView<?> appView);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ExtraUnusedNullParameter.java b/src/main/java/com/android/tools/r8/ir/conversion/ExtraUnusedNullParameter.java
new file mode 100644
index 0000000..f478661
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/ExtraUnusedNullParameter.java
@@ -0,0 +1,24 @@
+// Copyright (c) 2020, 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.conversion;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.Nullability;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.analysis.value.SingleNumberValue;
+
+public class ExtraUnusedNullParameter extends ExtraParameter {
+
+  @Override
+  public TypeElement getTypeElement(AppView<?> appView, DexType argType) {
+    return TypeElement.fromDexType(argType, Nullability.maybeNull(), appView);
+  }
+
+  @Override
+  public SingleNumberValue getValue(AppView<?> appView) {
+    return appView.abstractValueFactory().createNullValue();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index dfce72e..806917c 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -542,7 +542,7 @@
         method.method.proto.parameters.values.length
             + argumentsInfo.numberOfRemovedArguments()
             + (method.isStatic() ? 0 : 1)
-            - prototypeChanges.numberOfExtraUnusedNullParameters();
+            - prototypeChanges.numberOfExtraParameters();
 
     int usedArgumentIndex = 0;
     while (argumentIndex < originalNumberOfArguments) {
@@ -581,13 +581,17 @@
       argumentIndex++;
     }
 
-    for (int i = 0; i < prototypeChanges.numberOfExtraUnusedNullParameters(); i++) {
+    for (ExtraParameter extraParameter : prototypeChanges.getExtraParameters()) {
       DexType argType = method.method.proto.getParameter(usedArgumentIndex);
-      assert argType.isClassType();
-      TypeElement type = TypeElement.fromDexType(argType, Nullability.maybeNull(), appView);
+      TypeElement type = extraParameter.getTypeElement(appView, argType);
       register += type.requiredRegisters();
       usedArgumentIndex++;
-      addExtraUnusedNullArgument(register);
+      if (extraParameter instanceof ExtraUnusedNullParameter) {
+        assert argType.isClassType();
+        addExtraUnusedNullArgument(register);
+      } else {
+        addNonThisArgument(register, type);
+      }
     }
 
     flushArgumentInstructions();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index 3e59e47..bd8ba8d 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -59,6 +59,7 @@
 import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
 import com.android.tools.r8.ir.analysis.type.DestructivePhiTypeUpdater;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.analysis.value.SingleNumberValue;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.CatchHandlers;
 import com.android.tools.r8.ir.code.CheckCast;
@@ -83,9 +84,11 @@
 import com.android.tools.r8.ir.code.NewArrayEmpty;
 import com.android.tools.r8.ir.code.NewInstance;
 import com.android.tools.r8.ir.code.Phi;
+import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.ir.code.Return;
 import com.android.tools.r8.ir.code.StaticGet;
 import com.android.tools.r8.ir.code.StaticPut;
+import com.android.tools.r8.ir.code.TypeAndLocalInfoSupplier;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.optimize.enums.EnumUnboxer;
@@ -93,6 +96,7 @@
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.ListIterator;
 import java.util.Map;
@@ -284,14 +288,40 @@
                         ? null
                         : makeOutValue(invoke, code);
 
-                if (prototypeChanges.numberOfExtraUnusedNullParameters() > 0) {
-                  iterator.previous();
-                  Value nullInstruction =
-                      iterator.insertConstNullInstruction(code, appView.options());
-                  iterator.next();
-                  for (int i = 0; i < prototypeChanges.numberOfExtraUnusedNullParameters(); i++) {
-                    newInValues.add(nullInstruction);
-                  }
+                Map<SingleNumberValue, Map<DexType, Value>> parameterMap = new IdentityHashMap<>();
+
+                int parameterIndex = newInValues.size() - (actualInvokeType == STATIC ? 0 : 1);
+                for (ExtraParameter parameter : prototypeChanges.getExtraParameters()) {
+                  DexType type = actualTarget.proto.getParameter(parameterIndex++);
+
+                  SingleNumberValue numberValue = parameter.getValue(appView);
+
+                  // Try to find an existing constant instruction, otherwise generate a new one.
+                  Value value =
+                      parameterMap
+                          .computeIfAbsent(numberValue, ignore -> new IdentityHashMap<>())
+                          .computeIfAbsent(
+                              type,
+                              ignore -> {
+                                iterator.previous();
+                                Instruction instruction =
+                                    numberValue.createMaterializingInstruction(
+                                        appView,
+                                        code,
+                                        TypeAndLocalInfoSupplier.create(
+                                            parameter.getTypeElement(appView, type), null));
+                                assert !instruction.instructionTypeCanThrow();
+                                instruction.setPosition(
+                                    appView.options().debug
+                                        ? invoke.getPosition()
+                                        : Position.none());
+                                iterator.add(instruction);
+                                iterator.next();
+                                return instruction.outValue();
+                              });
+
+                  newInValues.add(value);
+
                   // TODO(b/164901008): Fix when the number of arguments overflows.
                   if (newInValues.size() > 255) {
                     throw new CompilationError(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index 0b653f7..5a50c02 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -4,8 +4,6 @@
 
 package com.android.tools.r8.ir.desugar;
 
-import static com.android.tools.r8.graph.DexEncodedMethod.setOriginalMethodPosition;
-
 import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.cf.code.CfInvoke;
 import com.android.tools.r8.code.Instruction;
@@ -96,12 +94,6 @@
         newFlags.promoteToStatic();
         DexEncodedMethod.setDebugInfoWithFakeThisParameter(
             code, companionMethod.getArity(), appView);
-        if (!appView.options().isDesugaredLibraryCompilation()) {
-          setOriginalMethodPosition(
-              code, appView.graphLens().getOriginalMethodSignature(virtual.method));
-        } else {
-          assert appView.graphLens().isIdentityLens();
-        }
         DexEncodedMethod implMethod =
             new DexEncodedMethod(
                 companionMethod,
@@ -142,12 +134,6 @@
             : "Static interface method " + direct.toSourceString() + " is expected to "
             + "either be public or private in " + iface.origin;
         DexMethod companionMethod = rewriter.staticAsMethodOfCompanionClass(oldMethod);
-        if (!appView.options().isDesugaredLibraryCompilation()) {
-          setOriginalMethodPosition(
-              direct.getCode(), appView.graphLens().getOriginalMethodSignature(oldMethod));
-        } else {
-          assert appView.graphLens().isIdentityLens();
-        }
         DexEncodedMethod implMethod =
             new DexEncodedMethod(
                 companionMethod,
@@ -175,12 +161,6 @@
           }
           DexEncodedMethod.setDebugInfoWithFakeThisParameter(
               code, companionMethod.getArity(), appView);
-          if (!appView.options().isDesugaredLibraryCompilation()) {
-            setOriginalMethodPosition(
-                code, appView.graphLens().getOriginalMethodSignature(oldMethod));
-          } else {
-            assert appView.graphLens().isIdentityLens();
-          }
           DexEncodedMethod implMethod =
               new DexEncodedMethod(
                   companionMethod,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index e40e0e4..605397a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -27,6 +27,7 @@
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.ResolutionResult;
 import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
 import com.android.tools.r8.ir.code.Invoke;
 import com.android.tools.r8.ir.code.Invoke.Type;
@@ -365,20 +366,22 @@
     assert implHandle != null;
     DexMethod implMethod = implHandle.asMethod();
 
-    // Lambda$ method. We must always find it.
+    // Lambda$ method. We should always find it. If not found an ICCE can be expected to be thrown.
     assert implMethod.holder == accessedFrom.getHolderType();
     assert descriptor.verifyTargetFoundInClass(accessedFrom.getHolderType());
     if (implHandle.type.isInvokeStatic()) {
-      SingleResolutionResult resolution =
-          appView
-              .appInfoForDesugaring()
-              .resolveMethod(implMethod, implHandle.isInterface)
-              .asSingleResolution();
-      assert resolution.getResolvedMethod().isStatic();
-      assert resolution.getResolvedHolder().isProgramClass();
+      ResolutionResult resolution =
+          appView.appInfoForDesugaring().resolveMethod(implMethod, implHandle.isInterface);
+      if (resolution.isFailedResolution()) {
+        return new InvalidLambdaImplTarget(
+            implMethod, Type.STATIC, appView.dexItemFactory().icceType);
+      }
+      SingleResolutionResult result = resolution.asSingleResolution();
+      assert result.getResolvedMethod().isStatic();
+      assert result.getResolvedHolder().isProgramClass();
       return new StaticLambdaImplTarget(
           new ProgramMethod(
-              resolution.getResolvedHolder().asProgramClass(), resolution.getResolvedMethod()));
+              result.getResolvedHolder().asProgramClass(), result.getResolvedMethod()));
     }
 
     assert implHandle.type.isInvokeInstance() || implHandle.type.isInvokeDirect();
@@ -640,6 +643,21 @@
     }
   }
 
+  class InvalidLambdaImplTarget extends Target {
+
+    final DexType exceptionType;
+
+    public InvalidLambdaImplTarget(DexMethod callTarget, Type invokeType, DexType exceptionType) {
+      super(callTarget, invokeType);
+      this.exceptionType = exceptionType;
+    }
+
+    @Override
+    ProgramMethod ensureAccessibility(boolean allowMethodModification) {
+      return null;
+    }
+  }
+
   // Used for instance private lambda$ methods which need to be converted to public methods.
   private class InstanceLambdaImplTarget extends Target {
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
index a5cf917..0d996d5 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
@@ -15,6 +15,8 @@
 import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.ir.desugar.LambdaClass.InvalidLambdaImplTarget;
+import com.android.tools.r8.ir.synthetic.ExceptionThrowingSourceCode;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import java.util.ArrayList;
@@ -166,6 +168,15 @@
     DexType enforcedReturnType = descriptor().enforcedProto.returnType;
 
     LambdaClass.Target target = lambda.target;
+    if (target instanceof InvalidLambdaImplTarget) {
+      add(
+          builder -> {
+            InvalidLambdaImplTarget invalidTarget = (InvalidLambdaImplTarget) target;
+            ExceptionThrowingSourceCode.build(builder, invalidTarget.exceptionType);
+          });
+      return;
+    }
+
     DexMethod methodToCall = target.callTarget;
 
     // Only constructor call should use direct invoke type since super
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLens.java b/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLens.java
index 2669724..e7c0b77 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLens.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLens.java
@@ -22,6 +22,7 @@
   private final DexType nestConstructorType;
   private final Map<DexField, DexMethod> getFieldMap;
   private final Map<DexField, DexMethod> putFieldMap;
+  private final AppView<?> appView;
 
   NestedPrivateMethodLens(
       AppView<?> appView,
@@ -42,6 +43,7 @@
     assert methodMap instanceof IdentityHashMap;
     assert getFieldMap instanceof IdentityHashMap;
     assert putFieldMap instanceof IdentityHashMap;
+    this.appView = appView;
     this.nestConstructorType = nestConstructorType;
     this.getFieldMap = getFieldMap;
     this.putFieldMap = putFieldMap;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/peepholes/BasicBlockMuncher.java b/src/main/java/com/android/tools/r8/ir/optimize/peepholes/BasicBlockMuncher.java
index 8eaf454..fc48072 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/peepholes/BasicBlockMuncher.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/peepholes/BasicBlockMuncher.java
@@ -19,7 +19,8 @@
 public class BasicBlockMuncher {
 
   private static List<BasicBlockPeephole> nonDestructivePeepholes() {
-    return ImmutableList.of(new MoveLoadUpPeephole(), new StoreLoadPeephole());
+    return ImmutableList.of(
+        new RemoveDebugPositionPeephole(), new MoveLoadUpPeephole(), new StoreLoadPeephole());
   }
 
   // The StoreLoadPeephole and StoreSequenceLoadPeephole are non-destructive but we would like it
@@ -48,13 +49,7 @@
           new LinearFlowInstructionListIterator(
               code, currentBlock, currentBlock.getInstructions().size());
       boolean matched = false;
-      while (matched || it.hasPrevious()) {
-        if (!it.hasPrevious()) {
-          matched = false;
-          it =
-              new LinearFlowInstructionListIterator(
-                  code, currentBlock, currentBlock.getInstructions().size());
-        }
+      while (true) {
         for (BasicBlockPeephole peepHole : peepholes) {
           boolean localMatch = peepHole.match(it);
           if (localMatch && peepHole.resetAfterMatch()) {
@@ -73,6 +68,13 @@
             iterations++;
           }
           it.previous();
+        } else if (matched) {
+          matched = false;
+          it =
+              new LinearFlowInstructionListIterator(
+                  code, currentBlock, currentBlock.getInstructions().size());
+        } else {
+          break;
         }
       }
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/peepholes/PeepholeHelper.java b/src/main/java/com/android/tools/r8/ir/optimize/peepholes/PeepholeHelper.java
index d656455..5fef766 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/peepholes/PeepholeHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/peepholes/PeepholeHelper.java
@@ -20,10 +20,12 @@
             && (t.outValue() == null || !t.outValue().hasLocalInfo());
   }
 
-  public static void resetNext(InstructionListIterator it, int count) {
+  public static Instruction resetNext(InstructionListIterator it, int count) {
+    Instruction instruction = null;
     for (int i = 0; i < count; i++) {
-      it.previous();
+      instruction = it.previous();
     }
+    return instruction;
   }
 
   public static void resetPrevious(InstructionListIterator it, int count) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/peepholes/RemoveDebugPositionPeephole.java b/src/main/java/com/android/tools/r8/ir/optimize/peepholes/RemoveDebugPositionPeephole.java
new file mode 100644
index 0000000..99128d0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/peepholes/RemoveDebugPositionPeephole.java
@@ -0,0 +1,94 @@
+// 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.optimize.peepholes;
+
+import com.android.tools.r8.ir.code.DebugPosition;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.Position;
+
+/**
+ * {@link RemoveDebugPositionPeephole} looks for the following two patterns:
+ *
+ * <pre>
+ *   p: DebugPosition
+ *   [q: Const]*
+ *   q: Instr // TODO(b/166074600): This must currently be a Const.
+ * </pre>
+ *
+ * if p = q:
+ *
+ * <pre>
+ *   [q: Const]*
+ *   q: Instr
+ * </pre>
+ *
+ * if p != q and size([q: Const]*) > 0:
+ *
+ * <pre>
+ *   [p: Const]*
+ *   q: Instr
+ * </pre>
+ *
+ * This rewrite will eliminate debug positions that can be placed on a constant, retaining the line
+ * but avoiding the nop resulting in a remaining position instruction.
+ */
+public class RemoveDebugPositionPeephole implements BasicBlockPeephole {
+
+  private final Point debugPositionExp = new Point(Instruction::isDebugPosition);
+  private final Point secondInstructionExp =
+      new Point(
+          // TODO(b/166074600): It should be possible to match on any materializing instruction
+          //  here. The phi-optimization seems to invalidate that by changing stack operations.
+          Instruction::isConstInstruction);
+
+  private final PeepholeLayout layout =
+      PeepholeLayout.lookForward(debugPositionExp, secondInstructionExp);
+
+  @Override
+  public boolean match(InstructionListIterator it) {
+    Match match = layout.test(it);
+    if (match == null) {
+      return false;
+    }
+    DebugPosition debugPosition = debugPositionExp.get(match).asDebugPosition();
+    Instruction secondInstruction = secondInstructionExp.get(match);
+
+    // If the position is the same on the following instruction it can simply be removed.
+    Position position = debugPosition.getPosition();
+    if (position.equals(secondInstruction.getPosition())) {
+      it.removeOrReplaceByDebugLocalRead();
+      return true;
+    }
+
+    boolean movedPosition = false;
+    it.next(); // skip debug position.
+    Instruction current = it.next(); // start loop at second instruction.
+    assert current == secondInstruction;
+    while (current.isConstInstruction() && it.hasNext()) {
+      Instruction next = it.next();
+      if (!next.getPosition().equals(current.getPosition())) {
+        break;
+      }
+      // The constant shares position with the next instruction so it subsumes the position of
+      // the debug position.
+      movedPosition = true;
+      current.forceOverwritePosition(position);
+      current = next;
+    }
+    it.previousUntil(i -> i == debugPosition);
+    if (movedPosition) {
+      it.next();
+      it.removeOrReplaceByDebugLocalRead();
+      return true;
+    }
+    return false;
+  }
+
+  @Override
+  public boolean resetAfterMatch() {
+    return false;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/ExceptionThrowingSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/ExceptionThrowingSourceCode.java
index bec9df8..080b519 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/ExceptionThrowingSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/ExceptionThrowingSourceCode.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
 import java.util.Collections;
 
 // Source code representing simple forwarding method.
@@ -27,21 +28,22 @@
 
   @Override
   protected void prepareInstructions() {
-    add(
-        builder -> {
-          DexItemFactory factory = builder.appView.dexItemFactory();
-          DexProto initProto = factory.createProto(factory.voidType);
-          DexMethod initMethod =
-              factory.createMethod(exceptionType, initProto, factory.constructorMethodName);
-          builder.addNewInstance(register, exceptionType);
-          builder.addInvoke(
-              Type.DIRECT,
-              initMethod,
-              initMethod.proto,
-              Collections.singletonList(ValueType.OBJECT),
-              Collections.singletonList(register),
-              false /* isInterface */);
-          builder.addThrow(register);
-        });
+    add(builder -> build(builder, exceptionType));
+  }
+
+  public static void build(IRBuilder builder, DexType exceptionType) {
+    DexItemFactory factory = builder.appView.dexItemFactory();
+    DexProto initProto = factory.createProto(factory.voidType);
+    DexMethod initMethod =
+        factory.createMethod(exceptionType, initProto, factory.constructorMethodName);
+    builder.addNewInstance(register, exceptionType);
+    builder.addInvoke(
+        Type.DIRECT,
+        initMethod,
+        initMethod.proto,
+        Collections.singletonList(ValueType.OBJECT),
+        Collections.singletonList(register),
+        false /* isInterface */);
+    builder.addThrow(register);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
index 2d63c1e..48f0d33 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -73,8 +73,10 @@
     this.keepInnerClassStructure = options.keepInnerClassStructure();
 
     // Initialize top-level naming state.
-    topLevelState = new Namespace(
-        getPackageBinaryNameFromJavaType(options.getProguardConfiguration().getPackagePrefix()));
+    topLevelState =
+        new Namespace(
+            getPackageBinaryNameFromJavaType(
+                options.getProguardConfiguration().getPackagePrefix()));
 
     states.put("", topLevelState);
 
@@ -212,9 +214,7 @@
     }
   }
 
-  /**
-   * Registers the given package prefix and all of parent packages as used.
-   */
+  /** Registers the given package prefix and all of parent packages as used. */
   private void registerPackagePrefixesAsUsed(String packagePrefix) {
     // If -allowaccessmodification is not set, we may keep classes in their original packages,
     // accounting for package-private accesses.
diff --git a/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
index d133c3f..7773239 100644
--- a/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
+++ b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
@@ -11,7 +11,6 @@
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItem;
 import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.InnerClassAttribute;
@@ -26,7 +25,6 @@
 import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
 
@@ -131,22 +129,6 @@
   }
 
   @Override
-  public boolean verifyNoOverlap(Map<DexType, DexString> map) {
-    for (DexType alreadyRenamedInOtherLens : map.keySet()) {
-      assert !renaming.containsKey(alreadyRenamedInOtherLens);
-      assert !renaming.containsKey(
-          appView.dexItemFactory().createType(map.get(alreadyRenamedInOtherLens)));
-    }
-    return true;
-  }
-
-  @Override
-  public void forAllRenamedTypes(Consumer<DexType> consumer) {
-    DexReference.filterDexType(DexReference.filterDexReference(renaming.keySet().stream()))
-        .forEach(consumer);
-  }
-
-  @Override
   public <T extends DexItem> Map<String, T> getRenamedItems(
       Class<T> clazz, Predicate<T> predicate, Function<T, String> namer) {
     return renaming.keySet().stream()
diff --git a/src/main/java/com/android/tools/r8/naming/NamingLens.java b/src/main/java/com/android/tools/r8/naming/NamingLens.java
index 7aab96a..3934b0f 100644
--- a/src/main/java/com/android/tools/r8/naming/NamingLens.java
+++ b/src/main/java/com/android/tools/r8/naming/NamingLens.java
@@ -24,7 +24,6 @@
 import java.util.Arrays;
 import java.util.Map;
 import java.util.Set;
-import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
 
@@ -41,8 +40,6 @@
  */
 public abstract class NamingLens {
 
-  protected boolean isSortingBeforeWriting;
-
   public abstract String lookupPackageName(String packageName);
 
   public abstract DexString lookupDescriptor(DexType type);
@@ -101,8 +98,6 @@
     return dexItemFactory.createType(lookupDescriptor(type));
   }
 
-  public abstract boolean verifyNoOverlap(Map<DexType, DexString> map);
-
   public boolean hasPrefixRewritingLogic() {
     return false;
   }
@@ -124,8 +119,6 @@
     return DescriptorUtils.descriptorToInternalName(lookupDescriptor(type).toString());
   }
 
-  public abstract void forAllRenamedTypes(Consumer<DexType> consumer);
-
   public abstract <T extends DexItem> Map<String, T> getRenamedItems(
       Class<T> clazz, Predicate<T> predicate, Function<T, String> namer);
 
@@ -165,10 +158,6 @@
     return true;
   }
 
-  public void setIsSortingBeforeWriting(boolean isSorting) {
-    isSortingBeforeWriting = isSorting;
-  }
-
   private static class IdentityLens extends NamingLens {
 
     private IdentityLens() {
@@ -201,21 +190,11 @@
     }
 
     @Override
-    public boolean verifyNoOverlap(Map<DexType, DexString> map) {
-      return true;
-    }
-
-    @Override
     public String lookupPackageName(String packageName) {
       return packageName;
     }
 
     @Override
-    public void forAllRenamedTypes(Consumer<DexType> consumer) {
-      // Intentionally left empty.
-    }
-
-    @Override
     public <T extends DexItem> Map<String, T> getRenamedItems(
         Class<T> clazz, Predicate<T> predicate, Function<T, String> namer) {
       return ImmutableMap.of();
diff --git a/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java b/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java
index 482750f..9b51f27 100644
--- a/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java
+++ b/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.naming;
 
-import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexField;
@@ -17,7 +16,6 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
@@ -114,11 +112,6 @@
   }
 
   @Override
-  public boolean verifyNoOverlap(Map<DexType, DexString> map) {
-    throw new Unreachable("Multiple prefix rewriting lens not supported.");
-  }
-
-  @Override
   public String lookupPackageName(String packageName) {
     // Used for resource shrinking.
     // Desugared libraries do not have resources.
@@ -138,18 +131,6 @@
   }
 
   @Override
-  public void forAllRenamedTypes(Consumer<DexType> consumer) {
-    // Used for printing the applyMapping map.
-    // If compiling the program using a desugared library, nothing needs to be printed.
-    // If compiling the desugared library, the mapping needs to be printed.
-    // When debugging the program, both mapping files need to be merged.
-    if (options.isDesugaredLibraryCompilation()) {
-      appView.rewritePrefix.forAllRewrittenTypes(consumer);
-    }
-    namingLens.forAllRenamedTypes(consumer);
-  }
-
-  @Override
   public <T extends DexItem> Map<String, T> getRenamedItems(
       Class<T> clazz, Predicate<T> predicate, Function<T, String> namer) {
     Map<String, T> renamedItemsPrefixRewritting;
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
index ad27200..a29f8ef 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
@@ -579,9 +579,7 @@
 
     @Override
     public DexString lookupDescriptor(DexType type) {
-      if (!isSortingBeforeWriting) {
-        checkForUseOfNotMappedReference(type);
-      }
+      checkForUseOfNotMappedReference(type);
       return super.lookupDescriptor(type);
     }
 
diff --git a/src/main/java/com/android/tools/r8/relocator/SimplePackagesRewritingMapper.java b/src/main/java/com/android/tools/r8/relocator/SimplePackagesRewritingMapper.java
index 3ec10a2..396d837 100644
--- a/src/main/java/com/android/tools/r8/relocator/SimplePackagesRewritingMapper.java
+++ b/src/main/java/com/android/tools/r8/relocator/SimplePackagesRewritingMapper.java
@@ -22,7 +22,6 @@
 import com.google.common.collect.ImmutableMap;
 import java.util.IdentityHashMap;
 import java.util.Map;
-import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
 
@@ -160,16 +159,6 @@
     }
 
     @Override
-    public boolean verifyNoOverlap(Map<DexType, DexString> map) {
-      return true;
-    }
-
-    @Override
-    public void forAllRenamedTypes(Consumer<DexType> consumer) {
-      typeMappings.keySet().forEach(consumer);
-    }
-
-    @Override
     public <T extends DexItem> Map<String, T> getRenamedItems(
         Class<T> clazz, Predicate<T> predicate, Function<T, String> namer) {
       return typeMappings.keySet().stream()
diff --git a/src/main/java/com/android/tools/r8/retrace/Retrace.java b/src/main/java/com/android/tools/r8/retrace/Retrace.java
index 70f4a81..8e65f12 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retrace.java
@@ -155,7 +155,8 @@
                     retracer,
                     command.stackTrace,
                     command.diagnosticsHandler,
-                    command.regularExpression)
+                    command.regularExpression,
+                    command.isVerbose)
                 .retrace();
       } else {
         result =
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceClassResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceClassResult.java
index a631ddf..f984045 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceClassResult.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceClassResult.java
@@ -90,7 +90,8 @@
         new Element(
             this,
             mapper == null ? obfuscatedReference : Reference.classFromTypeName(mapper.originalName),
-            mapper));
+            mapper,
+            false));
   }
 
   @Override
@@ -103,6 +104,11 @@
     R create(Element element, T mappings, String obfuscatedName);
   }
 
+  public boolean isAmbiguous() {
+    // Currently we have no way of producing ambiguous class results.
+    return false;
+  }
+
   public static class Element {
 
     private final RetraceClassResult classResult;
@@ -112,7 +118,8 @@
     public Element(
         RetraceClassResult classResult,
         ClassReference classReference,
-        ClassNamingForNameMapper mapper) {
+        ClassNamingForNameMapper mapper,
+        boolean isAmbiguous) {
       this.classResult = classResult;
       this.classReference = classReference;
       this.mapper = mapper;
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceFieldResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceFieldResult.java
index d02913e..57b5354 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceFieldResult.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceFieldResult.java
@@ -40,7 +40,7 @@
     return memberNamings != null;
   }
 
-  private boolean isAmbiguous() {
+  public boolean isAmbiguous() {
     if (!hasRetraceResult()) {
       return false;
     }
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceRegularExpression.java b/src/main/java/com/android/tools/r8/retrace/RetraceRegularExpression.java
index 611dea6..15479f9 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceRegularExpression.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceRegularExpression.java
@@ -7,20 +7,22 @@
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.FieldReference;
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.references.TypeReference;
 import com.android.tools.r8.retrace.RetraceClassResult.Element;
-import com.android.tools.r8.retrace.RetraceRegularExpression.RetraceString.RetraceStringBuilder;
+import com.android.tools.r8.retrace.RetraceRegularExpression.ClassNameGroup.ClassNameGroupHandler;
+import com.android.tools.r8.utils.Box;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.StringUtils;
 import com.google.common.collect.Lists;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.LinkedHashSet;
 import java.util.List;
-import java.util.Set;
+import java.util.Objects;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
@@ -38,13 +40,14 @@
       new SourceFileLineNumberGroup();
   private final TypeNameGroup typeNameGroup = new TypeNameGroup();
   private final BinaryNameGroup binaryNameGroup = new BinaryNameGroup();
-  private final MethodNameGroup methodNameGroup = new MethodNameGroup();
-  private final FieldNameGroup fieldNameGroup = new FieldNameGroup();
   private final SourceFileGroup sourceFileGroup = new SourceFileGroup();
   private final LineNumberGroup lineNumberGroup = new LineNumberGroup();
   private final FieldOrReturnTypeGroup fieldOrReturnTypeGroup = new FieldOrReturnTypeGroup();
   private final MethodArgumentsGroup methodArgumentsGroup = new MethodArgumentsGroup();
 
+  private final MethodNameGroup methodNameGroup;
+  private final FieldNameGroup fieldNameGroup;
+
   private static final String CAPTURE_GROUP_PREFIX = "captureGroup";
   private static final int FIRST_CAPTURE_GROUP_INDEX = 0;
 
@@ -52,11 +55,14 @@
       RetraceApi retracer,
       List<String> stackTrace,
       DiagnosticsHandler diagnosticsHandler,
-      String regularExpression) {
+      String regularExpression,
+      boolean isVerbose) {
     this.retracer = retracer;
     this.stackTrace = stackTrace;
     this.diagnosticsHandler = diagnosticsHandler;
     this.regularExpression = regularExpression;
+    methodNameGroup = new MethodNameGroup(isVerbose);
+    fieldNameGroup = new FieldNameGroup(isVerbose);
   }
 
   public RetraceCommandLineResult retrace() {
@@ -72,40 +78,47 @@
     List<String> result = new ArrayList<>();
     for (String string : stackTrace) {
       Matcher matcher = compiledPattern.matcher(string);
-      List<RetraceString> retracedStrings =
-          Lists.newArrayList(RetraceStringBuilder.create(string).build());
-      if (matcher.matches()) {
-        for (RegularExpressionGroupHandler handler : handlers) {
-          retracedStrings = handler.handleMatch(retracedStrings, matcher, retracer);
-        }
+      if (!matcher.matches()) {
+        result.add(string);
+        continue;
+      }
+      // Iterate through handlers to set contexts. That will allow us to process all handlers from
+      // left to right.
+      RetraceStringContext initialContext = RetraceStringContext.empty();
+      for (RegularExpressionGroupHandler handler : handlers) {
+        initialContext = handler.buildInitial(initialContext, matcher, retracer);
+      }
+      final RetraceString initialRetraceString = RetraceString.start(initialContext);
+      List<RetraceString> retracedStrings = Lists.newArrayList(initialRetraceString);
+      for (RegularExpressionGroupHandler handler : handlers) {
+        retracedStrings = handler.handleMatch(string, retracedStrings, matcher, retracer);
       }
       if (retracedStrings.isEmpty()) {
         // We could not find a match. Output the identity.
         result.add(string);
-      } else {
-        boolean isAmbiguous = retracedStrings.size() > 1 && retracedStrings.get(0).isAmbiguous;
-        if (isAmbiguous) {
-          retracedStrings.sort(new RetraceLineComparator());
-        }
-        ClassReference previousContext = null;
-        for (RetraceString retracedString : retracedStrings) {
-          String finalString = retracedString.getRetracedString();
-          if (!isAmbiguous) {
-            result.add(finalString);
-            continue;
-          }
-          assert retracedString.getClassContext() != null;
-          ClassReference currentContext = retracedString.getClassContext().getClassReference();
-          if (currentContext.equals(previousContext)) {
-            int firstNonWhitespaceCharacter = StringUtils.firstNonWhitespaceCharacter(finalString);
-            finalString =
-                finalString.substring(0, firstNonWhitespaceCharacter)
-                    + "<OR> "
-                    + finalString.substring(firstNonWhitespaceCharacter);
-          }
-          previousContext = currentContext;
+      }
+      boolean isAmbiguous = retracedStrings.size() > 1 && retracedStrings.get(0).isAmbiguous();
+      if (isAmbiguous) {
+        retracedStrings.sort(new RetraceLineComparator());
+      }
+      ClassReference previousContext = null;
+      for (RetraceString retracedString : retracedStrings) {
+        String finalString = retracedString.builder.build(string);
+        if (!isAmbiguous) {
           result.add(finalString);
+          continue;
         }
+        assert retracedString.getClassContext() != null;
+        ClassReference currentContext = retracedString.getClassContext().getClassReference();
+        if (currentContext.equals(previousContext)) {
+          int firstNonWhitespaceCharacter = StringUtils.firstNonWhitespaceCharacter(finalString);
+          finalString =
+              finalString.substring(0, firstNonWhitespaceCharacter)
+                  + "<OR> "
+                  + finalString.substring(firstNonWhitespaceCharacter);
+        }
+        previousContext = currentContext;
+        result.add(finalString);
       }
     }
     return new RetraceCommandLineResult(result);
@@ -139,6 +152,7 @@
       List<RegularExpressionGroupHandler> handlers,
       int captureGroupIndex) {
     int lastCommittedIndex = 0;
+    RegularExpressionGroupHandler lastHandler = null;
     boolean seenPercentage = false;
     boolean escaped = false;
     for (int i = 0; i < regularExpression.length(); i++) {
@@ -146,7 +160,6 @@
         assert !escaped;
         final RegularExpressionGroup group = getGroupFromVariable(regularExpression.charAt(i));
         refinedRegularExpression.append(regularExpression, lastCommittedIndex, i - 1);
-        lastCommittedIndex = i + 1;
         if (group.isSynthetic()) {
           captureGroupIndex =
               registerGroups(
@@ -159,8 +172,25 @@
               .append(">")
               .append(group.subExpression())
               .append(")");
-          handlers.add(group.createHandler(captureGroupName));
+          final RegularExpressionGroupHandler handler = group.createHandler(captureGroupName);
+          // If we see a pattern as %c.%m or %C/%m, then register the groups to allow delaying
+          // writing of the class string until we have the fully qualified member.
+          if (lastHandler != null
+              && handler.isQualifiedHandler()
+              && lastHandler.isClassNameGroupHandler()
+              && lastCommittedIndex == i - 3
+              && isTypeOrBinarySeparator(regularExpression, lastCommittedIndex, i - 2)) {
+            final ClassNameGroupHandler classNameGroupHandler =
+                lastHandler.asClassNameGroupHandler();
+            final QualifiedRegularExpressionGroupHandler qualifiedHandler =
+                handler.asQualifiedHandler();
+            classNameGroupHandler.setQualifiedHandler(qualifiedHandler);
+            qualifiedHandler.setClassNameGroupHandler(classNameGroupHandler);
+          }
+          lastHandler = handler;
+          handlers.add(handler);
         }
+        lastCommittedIndex = i + 1;
         seenPercentage = false;
       } else {
         seenPercentage = !escaped && regularExpression.charAt(i) == '%';
@@ -172,6 +202,18 @@
     return captureGroupIndex;
   }
 
+  private boolean isTypeOrBinarySeparator(String regularExpression, int startIndex, int endIndex) {
+    assert endIndex < regularExpression.length();
+    if (startIndex + 1 != endIndex) {
+      return false;
+    }
+    if (regularExpression.charAt(startIndex) != '\\') {
+      return false;
+    }
+    return regularExpression.charAt(startIndex + 1) == '.'
+        || regularExpression.charAt(startIndex + 1) == '/';
+  }
+
   private RegularExpressionGroup getGroupFromVariable(char variable) {
     switch (variable) {
       case 'c':
@@ -197,223 +239,230 @@
     }
   }
 
-  static class RetraceString {
-
+  static class RetraceStringContext {
     private final Element classContext;
-    private final ClassNameGroup classNameGroup;
     private final ClassReference qualifiedContext;
+    private final String methodName;
     private final RetraceMethodResult.Element methodContext;
-    private final TypeReference typeOrReturnTypeContext;
-    private final boolean hasTypeOrReturnTypeContext;
-    private final String retracedString;
-    private final int adjustedIndex;
-    private final boolean isAmbiguous;
-    private final int lineNumber;
+    private final int minifiedLineNumber;
+    private final int originalLineNumber;
     private final String source;
+    private final boolean isAmbiguous;
 
-    private RetraceString(
+    private RetraceStringContext(
         Element classContext,
-        ClassNameGroup classNameGroup,
         ClassReference qualifiedContext,
+        String methodName,
         RetraceMethodResult.Element methodContext,
-        TypeReference typeOrReturnTypeContext,
-        boolean hasTypeOrReturnTypeContext,
-        String retracedString,
-        int adjustedIndex,
-        boolean isAmbiguous,
-        int lineNumber,
-        String source) {
+        int minifiedLineNumber,
+        int originalLineNumber,
+        String source,
+        boolean isAmbiguous) {
       this.classContext = classContext;
-      this.classNameGroup = classNameGroup;
       this.qualifiedContext = qualifiedContext;
+      this.methodName = methodName;
       this.methodContext = methodContext;
-      this.typeOrReturnTypeContext = typeOrReturnTypeContext;
-      this.hasTypeOrReturnTypeContext = hasTypeOrReturnTypeContext;
-      this.retracedString = retracedString;
-      this.adjustedIndex = adjustedIndex;
-      this.isAmbiguous = isAmbiguous;
-      this.lineNumber = lineNumber;
+      this.minifiedLineNumber = minifiedLineNumber;
+      this.originalLineNumber = originalLineNumber;
       this.source = source;
+      this.isAmbiguous = isAmbiguous;
     }
 
-    String getRetracedString() {
-      return retracedString;
+    private static RetraceStringContext empty() {
+      return new RetraceStringContext(null, null, null, null, NO_MATCH, NO_MATCH, null, false);
     }
 
-    boolean hasTypeOrReturnTypeContext() {
-      return hasTypeOrReturnTypeContext;
+    private RetraceStringContext withClassContext(
+        Element classContext, ClassReference qualifiedContext) {
+      return new RetraceStringContext(
+          classContext,
+          qualifiedContext,
+          methodName,
+          methodContext,
+          minifiedLineNumber,
+          originalLineNumber,
+          source,
+          isAmbiguous);
     }
 
-    Element getClassContext() {
-      return classContext;
+    private RetraceStringContext withMethodName(String methodName) {
+      return new RetraceStringContext(
+          classContext,
+          qualifiedContext,
+          methodName,
+          methodContext,
+          minifiedLineNumber,
+          originalLineNumber,
+          source,
+          isAmbiguous);
     }
 
-    RetraceMethodResult.Element getMethodContext() {
-      return methodContext;
+    private RetraceStringContext withMethodContext(
+        RetraceMethodResult.Element methodContext,
+        ClassReference qualifiedContext,
+        boolean isAmbiguous) {
+      return new RetraceStringContext(
+          classContext,
+          qualifiedContext,
+          methodName,
+          methodContext,
+          minifiedLineNumber,
+          originalLineNumber,
+          source,
+          isAmbiguous);
     }
 
-    TypeReference getTypeOrReturnTypeContext() {
-      return typeOrReturnTypeContext;
+    private RetraceStringContext withQualifiedContext(ClassReference qualifiedContext) {
+      return new RetraceStringContext(
+          classContext,
+          qualifiedContext,
+          methodName,
+          methodContext,
+          minifiedLineNumber,
+          originalLineNumber,
+          source,
+          isAmbiguous);
     }
 
-    public ClassReference getQualifiedContext() {
-      return qualifiedContext;
+    public RetraceStringContext withSource(String source) {
+      return new RetraceStringContext(
+          classContext,
+          qualifiedContext,
+          methodName,
+          methodContext,
+          minifiedLineNumber,
+          originalLineNumber,
+          source,
+          isAmbiguous);
     }
 
-    RetraceStringBuilder transform() {
-      return RetraceStringBuilder.create(this);
+    public RetraceStringContext withLineNumbers(int minifiedLineNumber, int originalLineNumber) {
+      return new RetraceStringContext(
+          classContext,
+          qualifiedContext,
+          methodName,
+          methodContext,
+          minifiedLineNumber,
+          originalLineNumber,
+          source,
+          isAmbiguous);
+    }
+  }
+
+  static class RetraceStringBuilder {
+
+    private final StringBuilder retracedString;
+    private int lastCommittedIndex;
+
+    private RetraceStringBuilder(String retracedString, int lastCommittedIndex) {
+      this.retracedString = new StringBuilder(retracedString);
+      this.lastCommittedIndex = lastCommittedIndex;
     }
 
-    public int getLineNumber() {
-      return lineNumber;
+    private void appendRetracedString(
+        String source, String stringToAppend, int originalFromIndex, int originalToIndex) {
+      retracedString.append(source, lastCommittedIndex, originalFromIndex);
+      retracedString.append(stringToAppend);
+      lastCommittedIndex = originalToIndex;
     }
 
-    public String getSource() {
-      return source;
+    private String build(String source) {
+      return retracedString.append(source, lastCommittedIndex, source.length()).toString();
+    }
+  }
+
+  private static class RetraceString {
+
+    private final RetraceStringBuilder builder;
+    private final RetraceStringContext context;
+
+    private RetraceString(RetraceStringBuilder builder, RetraceStringContext context) {
+      this.builder = builder;
+      this.context = context;
     }
 
-    static class RetraceStringBuilder {
+    private Element getClassContext() {
+      return context.classContext;
+    }
 
-      private Element classContext;
-      private ClassNameGroup classNameGroup;
-      private ClassReference qualifiedContext;
-      private RetraceMethodResult.Element methodContext;
-      private TypeReference typeOrReturnTypeContext;
-      private boolean hasTypeOrReturnTypeContext;
-      private String retracedString;
-      private int adjustedIndex;
-      private boolean isAmbiguous;
-      private int lineNumber;
-      private String source;
+    private RetraceMethodResult.Element getMethodContext() {
+      return context.methodContext;
+    }
 
-      private int maxReplaceStringIndex = NO_MATCH;
+    private String getSource() {
+      return context.source;
+    }
 
-      private RetraceStringBuilder(
-          Element classContext,
-          ClassNameGroup classNameGroup,
-          ClassReference qualifiedContext,
-          RetraceMethodResult.Element methodContext,
-          TypeReference typeOrReturnTypeContext,
-          boolean hasTypeOrReturnTypeContext,
-          String retracedString,
-          int adjustedIndex,
-          boolean isAmbiguous,
-          int lineNumber,
-          String source) {
-        this.classContext = classContext;
-        this.classNameGroup = classNameGroup;
-        this.qualifiedContext = qualifiedContext;
-        this.methodContext = methodContext;
-        this.typeOrReturnTypeContext = typeOrReturnTypeContext;
-        this.hasTypeOrReturnTypeContext = hasTypeOrReturnTypeContext;
-        this.retracedString = retracedString;
-        this.adjustedIndex = adjustedIndex;
-        this.isAmbiguous = isAmbiguous;
-        this.lineNumber = lineNumber;
-        this.source = source;
-      }
+    private int getLineNumber() {
+      return context.originalLineNumber;
+    }
 
-      static RetraceStringBuilder create(String string) {
-        return new RetraceStringBuilder(
-            null, null, null, null, null, false, string, 0, false, 0, "");
-      }
+    private boolean isAmbiguous() {
+      return context.isAmbiguous;
+    }
 
-      static RetraceStringBuilder create(RetraceString string) {
-        return new RetraceStringBuilder(
-            string.classContext,
-            string.classNameGroup,
-            string.qualifiedContext,
-            string.methodContext,
-            string.typeOrReturnTypeContext,
-            string.hasTypeOrReturnTypeContext,
-            string.retracedString,
-            string.adjustedIndex,
-            string.isAmbiguous,
-            string.lineNumber,
-            string.source);
-      }
+    private static RetraceString start(RetraceStringContext initialContext) {
+      return new RetraceString(new RetraceStringBuilder("", 0), initialContext);
+    }
 
-      RetraceStringBuilder setClassContext(Element classContext, ClassNameGroup classNameGroup) {
-        this.classContext = classContext;
-        this.classNameGroup = classNameGroup;
-        return this;
-      }
+    private RetraceString updateContext(
+        Function<RetraceStringContext, RetraceStringContext> update) {
+      return new RetraceString(builder, update.apply(context));
+    }
 
-      RetraceStringBuilder setMethodContext(RetraceMethodResult.Element methodContext) {
-        this.methodContext = methodContext;
-        return this;
-      }
+    private RetraceString duplicate(RetraceStringContext newContext) {
+      return new RetraceString(
+          new RetraceStringBuilder(builder.retracedString.toString(), builder.lastCommittedIndex),
+          newContext);
+    }
 
-      RetraceStringBuilder setTypeOrReturnTypeContext(TypeReference typeOrReturnTypeContext) {
-        hasTypeOrReturnTypeContext = true;
-        this.typeOrReturnTypeContext = typeOrReturnTypeContext;
-        return this;
-      }
-
-      RetraceStringBuilder setQualifiedContext(ClassReference qualifiedContext) {
-        this.qualifiedContext = qualifiedContext;
-        return this;
-      }
-
-      RetraceStringBuilder setAmbiguous(boolean isAmbiguous) {
-        this.isAmbiguous = isAmbiguous;
-        return this;
-      }
-
-      RetraceStringBuilder setLineNumber(int lineNumber) {
-        this.lineNumber = lineNumber;
-        return this;
-      }
-
-      RetraceStringBuilder setSource(String source) {
-        this.source = source;
-        return this;
-      }
-
-      RetraceStringBuilder replaceInString(String oldString, String newString) {
-        int oldStringStartIndex = retracedString.indexOf(oldString);
-        assert oldStringStartIndex > NO_MATCH;
-        int oldStringEndIndex = oldStringStartIndex + oldString.length();
-        return replaceInStringRaw(newString, oldStringStartIndex, oldStringEndIndex);
-      }
-
-      RetraceStringBuilder replaceInString(String newString, int originalFrom, int originalTo) {
-        return replaceInStringRaw(
-            newString, originalFrom + adjustedIndex, originalTo + adjustedIndex);
-      }
-
-      RetraceStringBuilder replaceInStringRaw(String newString, int from, int to) {
-        assert from <= to;
-        assert from > maxReplaceStringIndex;
-        String prefix = retracedString.substring(0, from);
-        String postFix = retracedString.substring(to);
-        this.retracedString = prefix + newString + postFix;
-        this.adjustedIndex = adjustedIndex + newString.length() - (to - from);
-        maxReplaceStringIndex = prefix.length() + newString.length();
-        return this;
-      }
-
-      RetraceString build() {
-        return new RetraceString(
-            classContext,
-            classNameGroup,
-            qualifiedContext,
-            methodContext,
-            typeOrReturnTypeContext,
-            hasTypeOrReturnTypeContext,
-            retracedString,
-            adjustedIndex,
-            isAmbiguous,
-            lineNumber,
-            source);
-      }
+    private RetraceString appendRetracedString(
+        String source, String stringToAppend, int originalFromIndex, int originalToIndex) {
+      builder.appendRetracedString(source, stringToAppend, originalFromIndex, originalToIndex);
+      return this;
     }
   }
 
   private interface RegularExpressionGroupHandler {
 
     List<RetraceString> handleMatch(
-        List<RetraceString> strings, Matcher matcher, RetraceApi retracer);
+        String original, List<RetraceString> strings, Matcher matcher, RetraceApi retracer);
+
+    default RetraceStringContext buildInitial(
+        RetraceStringContext context, Matcher matcher, RetraceApi retracer) {
+      return context;
+    }
+
+    default boolean isClassNameGroupHandler() {
+      return false;
+    }
+
+    default ClassNameGroupHandler asClassNameGroupHandler() {
+      return null;
+    }
+
+    default boolean isQualifiedHandler() {
+      return false;
+    }
+
+    default QualifiedRegularExpressionGroupHandler asQualifiedHandler() {
+      return null;
+    }
+  }
+
+  private interface QualifiedRegularExpressionGroupHandler extends RegularExpressionGroupHandler {
+
+    @Override
+    default boolean isQualifiedHandler() {
+      return true;
+    }
+
+    @Override
+    default QualifiedRegularExpressionGroupHandler asQualifiedHandler() {
+      return this;
+    }
+
+    void setClassNameGroupHandler(ClassNameGroupHandler handler);
   }
 
   private abstract static class RegularExpressionGroup {
@@ -431,7 +480,10 @@
   private static final String javaIdentifierSegment =
       "\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*";
 
-  private abstract static class ClassNameGroup extends RegularExpressionGroup {
+  private static final String METHOD_NAME_REGULAR_EXPRESSION =
+      "(?:(" + javaIdentifierSegment + "|\\<init\\>|\\<clinit\\>))";
+
+  abstract static class ClassNameGroup extends RegularExpressionGroup {
 
     abstract String getClassName(ClassReference classReference);
 
@@ -439,30 +491,101 @@
 
     @Override
     RegularExpressionGroupHandler createHandler(String captureGroup) {
-      return (strings, matcher, retracer) -> {
-        if (matcher.start(captureGroup) == NO_MATCH) {
+      return new ClassNameGroupHandler(this, captureGroup);
+    }
+
+    static class ClassNameGroupHandler implements RegularExpressionGroupHandler {
+
+      private RetraceClassResult retraceClassResult = null;
+      private final ClassNameGroup classNameGroup;
+      private final String captureGroup;
+      private RegularExpressionGroupHandler qualifiedHandler;
+
+      public ClassNameGroupHandler(ClassNameGroup classNameGroup, String captureGroup) {
+        this.classNameGroup = classNameGroup;
+        this.captureGroup = captureGroup;
+      }
+
+      @Override
+      public List<RetraceString> handleMatch(
+          String original, List<RetraceString> strings, Matcher matcher, RetraceApi retracer) {
+        final int startOfGroup = matcher.start(captureGroup);
+        if (startOfGroup == NO_MATCH) {
           return strings;
         }
         String typeName = matcher.group(captureGroup);
-        RetraceClassResult retraceResult = retracer.retrace(classFromMatch(typeName));
-        List<RetraceString> retracedStrings = new ArrayList<>();
+        RetraceClassResult retraceResult =
+            retraceClassResult == null
+                ? retracer.retrace(classNameGroup.classFromMatch(typeName))
+                : retraceClassResult;
+        assert !retraceResult.isAmbiguous();
+        List<RetraceString> retraceStrings = new ArrayList<>(strings.size());
         for (RetraceString retraceString : strings) {
           retraceResult.forEach(
               element -> {
-                retracedStrings.add(
-                    retraceString
-                        .transform()
-                        .setClassContext(element, this)
-                        .setMethodContext(null)
-                        .replaceInString(
-                            getClassName(element.getClassReference()),
-                            matcher.start(captureGroup),
-                            matcher.end(captureGroup))
-                        .build());
+                final RetraceString newRetraceString =
+                    retraceString.updateContext(
+                        context -> context.withClassContext(element, element.getClassReference()));
+                retraceStrings.add(newRetraceString);
+                if (qualifiedHandler == null) {
+                  // If there is no qualified handler, commit right away.
+                  newRetraceString.builder.appendRetracedString(
+                      original,
+                      classNameGroup.getClassName(element.getClassReference()),
+                      startOfGroup,
+                      matcher.end(captureGroup));
+                }
               });
         }
-        return retracedStrings;
-      };
+        return retraceStrings;
+      }
+
+      void commitClassName(
+          String original,
+          RetraceString retraceString,
+          ClassReference qualifiedContext,
+          Matcher matcher) {
+        if (matcher.start(captureGroup) == NO_MATCH) {
+          return;
+        }
+        retraceString.builder.appendRetracedString(
+            original,
+            classNameGroup.getClassName(qualifiedContext),
+            matcher.start(captureGroup),
+            matcher.end(captureGroup));
+      }
+
+      @Override
+      public RetraceStringContext buildInitial(
+          RetraceStringContext context, Matcher matcher, RetraceApi retracer) {
+        // Reset the local class context since this the same handler is used for multiple lines.
+        retraceClassResult = null;
+        if (matcher.start(captureGroup) == NO_MATCH || context.classContext != null) {
+          return context;
+        }
+        String typeName = matcher.group(captureGroup);
+        retraceClassResult = retracer.retrace(classNameGroup.classFromMatch(typeName));
+        assert !retraceClassResult.isAmbiguous();
+        Box<RetraceStringContext> box = new Box<>();
+        retraceClassResult.forEach(
+            element -> box.set(context.withClassContext(element, element.getClassReference())));
+        return box.get();
+      }
+
+      public void setQualifiedHandler(RegularExpressionGroupHandler handler) {
+        assert handler.isQualifiedHandler();
+        this.qualifiedHandler = handler;
+      }
+
+      @Override
+      public boolean isClassNameGroupHandler() {
+        return true;
+      }
+
+      @Override
+      public ClassNameGroupHandler asClassNameGroupHandler() {
+        return this;
+      }
     }
   }
 
@@ -504,70 +627,114 @@
 
   private static class MethodNameGroup extends RegularExpressionGroup {
 
+    private final boolean printVerbose;
+
+    public MethodNameGroup(boolean printVerbose) {
+      this.printVerbose = printVerbose;
+    }
+
     @Override
     String subExpression() {
-      return "(?:(" + javaIdentifierSegment + "|\\<init\\>|\\<clinit\\>))";
+      return METHOD_NAME_REGULAR_EXPRESSION;
     }
 
     @Override
     RegularExpressionGroupHandler createHandler(String captureGroup) {
-      return (strings, matcher, retracer) -> {
-        if (matcher.start(captureGroup) == NO_MATCH) {
-          return strings;
+      return new QualifiedRegularExpressionGroupHandler() {
+
+        private ClassNameGroupHandler classNameGroupHandler;
+
+        @Override
+        public void setClassNameGroupHandler(ClassNameGroupHandler handler) {
+          classNameGroupHandler = handler;
         }
-        String methodName = matcher.group(captureGroup);
-        List<RetraceString> retracedStrings = new ArrayList<>();
-        for (RetraceString retraceString : strings) {
-          if (retraceString.classContext == null) {
-            retracedStrings.add(retraceString);
-            continue;
+
+        @Override
+        public List<RetraceString> handleMatch(
+            String original, List<RetraceString> strings, Matcher matcher, RetraceApi retracer) {
+          final int startOfGroup = matcher.start(captureGroup);
+          if (startOfGroup == NO_MATCH) {
+            if (classNameGroupHandler != null) {
+              for (RetraceString string : strings) {
+                classNameGroupHandler.commitClassName(
+                    original, string, string.context.qualifiedContext, matcher);
+              }
+            }
+            return strings;
           }
-          retraceString
-              .getClassContext()
-              .lookupMethod(methodName)
-              .forEach(
-                  element -> {
-                    MethodReference methodReference = element.getMethodReference();
-                    if (retraceString.hasTypeOrReturnTypeContext()) {
-                      if (methodReference.getReturnType() == null
-                          && retraceString.getTypeOrReturnTypeContext() != null) {
-                        return;
-                      } else if (methodReference.getReturnType() != null
-                          && !methodReference
-                              .getReturnType()
-                              .equals(retraceString.getTypeOrReturnTypeContext())) {
-                        return;
-                      }
-                    }
-                    RetraceStringBuilder newRetraceString = retraceString.transform();
-                    ClassReference existingClass =
-                        retraceString.getClassContext().getClassReference();
-                    ClassReference holder = methodReference.getHolderClass();
-                    if (holder != existingClass) {
-                      // The element is defined on another holder.
-                      newRetraceString
-                          .replaceInString(
-                              newRetraceString.classNameGroup.getClassName(existingClass),
-                              newRetraceString.classNameGroup.getClassName(holder))
-                          .setQualifiedContext(holder);
-                    }
-                    newRetraceString
-                        .setMethodContext(element)
-                        .setAmbiguous(element.getRetraceMethodResult().isAmbiguous())
-                        .replaceInString(
-                            methodReference.getMethodName(),
-                            matcher.start(captureGroup),
-                            matcher.end(captureGroup));
-                    retracedStrings.add(newRetraceString.build());
-                  });
+          String methodName = matcher.group(captureGroup);
+          List<RetraceString> retracedStrings = new ArrayList<>();
+          for (RetraceString retraceString : strings) {
+            retraceMethodForString(
+                retraceString,
+                methodName,
+                (element, newContext) -> {
+                  final RetraceString newRetraceString = retraceString.duplicate(newContext);
+                  if (classNameGroupHandler != null) {
+                    classNameGroupHandler.commitClassName(
+                        original,
+                        newRetraceString,
+                        element.getMethodReference().getHolderClass(),
+                        matcher);
+                  }
+                  retracedStrings.add(
+                      newRetraceString.appendRetracedString(
+                          original,
+                          printVerbose
+                              ? RetraceUtils.methodDescriptionFromMethodReference(
+                                  element.getMethodReference(), false, true)
+                              : element.getMethodReference().getMethodName(),
+                          startOfGroup,
+                          matcher.end(captureGroup)));
+                });
+          }
+          return retracedStrings;
         }
-        return retracedStrings;
+
+        @Override
+        public RetraceStringContext buildInitial(
+            RetraceStringContext context, Matcher matcher, RetraceApi retracer) {
+          final int startOfGroup = matcher.start(captureGroup);
+          if (startOfGroup == NO_MATCH || context.methodName != null) {
+            return context;
+          }
+          return context.withMethodName(matcher.group(captureGroup));
+        }
       };
     }
+
+    private static void retraceMethodForString(
+        RetraceString retraceString,
+        String methodName,
+        BiConsumer<RetraceMethodResult.Element, RetraceStringContext> process) {
+      if (retraceString.context.classContext == null) {
+        return;
+      }
+      RetraceMethodResult retraceMethodResult =
+          retraceString.getClassContext().lookupMethod(methodName);
+      if (retraceString.context.minifiedLineNumber > NO_MATCH) {
+        retraceMethodResult =
+            retraceMethodResult.narrowByLine(retraceString.context.minifiedLineNumber);
+      }
+      retraceMethodResult.forEach(
+          element ->
+              process.accept(
+                  element,
+                  retraceString.context.withMethodContext(
+                      element,
+                      element.getMethodReference().getHolderClass(),
+                      element.getRetraceMethodResult().isAmbiguous())));
+    }
   }
 
   private static class FieldNameGroup extends RegularExpressionGroup {
 
+    private final boolean printVerbose;
+
+    public FieldNameGroup(boolean printVerbose) {
+      this.printVerbose = printVerbose;
+    }
+
     @Override
     String subExpression() {
       return javaIdentifierSegment;
@@ -575,44 +742,71 @@
 
     @Override
     RegularExpressionGroupHandler createHandler(String captureGroup) {
-      return (strings, matcher, retracer) -> {
-        if (matcher.start(captureGroup) == NO_MATCH) {
-          return strings;
+      return new QualifiedRegularExpressionGroupHandler() {
+
+        private ClassNameGroupHandler classNameGroupHandler;
+
+        @Override
+        public void setClassNameGroupHandler(ClassNameGroupHandler handler) {
+          classNameGroupHandler = handler;
         }
-        String methodName = matcher.group(captureGroup);
-        List<RetraceString> retracedStrings = new ArrayList<>();
-        for (RetraceString retraceString : strings) {
-          if (retraceString.getClassContext() == null) {
-            retracedStrings.add(retraceString);
-            continue;
+
+        @Override
+        public List<RetraceString> handleMatch(
+            String original, List<RetraceString> strings, Matcher matcher, RetraceApi retracer) {
+          final int startOfGroup = matcher.start(captureGroup);
+          if (startOfGroup == NO_MATCH) {
+            if (classNameGroupHandler != null) {
+              for (RetraceString string : strings) {
+                classNameGroupHandler.commitClassName(
+                    original, string, string.context.qualifiedContext, matcher);
+              }
+            }
+            return strings;
           }
-          retraceString
-              .getClassContext()
-              .lookupField(methodName)
-              .forEach(
-                  element -> {
-                    RetraceStringBuilder newRetraceString = retraceString.transform();
-                    ClassReference existingClass =
-                        retraceString.getClassContext().getClassReference();
-                    ClassReference holder = element.getFieldReference().getHolderClass();
-                    if (holder != existingClass) {
-                      // The element is defined on another holder.
-                      newRetraceString
-                          .replaceInString(
-                              newRetraceString.classNameGroup.getClassName(existingClass),
-                              newRetraceString.classNameGroup.getClassName(holder))
-                          .setQualifiedContext(holder);
-                    }
-                    newRetraceString.replaceInString(
-                        element.getFieldReference().getFieldName(),
-                        matcher.start(captureGroup),
-                        matcher.end(captureGroup));
-                    retracedStrings.add(newRetraceString.build());
-                  });
+          String methodName = matcher.group(captureGroup);
+          List<RetraceString> retracedStrings = new ArrayList<>();
+          for (RetraceString retraceString : strings) {
+            if (retraceString.getClassContext() == null) {
+              assert classNameGroupHandler == null;
+              return strings;
+            }
+            final RetraceFieldResult retraceFieldResult =
+                retraceString.getClassContext().lookupField(methodName);
+            assert !retraceFieldResult.isAmbiguous();
+            retraceFieldResult.forEach(
+                element -> {
+                  if (classNameGroupHandler != null) {
+                    classNameGroupHandler.commitClassName(
+                        original,
+                        retraceString,
+                        element.getFieldReference().getHolderClass(),
+                        matcher);
+                  }
+                  retracedStrings.add(
+                      retraceString
+                          .updateContext(
+                              context ->
+                                  context.withQualifiedContext(
+                                      element.getFieldReference().getHolderClass()))
+                          .appendRetracedString(
+                              original,
+                              getFieldString(element.getFieldReference()),
+                              startOfGroup,
+                              matcher.end(captureGroup)));
+                });
+          }
+          return retracedStrings;
         }
-        return retracedStrings;
       };
     }
+
+    private String getFieldString(FieldReference fieldReference) {
+      if (!printVerbose) {
+        return fieldReference.getFieldName();
+      }
+      return fieldReference.getFieldType().getTypeName() + " " + fieldReference.getFieldName();
+    }
   }
 
   private static class SourceFileGroup extends RegularExpressionGroup {
@@ -624,33 +818,35 @@
 
     @Override
     RegularExpressionGroupHandler createHandler(String captureGroup) {
-      return (strings, matcher, retracer) -> {
-        if (matcher.start(captureGroup) == NO_MATCH) {
+      return (original, strings, matcher, retracer) -> {
+        final int startOfGroup = matcher.start(captureGroup);
+        if (startOfGroup == NO_MATCH) {
           return strings;
         }
         String fileName = matcher.group(captureGroup);
-        List<RetraceString> retracedStrings = new ArrayList<>();
+        List<RetraceString> retracedStrings = null;
         for (RetraceString retraceString : strings) {
-          if (retraceString.classContext == null) {
-            retracedStrings.add(retraceString);
-            continue;
+          if (retraceString.context.classContext == null) {
+            return strings;
+          }
+          if (retracedStrings == null) {
+            retracedStrings = new ArrayList<>();
           }
           RetraceSourceFileResult sourceFileResult =
               retraceString.getMethodContext() != null
                   ? retraceString.getMethodContext().retraceSourceFile(fileName)
                   : RetraceUtils.getSourceFile(
                       retraceString.getClassContext(),
-                      retraceString.getQualifiedContext(),
+                      retraceString.context.qualifiedContext,
                       fileName);
           retracedStrings.add(
               retraceString
-                  .transform()
-                  .setSource(fileName)
-                  .replaceInString(
+                  .updateContext(context -> context.withSource(sourceFileResult.getFilename()))
+                  .appendRetracedString(
+                      original,
                       sourceFileResult.getFilename(),
-                      matcher.start(captureGroup),
-                      matcher.end(captureGroup))
-                  .build());
+                      startOfGroup,
+                      matcher.end(captureGroup)));
         }
         return retracedStrings;
       };
@@ -666,63 +862,90 @@
 
     @Override
     RegularExpressionGroupHandler createHandler(String captureGroup) {
-      return (strings, matcher, retracer) -> {
-        if (matcher.start(captureGroup) == NO_MATCH) {
-          return strings;
+      return new RegularExpressionGroupHandler() {
+        @Override
+        public List<RetraceString> handleMatch(
+            String original, List<RetraceString> strings, Matcher matcher, RetraceApi retracer) {
+          final int startOfGroup = matcher.start(captureGroup);
+          if (startOfGroup == NO_MATCH) {
+            return strings;
+          }
+          String lineNumberAsString = matcher.group(captureGroup);
+          if (lineNumberAsString.isEmpty()) {
+            return strings;
+          }
+          int lineNumber = Integer.parseInt(lineNumberAsString);
+          List<RetraceString> retracedStrings = new ArrayList<>();
+          for (RetraceString retraceString : strings) {
+            RetraceMethodResult.Element methodContext = retraceString.context.methodContext;
+            if (methodContext == null) {
+              if (retraceString.context.classContext == null
+                  || retraceString.context.methodName == null) {
+                // We have no way of retracing the line number.
+                retracedStrings.add(retraceString);
+                continue;
+              }
+              // This situation arises when we have a matched pattern as %l..%c.%m where the
+              // line number handler is defined before the methodname handler.
+              MethodNameGroup.retraceMethodForString(
+                  retraceString,
+                  retraceString.context.methodName,
+                  (element, newContext) -> {
+                    // The same method can be represented multiple times if it has multiple
+                    // mappings.
+                    if (element.hasNoLineNumberRange()
+                        || !element.containsMinifiedLineNumber(lineNumber)) {
+                      diagnosticsHandler.info(
+                          new StringDiagnostic(
+                              "Pruning "
+                                  + retraceString.builder.retracedString.toString()
+                                  + " from result because method is not in range on line number "
+                                  + lineNumber));
+                    }
+                    final int originalLineNumber = element.getOriginalLineNumber(lineNumber);
+                    retracedStrings.add(
+                        retraceString
+                            .updateContext(
+                                context -> context.withLineNumbers(lineNumber, originalLineNumber))
+                            .appendRetracedString(
+                                original,
+                                originalLineNumber + "",
+                                startOfGroup,
+                                matcher.end(captureGroup)));
+                  });
+              continue;
+            }
+            // If the method context is unknown, do nothing.
+            if (methodContext.getMethodReference().isUnknown()
+                || methodContext.hasNoLineNumberRange()) {
+              retracedStrings.add(retraceString);
+              continue;
+            }
+            int originalLineNumber = methodContext.getOriginalLineNumber(lineNumber);
+            retracedStrings.add(
+                retraceString
+                    .updateContext(
+                        context -> context.withLineNumbers(lineNumber, originalLineNumber))
+                    .appendRetracedString(
+                        original,
+                        originalLineNumber + "",
+                        startOfGroup,
+                        matcher.end(captureGroup)));
+          }
+          return retracedStrings;
         }
-        String lineNumberAsString = matcher.group(captureGroup);
-        int lineNumber =
-            lineNumberAsString.isEmpty() ? NO_MATCH : Integer.parseInt(lineNumberAsString);
-        List<RetraceString> retracedStrings = new ArrayList<>();
-        boolean seenRange = false;
-        for (RetraceString retraceString : strings) {
-          RetraceMethodResult.Element methodContext = retraceString.methodContext;
-          if (methodContext == null || methodContext.getMethodReference().isUnknown()) {
-            retracedStrings.add(retraceString);
-            continue;
+
+        @Override
+        public RetraceStringContext buildInitial(
+            RetraceStringContext context, Matcher matcher, RetraceApi retracer) {
+          if (matcher.start(captureGroup) == NO_MATCH || context.minifiedLineNumber > NO_MATCH) {
+            return context;
           }
-          if (methodContext.hasNoLineNumberRange()) {
-            continue;
-          }
-          seenRange = true;
-          Set<MethodReference> narrowedSet =
-              methodContext.getRetraceMethodResult().narrowByLine(lineNumber).stream()
-                  .map(RetraceMethodResult.Element::getMethodReference)
-                  .collect(Collectors.toSet());
-          if (!narrowedSet.contains(methodContext.getMethodReference())) {
-            // Prune the retraceString since we now have line number information and this is not
-            // a part of the result.
-            diagnosticsHandler.info(
-                new StringDiagnostic(
-                    "Pruning "
-                        + retraceString.getRetracedString()
-                        + " from result because method is not defined on line number "
-                        + lineNumber));
-            continue;
-          }
-          // The same method can be represented multiple times if it has multiple mappings.
-          if (!methodContext.containsMinifiedLineNumber(lineNumber)) {
-            diagnosticsHandler.info(
-                new StringDiagnostic(
-                    "Pruning "
-                        + retraceString.getRetracedString()
-                        + " from result because method is not in range on line number "
-                        + lineNumber));
-            continue;
-          }
-          int originalLineNumber = methodContext.getOriginalLineNumber(lineNumber);
-          retracedStrings.add(
-              retraceString
-                  .transform()
-                  .setAmbiguous(false)
-                  .setLineNumber(originalLineNumber)
-                  .replaceInString(
-                      originalLineNumber + "",
-                      matcher.start(captureGroup),
-                      matcher.end(captureGroup))
-                  .build());
+          String lineNumberAsString = matcher.group(captureGroup);
+          return context.withLineNumbers(
+              lineNumberAsString.isEmpty() ? NO_MATCH : Integer.parseInt(lineNumberAsString),
+              NO_MATCH);
         }
-        return seenRange ? retracedStrings : strings;
       };
     }
   }
@@ -757,8 +980,9 @@
 
     @Override
     RegularExpressionGroupHandler createHandler(String captureGroup) {
-      return (strings, matcher, retracer) -> {
-        if (matcher.start(captureGroup) == NO_MATCH) {
+      return (original, strings, matcher, retracer) -> {
+        final int startOfGroup = matcher.start(captureGroup);
+        if (startOfGroup == NO_MATCH) {
           return strings;
         }
         String typeName = matcher.group(captureGroup);
@@ -767,29 +991,25 @@
           return strings;
         }
         TypeReference typeReference = Reference.returnTypeFromDescriptor(descriptor);
-        List<RetraceString> retracedStrings = new ArrayList<>();
         RetraceTypeResult retracedType = retracer.retrace(typeReference);
+        assert !retracedType.isAmbiguous();
         for (RetraceString retraceString : strings) {
           retracedType.forEach(
               element -> {
                 TypeReference retracedReference = element.getTypeReference();
-                retracedStrings.add(
-                    retraceString
-                        .transform()
-                        .setTypeOrReturnTypeContext(retracedReference)
-                        .replaceInString(
-                            retracedReference == null ? "void" : retracedReference.getTypeName(),
-                            matcher.start(captureGroup),
-                            matcher.end(captureGroup))
-                        .build());
+                retraceString.appendRetracedString(
+                    original,
+                    retracedReference == null ? "void" : retracedReference.getTypeName(),
+                    startOfGroup,
+                    matcher.end(captureGroup));
               });
         }
-        return retracedStrings;
+        return strings;
       };
     }
   }
 
-  private class MethodArgumentsGroup extends RegularExpressionGroup {
+  private static class MethodArgumentsGroup extends RegularExpressionGroup {
 
     @Override
     String subExpression() {
@@ -798,74 +1018,36 @@
 
     @Override
     RegularExpressionGroupHandler createHandler(String captureGroup) {
-      return (strings, matcher, retracer) -> {
-        if (matcher.start(captureGroup) == NO_MATCH) {
+      return (original, strings, matcher, retracer) -> {
+        final int startOfGroup = matcher.start(captureGroup);
+        if (startOfGroup == NO_MATCH) {
           return strings;
         }
-        Set<List<TypeReference>> initialValue = new LinkedHashSet<>();
-        initialValue.add(new ArrayList<>());
-        Set<List<TypeReference>> allRetracedReferences =
+        final String formals =
             Arrays.stream(matcher.group(captureGroup).split(","))
-                .map(String::trim)
-                .reduce(
-                    initialValue,
-                    (acc, typeName) -> {
+                .map(
+                    typeName -> {
+                      typeName = typeName.trim();
+                      if (typeName.isEmpty()) {
+                        return null;
+                      }
                       String descriptor = DescriptorUtils.javaTypeToDescriptor(typeName);
                       if (!DescriptorUtils.isDescriptor(descriptor) && !"V".equals(descriptor)) {
-                        return acc;
+                        return typeName;
                       }
-                      TypeReference typeReference = Reference.returnTypeFromDescriptor(descriptor);
-                      Set<List<TypeReference>> retracedTypes = new LinkedHashSet<>();
-                      retracer
-                          .retrace(typeReference)
-                          .forEach(
-                              element -> {
-                                for (List<TypeReference> currentReferences : acc) {
-                                  ArrayList<TypeReference> newList =
-                                      new ArrayList<>(currentReferences);
-                                  newList.add(element.getTypeReference());
-                                  retracedTypes.add(newList);
-                                }
-                              });
-                      return retracedTypes;
-                    },
-                    (l1, l2) -> {
-                      l1.addAll(l2);
-                      return l1;
-                    });
-        List<RetraceString> retracedStrings = new ArrayList<>();
-        for (RetraceString retraceString : strings) {
-          if (retraceString.getMethodContext() != null
-              && !allRetracedReferences.contains(
-                  retraceString.getMethodContext().getMethodReference().getFormalTypes())) {
-            // Prune the string since we now know the formals.
-            String formals =
-                retraceString.getMethodContext().getMethodReference().getFormalTypes().stream()
-                    .map(TypeReference::getTypeName)
-                    .collect(Collectors.joining(","));
-            diagnosticsHandler.info(
-                new StringDiagnostic(
-                    "Pruning "
-                        + retraceString.getRetracedString()
-                        + " from result because formals ("
-                        + formals
-                        + ") do not match result set."));
-            continue;
-          }
-          for (List<TypeReference> retracedReferences : allRetracedReferences) {
-            retracedStrings.add(
-                retraceString
-                    .transform()
-                    .replaceInString(
-                        retracedReferences.stream()
-                            .map(TypeReference::getTypeName)
-                            .collect(Collectors.joining(",")),
-                        matcher.start(captureGroup),
-                        matcher.end(captureGroup))
-                    .build());
-          }
+                      final RetraceTypeResult retraceResult =
+                          retracer.retrace(Reference.returnTypeFromDescriptor(descriptor));
+                      assert !retraceResult.isAmbiguous();
+                      final Box<TypeReference> elementBox = new Box<>();
+                      retraceResult.forEach(element -> elementBox.set(element.getTypeReference()));
+                      return elementBox.get().getTypeName();
+                    })
+                .filter(Objects::nonNull)
+                .collect(Collectors.joining(","));
+        for (RetraceString string : strings) {
+          string.appendRetracedString(original, formals, startOfGroup, matcher.end(captureGroup));
         }
-        return retracedStrings;
+        return strings;
       };
     }
   }
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceStackTrace.java b/src/main/java/com/android/tools/r8/retrace/RetraceStackTrace.java
index 88d048f..fd28127 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceStackTrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceStackTrace.java
@@ -434,7 +434,7 @@
                     moduleName,
                     methodReference.getHolderClass().getTypeName(),
                     methodReference.getMethodName(),
-                    methodDescriptionFromMethodReference(methodReference, verbose),
+                    methodDescriptionFromMethodReference(methodReference, true, verbose),
                     methodElement.retraceSourceFile(fileName).getFilename(),
                     hasLinePosition()
                         ? methodElement.getOriginalLineNumber(linePosition)
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceTypeResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceTypeResult.java
index 8ea169f..b70602d 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceTypeResult.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceTypeResult.java
@@ -35,6 +35,10 @@
         .map(clazz -> new Element(clazz.getClassReference()));
   }
 
+  public boolean isAmbiguous() {
+    return false;
+  }
+
   @Override
   public RetraceTypeResult forEach(Consumer<Element> resultConsumer) {
     stream().forEach(resultConsumer);
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceUtils.java b/src/main/java/com/android/tools/r8/retrace/RetraceUtils.java
index c82bf36..8e862d0 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceUtils.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceUtils.java
@@ -17,18 +17,20 @@
       Sets.newHashSet("", "SourceFile", "Unknown", "Unknown Source");
 
   public static String methodDescriptionFromMethodReference(
-      MethodReference methodReference, boolean verbose) {
+      MethodReference methodReference, boolean appendHolder, boolean verbose) {
     if (!verbose || methodReference.isUnknown()) {
       return methodReference.getHolderClass().getTypeName() + "." + methodReference.getMethodName();
     }
     StringBuilder sb = new StringBuilder();
+    if (appendHolder) {
+      sb.append(methodReference.getHolderClass().getTypeName());
+      sb.append(".");
+    }
     sb.append(
         methodReference.getReturnType() == null
             ? "void"
             : methodReference.getReturnType().getTypeName());
     sb.append(" ");
-    sb.append(methodReference.getHolderClass().getTypeName());
-    sb.append(".");
     sb.append(methodReference.getMethodName());
     sb.append("(");
     boolean seenFirstIndex = false;
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index c716da8..6768368 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -42,13 +42,13 @@
 import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
 import com.android.tools.r8.graph.SubtypingInfo;
 import com.android.tools.r8.graph.SyntheticItems;
+import com.android.tools.r8.graph.SyntheticItems.CommittedItems;
 import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
 import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter;
 import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
 import com.android.tools.r8.ir.desugar.LambdaDescriptor;
 import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter;
-import com.android.tools.r8.utils.AssertionUtils;
 import com.android.tools.r8.utils.CollectionUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ListUtils;
@@ -1000,17 +1000,11 @@
             .map(FieldResolutionResult::getResolvedField)
             .collect(Collectors.toList()));
 
-    assert lens.assertDefinitionsNotModified(
-        neverMerge.stream()
-            .map(this::definitionFor)
-            .filter(AssertionUtils::assertNotNull)
-            .collect(Collectors.toList()));
-
-    DexDefinitionSupplier definitionSupplier =
-        application.getDefinitionsSupplier(SyntheticItems.createInitialSyntheticItems());
+    CommittedItems committedItems = getSyntheticItems().commit(application, lens);
+    DexDefinitionSupplier definitionSupplier = application.getDefinitionsSupplier(committedItems);
     return new AppInfoWithLiveness(
         application,
-        getSyntheticItems().commit(application, lens),
+        committedItems,
         deadProtoTypes,
         missingTypes,
         lens.rewriteTypes(liveTypes),
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index f0340a3..c23e47d 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -107,6 +107,7 @@
   private final ImmutableList<InternalArchiveClassFileProvider> archiveProvidersToClose;
 
   private final StringResource proguardMapOutputData;
+  private final StringResource proguardMapInputData;
   private final List<StringResource> mainDexListResources;
   private final List<String> mainDexClasses;
 
@@ -176,6 +177,7 @@
       ImmutableList<ClassFileResourceProvider> libraryResourceProviders,
       ImmutableList<InternalArchiveClassFileProvider> archiveProvidersToClose,
       StringResource proguardMapOutputData,
+      StringResource proguardMapInputData,
       List<StringResource> mainDexListResources,
       List<String> mainDexClasses) {
     this.programResourceProviders = programResourceProviders;
@@ -184,6 +186,7 @@
     this.libraryResourceProviders = libraryResourceProviders;
     this.archiveProvidersToClose = archiveProvidersToClose;
     this.proguardMapOutputData = proguardMapOutputData;
+    this.proguardMapInputData = proguardMapInputData;
     this.mainDexListResources = mainDexListResources;
     this.mainDexClasses = mainDexClasses;
     assert verifyInternalProvidersInCloseSet(classpathResourceProviders, archiveProvidersToClose);
@@ -344,6 +347,11 @@
     return proguardMapOutputData;
   }
 
+  /** Get the proguard-map associated with an input "app" if it exists. */
+  public StringResource getProguardMapInputData() {
+    return proguardMapInputData;
+  }
+
   /**
    * True if the main dex list resources exists.
    */
@@ -381,6 +389,7 @@
         libraryResourceProviders,
         archiveProvidersToClose,
         proguardMapOutputData,
+        proguardMapInputData,
         ImmutableList.of(),
         ImmutableList.of());
   }
@@ -718,8 +727,8 @@
     private List<String> mainDexListClasses = new ArrayList<>();
     private boolean ignoreDexInArchive = false;
 
-    // Proguard map data is output only data. This should never be used as input to a compilation.
     private StringResource proguardMapOutputData;
+    private StringResource proguardMapInputData;
 
     private final Reporter reporter;
 
@@ -737,6 +746,7 @@
       archiveProvidersToClose.addAll(app.archiveProvidersToClose);
       mainDexListResources = app.mainDexListResources;
       mainDexListClasses = app.mainDexClasses;
+      proguardMapInputData = app.proguardMapInputData;
     }
 
     public Reporter getReporter() {
@@ -1055,6 +1065,11 @@
       return this;
     }
 
+    public Builder setProguardMapInputData(Path mapPath) {
+      this.proguardMapInputData = StringResource.fromFile(mapPath);
+      return this;
+    }
+
     /**
      * Add a main-dex list file.
      */
@@ -1150,6 +1165,7 @@
           ImmutableList.copyOf(libraryResourceProviders),
           ImmutableList.copyOf(archiveProvidersToClose),
           proguardMapOutputData,
+          proguardMapInputData,
           mainDexListResources,
           mainDexListClasses);
     }
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 69410dd..bb51cc0 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1257,6 +1257,8 @@
     // Option for testing outlining with interface array arguments, see b/132420510.
     public boolean allowOutlinerInterfaceArrayArguments = false;
 
+    public int limitNumberOfClassesPerDex = -1;
+
     public MinifierTestingOptions minifier = new MinifierTestingOptions();
 
     // Testing hooks to trigger effects in various compiler places.
diff --git a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
index 9c703e7..17c2fd2 100644
--- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -235,7 +235,8 @@
           currentPc,
           currentPosition,
           processedEvents,
-          dexItemFactory);
+          dexItemFactory,
+          true);
       previousPc = currentPc;
       previousPosition = currentPosition;
     }
@@ -377,10 +378,9 @@
           Map<DexMethod, MethodSignature> signatures = new IdentityHashMap<>();
           signatures.put(originalMethod, originalSignature);
           Function<DexMethod, MethodSignature> getOriginalMethodSignature =
-              m -> {
-                return signatures.computeIfAbsent(
-                    m, key -> MethodSignature.fromDexMethod(m, m.holder != clazz.getType()));
-              };
+              m ->
+                  signatures.computeIfAbsent(
+                      m, key -> MethodSignature.fromDexMethod(m, m.holder != clazz.getType()));
 
           MemberNaming memberNaming = new MemberNaming(originalSignature, obfuscatedName);
           onDemandClassNamingBuilder.get().addMemberEntry(memberNaming);
@@ -622,13 +622,17 @@
     List<DexDebugEvent> processedEvents = new ArrayList<>();
 
     PositionEventEmitter positionEventEmitter =
-        new PositionEventEmitter(application.dexItemFactory, method.method, processedEvents);
+        new PositionEventEmitter(
+            application.dexItemFactory,
+            appView.graphLens().getOriginalMethodSignature(method.method),
+            processedEvents);
 
     Box<Boolean> inlinedOriginalPosition = new Box<>(false);
 
     // Debug event visitor to map line numbers.
     DexDebugEventVisitor visitor =
-        new DexDebugPositionState(debugInfo.startLine, method.method) {
+        new DexDebugPositionState(
+            debugInfo.startLine, appView.graphLens().getOriginalMethodSignature(method.method)) {
 
           // Keep track of what PC has been emitted.
           private int emittedPc = 0;
diff --git a/src/test/java/com/android/tools/r8/D8CommandTest.java b/src/test/java/com/android/tools/r8/D8CommandTest.java
index f395118..5be6b85 100644
--- a/src/test/java/com/android/tools/r8/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/D8CommandTest.java
@@ -621,6 +621,17 @@
   }
 
   @Test
+  public void pgInputMap() throws CompilationFailedException, IOException, ResourceException {
+    Path mapFile = temp.newFile().toPath();
+    FileUtils.writeTextFile(
+        mapFile,
+        "com.android.tools.r8.ApiLevelException -> com.android.tools.r8.a:",
+        "    boolean $assertionsDisabled -> c");
+    D8Command d8Command = parse("--pg-map", mapFile.toString());
+    assertFalse(d8Command.getInputApp().getProguardMapInputData().getString().isEmpty());
+  }
+
+  @Test
   public void numThreadsOption() throws Exception {
     assertEquals(ThreadUtils.NOT_SPECIFIED, parse().getThreadCount());
     assertEquals(1, parse("--thread-count", "1").getThreadCount());
diff --git a/src/test/java/com/android/tools/r8/JvmTestBuilder.java b/src/test/java/com/android/tools/r8/JvmTestBuilder.java
index 97e244b..4317ca8 100644
--- a/src/test/java/com/android/tools/r8/JvmTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/JvmTestBuilder.java
@@ -162,6 +162,11 @@
     return addVmArguments(Arrays.asList(arguments));
   }
 
+  public JvmTestBuilder disassemble() throws Exception {
+    ToolHelper.disassemble(builder.build(), System.out);
+    return self();
+  }
+
   public JvmTestBuilder addAndroidBuildVersion() {
     addVmArguments("-D" + AndroidBuildVersion.PROPERTY + "=10000");
     return addProgramClasses(AndroidBuildVersion.class);
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 35e1eec..38637cf 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -73,6 +73,7 @@
 import com.android.tools.r8.utils.Timing;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
@@ -451,6 +452,11 @@
         .build();
   }
 
+  protected List<String> classNamesFromDexFile(Path dexFile) throws IOException {
+    return new CodeInspector(dexFile)
+        .allClasses().stream().map(FoundClassSubject::toString).collect(Collectors.toList());
+  }
+
   /**
    * Build an AndroidApp with the specified test classes.
    */
diff --git a/src/test/java/com/android/tools/r8/TestParametersBuilder.java b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
index 8c6b864..7b630c3 100644
--- a/src/test/java/com/android/tools/r8/TestParametersBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
@@ -192,7 +192,7 @@
   }
 
   public TestParametersCollection build() {
-    assert !enableApiLevels || hasDexRuntimeFilter;
+    assert !enableApiLevels || enableApiLevelsForCf || hasDexRuntimeFilter;
     return new TestParametersCollection(
         getAvailableRuntimes()
             .flatMap(this::createParameters)
diff --git a/src/test/java/com/android/tools/r8/d8/ProguardMapSortByTest.java b/src/test/java/com/android/tools/r8/d8/ProguardMapSortByTest.java
new file mode 100644
index 0000000..49e78f6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/d8/ProguardMapSortByTest.java
@@ -0,0 +1,109 @@
+// Copyright (c) 2020, 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.d8;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.D8TestBuilder;
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+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.util.HashMap;
+import java.util.Map;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ProguardMapSortByTest extends TestBase {
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+  }
+
+  public ProguardMapSortByTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testMapSorting() throws Exception {
+    Path mappingFile = temp.newFile().toPath();
+    // Sort the classes, so that they are in 3 packages _not_ sorted by their original name.
+    FileUtils.writeTextFile(
+        mappingFile,
+        "com.A.a -> " + C.class.getTypeName() + ":",
+        "com.A.b -> " + B.class.getTypeName() + ":",
+        "com.B.a -> " + D.class.getTypeName() + ":",
+        "com.B.b -> " + E.class.getTypeName() + ":",
+        "com.C.a -> " + A.class.getTypeName() + ":");
+    D8TestBuilder d8TestBuilder =
+        testForD8()
+            .addOptionsModification(
+                internalOptions -> internalOptions.testing.limitNumberOfClassesPerDex = 2);
+    if (parameters.getApiLevel().isLessThan(AndroidApiLevel.L)) {
+      d8TestBuilder.addMainDexListClasses(A.class);
+    }
+    Path outputDir = temp.newFolder().toPath();
+    d8TestBuilder
+        .release()
+        .addProgramClasses(A.class, B.class, C.class, D.class, E.class)
+        .setMinApi(parameters.getApiLevel())
+        .apply(b -> b.getBuilder().setProguardInputMapFile(mappingFile))
+        .run(parameters.getRuntime(), A.class)
+        .assertSuccessWithOutputLines("Hello world!")
+        .app()
+        .writeToDirectory(outputDir, OutputMode.DexIndexed);
+    Map<String, String> mapping = new HashMap<>();
+    for (String dexFile : ImmutableList.of("classes.dex", "classes2.dex", "classes3.dex")) {
+      Path resolvedDexFile = outputDir.resolve(dexFile);
+      assertTrue(Files.exists(resolvedDexFile));
+      classNamesFromDexFile(resolvedDexFile).forEach(name -> mapping.put(name, dexFile));
+    }
+    // Check that the classes are grouped by their original package name.
+    assertEquals(mapping.get(B.class.getTypeName()), mapping.get(C.class.getTypeName()));
+    assertEquals(mapping.get(D.class.getTypeName()), mapping.get(E.class.getTypeName()));
+    assertTrue(
+        mapping.values().stream().filter(s -> s.equals(mapping.get(A.class.getTypeName()))).count()
+            == 1);
+  }
+
+  static class A {
+    public static void main(String[] args) {
+      System.out.println("Hello world!");
+    }
+  }
+
+  static class B {
+    public static void foo() {
+      System.out.println("foo");
+    }
+  }
+
+  static class C {
+    public static void bar() {
+      System.out.println("bar");
+    }
+  }
+
+  static class D {
+    public static void foobar() {
+      System.out.println("foobar");
+    }
+  }
+
+  static class E {
+    public static void barfoo() {
+      System.out.println("barfoo");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarToClassFileB156591935.java b/src/test/java/com/android/tools/r8/desugar/DesugarToClassFileB156591935.java
index 63f1aa6..674f288 100644
--- a/src/test/java/com/android/tools/r8/desugar/DesugarToClassFileB156591935.java
+++ b/src/test/java/com/android/tools/r8/desugar/DesugarToClassFileB156591935.java
@@ -4,9 +4,13 @@
 
 package com.android.tools.r8.desugar;
 
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 
 import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersBuilder;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -24,31 +28,87 @@
 public class DesugarToClassFileB156591935 extends TestBase implements Opcodes {
 
   @Parameterized.Parameters(name = "{0}")
-  public static AndroidApiLevel[] data() {
-    return AndroidApiLevel.values();
+  public static TestParametersCollection data() {
+    return TestParametersBuilder.builder().withCfRuntimes().withAllApiLevelsAlsoForCf().build();
   }
 
   private final AndroidApiLevel apiLevel;
 
-  public DesugarToClassFileB156591935(AndroidApiLevel apiLevel) {
-    this.apiLevel = apiLevel;
+  public DesugarToClassFileB156591935(TestParameters parameters) {
+    this.apiLevel = parameters.getApiLevel();
   }
 
-  private void expectNops(CodeInspector inspector, int numberOfNops) {
-    ClassSubject a = inspector.clazz("A");
-    assertEquals(
-        numberOfNops, a.clinit().streamInstructions().filter(InstructionSubject::isNop).count());
+  private void expectNoNops(String className, CodeInspector inspector) {
+    ClassSubject classSubject = inspector.clazz(className);
+    assertFalse(classSubject.clinit().streamInstructions().anyMatch(InstructionSubject::isNop));
+  }
+
+  private void expectNoLoad(String className, CodeInspector inspector) {
+    ClassSubject classSubject = inspector.clazz(className);
+    assertFalse(
+        classSubject
+            .clinit()
+            .streamInstructions()
+            .anyMatch(instructionSubject -> instructionSubject.asCfInstruction().isLoad()));
+  }
+
+  private void expectNoStore(String className, CodeInspector inspector) {
+    ClassSubject classSubject = inspector.clazz(className);
+    assertFalse(
+        classSubject
+            .clinit()
+            .streamInstructions()
+            .anyMatch(instructionSubject -> instructionSubject.asCfInstruction().isStore()));
+  }
+
+  private void expectNoSwap(String className, CodeInspector inspector) {
+    ClassSubject classSubject = inspector.clazz(className);
+    assertFalse(
+        classSubject
+            .clinit()
+            .streamInstructions()
+            .anyMatch(
+                instructionSubject ->
+                    instructionSubject.asCfInstruction().isStackInstruction(Opcode.Swap)));
   }
 
   @Test
   public void test() throws Exception {
     // No nops in the input - see dump below.
-    // TODO(b/156591935): The three nops should be avoided.
     testForD8(Backend.CF)
         .addProgramClassFileData(dump())
         .setMinApi(apiLevel)
         .compile()
-        .inspect(inspector -> expectNops(inspector, 3));
+        .inspect(inspector -> expectNoNops("A", inspector))
+        .inspect(inspector -> expectNoLoad("A", inspector))
+        .inspect(inspector -> expectNoStore("A", inspector))
+        .inspect(inspector -> expectNoSwap("A", inspector));
+  }
+
+  @Test
+  public void testWithExtraLine() throws Exception {
+    // No nops in the input - see dump below.
+    testForD8(Backend.CF)
+        .addProgramClassFileData(dumpWithExtraLine())
+        .setMinApi(apiLevel)
+        .compile()
+        .inspect(inspector -> expectNoNops("A", inspector))
+        .inspect(inspector -> expectNoLoad("A", inspector))
+        .inspect(inspector -> expectNoStore("A", inspector))
+        .inspect(inspector -> expectNoSwap("A", inspector));
+  }
+
+  @Test
+  public void testWithMoreConsts() throws Exception {
+    // No nops in the input - see dump below.
+    testForD8(Backend.CF)
+        .addProgramClassFileData(dumpMoreConsts())
+        .setMinApi(apiLevel)
+        .compile()
+        .inspect(inspector -> expectNoNops("B", inspector))
+        .inspect(inspector -> expectNoLoad("B", inspector))
+        .inspect(inspector -> expectNoStore("B", inspector))
+        .inspect(inspector -> expectNoSwap("B", inspector));
   }
 
   /*
@@ -86,7 +146,7 @@
       }
     }
   */
-  public static byte[] dump() {
+  private static byte[] dump() {
 
     ClassWriter classWriter = new ClassWriter(0);
     FieldVisitor fieldVisitor;
@@ -251,4 +311,460 @@
 
     return classWriter.toByteArray();
   }
+
+  // Patched version of the code above. Added an additional line change between the two const
+  // instructions for the "createA" calls in <clinit>. Look for label variable names ending in x.
+  private static byte[] dumpWithExtraLine() {
+
+    ClassWriter classWriter = new ClassWriter(0);
+    FieldVisitor fieldVisitor;
+    MethodVisitor methodVisitor;
+
+    classWriter.visit(V1_8, ACC_SUPER, "A", null, "java/lang/Object", null);
+
+    classWriter.visitSource("A.java", null);
+
+    {
+      fieldVisitor =
+          classWriter.visitField(ACC_PUBLIC | ACC_FINAL | ACC_STATIC, "A_1", "LA;", null, null);
+      fieldVisitor.visitEnd();
+    }
+    {
+      fieldVisitor =
+          classWriter.visitField(ACC_PUBLIC | ACC_FINAL | ACC_STATIC, "A_2", "LA;", null, null);
+      fieldVisitor.visitEnd();
+    }
+    {
+      fieldVisitor =
+          classWriter.visitField(ACC_PUBLIC | ACC_FINAL | ACC_STATIC, "A_3", "LA;", null, null);
+      fieldVisitor.visitEnd();
+    }
+    {
+      fieldVisitor = classWriter.visitField(ACC_PRIVATE | ACC_FINAL, "value", "I", null, null);
+      fieldVisitor.visitEnd();
+    }
+    {
+      fieldVisitor =
+          classWriter.visitField(ACC_PRIVATE | ACC_FINAL, "name", "Ljava/lang/String;", null, null);
+      fieldVisitor.visitEnd();
+    }
+    {
+      methodVisitor =
+          classWriter.visitMethod(
+              ACC_PRIVATE | ACC_STATIC, "createA", "(ILjava/lang/String;)LA;", null, null);
+      methodVisitor.visitCode();
+      Label label0 = new Label();
+      methodVisitor.visitLabel(label0);
+      methodVisitor.visitLineNumber(69, label0);
+      methodVisitor.visitTypeInsn(NEW, "A");
+      methodVisitor.visitInsn(DUP);
+      methodVisitor.visitVarInsn(ILOAD, 0);
+      methodVisitor.visitVarInsn(ALOAD, 1);
+      methodVisitor.visitMethodInsn(INVOKESPECIAL, "A", "<init>", "(ILjava/lang/String;)V", false);
+      methodVisitor.visitInsn(ARETURN);
+      Label label1 = new Label();
+      methodVisitor.visitLabel(label1);
+      methodVisitor.visitLocalVariable("value", "I", null, label0, label1, 0);
+      methodVisitor.visitLocalVariable("name", "Ljava/lang/String;", null, label0, label1, 1);
+      methodVisitor.visitMaxs(4, 2);
+      methodVisitor.visitEnd();
+    }
+    {
+      methodVisitor =
+          classWriter.visitMethod(ACC_PRIVATE, "<init>", "(ILjava/lang/String;)V", null, null);
+      methodVisitor.visitCode();
+      Label label0 = new Label();
+      methodVisitor.visitLabel(label0);
+      methodVisitor.visitLineNumber(72, label0);
+      methodVisitor.visitVarInsn(ALOAD, 0);
+      methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+      Label label1 = new Label();
+      methodVisitor.visitLabel(label1);
+      methodVisitor.visitLineNumber(73, label1);
+      methodVisitor.visitVarInsn(ALOAD, 0);
+      methodVisitor.visitVarInsn(ILOAD, 1);
+      methodVisitor.visitFieldInsn(PUTFIELD, "A", "value", "I");
+      Label label2 = new Label();
+      methodVisitor.visitLabel(label2);
+      methodVisitor.visitLineNumber(74, label2);
+      methodVisitor.visitVarInsn(ALOAD, 0);
+      methodVisitor.visitVarInsn(ALOAD, 2);
+      methodVisitor.visitFieldInsn(PUTFIELD, "A", "name", "Ljava/lang/String;");
+      Label label3 = new Label();
+      methodVisitor.visitLabel(label3);
+      methodVisitor.visitLineNumber(75, label3);
+      methodVisitor.visitInsn(RETURN);
+      Label label4 = new Label();
+      methodVisitor.visitLabel(label4);
+      methodVisitor.visitLocalVariable("this", "LA;", null, label0, label4, 0);
+      methodVisitor.visitLocalVariable("value", "I", null, label0, label4, 1);
+      methodVisitor.visitLocalVariable("name", "Ljava/lang/String;", null, label0, label4, 2);
+      methodVisitor.visitMaxs(2, 3);
+      methodVisitor.visitEnd();
+    }
+    {
+      methodVisitor = classWriter.visitMethod(0, "getValue", "()I", null, null);
+      methodVisitor.visitCode();
+      Label label0 = new Label();
+      methodVisitor.visitLabel(label0);
+      methodVisitor.visitLineNumber(78, label0);
+      methodVisitor.visitVarInsn(ALOAD, 0);
+      methodVisitor.visitFieldInsn(GETFIELD, "A", "value", "I");
+      methodVisitor.visitInsn(IRETURN);
+      Label label1 = new Label();
+      methodVisitor.visitLabel(label1);
+      methodVisitor.visitLocalVariable("this", "LA;", null, label0, label1, 0);
+      methodVisitor.visitMaxs(1, 1);
+      methodVisitor.visitEnd();
+    }
+    {
+      methodVisitor = classWriter.visitMethod(0, "getName", "()Ljava/lang/String;", null, null);
+      methodVisitor.visitCode();
+      Label label0 = new Label();
+      methodVisitor.visitLabel(label0);
+      methodVisitor.visitLineNumber(82, label0);
+      methodVisitor.visitVarInsn(ALOAD, 0);
+      methodVisitor.visitFieldInsn(GETFIELD, "A", "name", "Ljava/lang/String;");
+      methodVisitor.visitInsn(ARETURN);
+      Label label1 = new Label();
+      methodVisitor.visitLabel(label1);
+      methodVisitor.visitLocalVariable("this", "LA;", null, label0, label1, 0);
+      methodVisitor.visitMaxs(1, 1);
+      methodVisitor.visitEnd();
+    }
+    {
+      methodVisitor = classWriter.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
+      methodVisitor.visitCode();
+      Label label0 = new Label();
+      methodVisitor.visitLabel(label0);
+      methodVisitor.visitLineNumber(67, label0);
+      methodVisitor.visitInsn(ICONST_1);
+      Label label0x = new Label();
+      methodVisitor.visitLabel(label0x);
+      methodVisitor.visitLineNumber(68, label0x);
+      methodVisitor.visitLdcInsn("FIRST");
+      Label label1 = new Label();
+      methodVisitor.visitLabel(label1);
+      methodVisitor.visitLineNumber(69, label1);
+      methodVisitor.visitMethodInsn(
+          INVOKESTATIC, "A", "createA", "(ILjava/lang/String;)LA;", false);
+      methodVisitor.visitFieldInsn(PUTSTATIC, "A", "A_1", "LA;");
+      Label label2 = new Label();
+      methodVisitor.visitLabel(label2);
+      methodVisitor.visitLineNumber(70, label2);
+      methodVisitor.visitInsn(ICONST_1);
+      Label label2x = new Label();
+      methodVisitor.visitLabel(label2x);
+      methodVisitor.visitLineNumber(71, label2x);
+      methodVisitor.visitLdcInsn("SECOND");
+      Label label3 = new Label();
+      methodVisitor.visitLabel(label3);
+      methodVisitor.visitLineNumber(72, label3);
+      methodVisitor.visitMethodInsn(
+          INVOKESTATIC, "A", "createA", "(ILjava/lang/String;)LA;", false);
+      methodVisitor.visitFieldInsn(PUTSTATIC, "A", "A_2", "LA;");
+      Label label4 = new Label();
+      methodVisitor.visitLabel(label4);
+      methodVisitor.visitLineNumber(73, label4);
+      methodVisitor.visitInsn(ICONST_1);
+      Label label4x = new Label();
+      methodVisitor.visitLabel(label4x);
+      methodVisitor.visitLineNumber(74, label4x);
+      methodVisitor.visitLdcInsn("THIRD");
+      Label label5 = new Label();
+      methodVisitor.visitLabel(label5);
+      methodVisitor.visitLineNumber(75, label5);
+      methodVisitor.visitMethodInsn(
+          INVOKESTATIC, "A", "createA", "(ILjava/lang/String;)LA;", false);
+      methodVisitor.visitFieldInsn(PUTSTATIC, "A", "A_3", "LA;");
+      Label label6 = new Label();
+      methodVisitor.visitLabel(label6);
+      methodVisitor.visitLineNumber(74, label6);
+      methodVisitor.visitInsn(RETURN);
+      methodVisitor.visitMaxs(2, 0);
+      methodVisitor.visitEnd();
+    }
+    classWriter.visitEnd();
+
+    return classWriter.toByteArray();
+  }
+
+  /*
+    Dump of the compiled code for ths class below. The dump has not been modified, but the
+    code needs to have the specific line breaks for javac to insert the line numbers in the
+    way that this test is about. Used a dump to avoid source formatting invalidating the test.
+
+    This is the same pattern like class A above, just with more constants attributed to the
+    same line.
+
+    class B {
+      // The line break before createA is needed for the expected line info.
+      public static final B B_1 =
+          createB(1, 2, 3, "FIRST");
+      public static final B B_2 =
+          createB(1, 2, 3, "SECOND");
+      public static final B B_3 =
+          createB(1, 2, 3, "THIRD");
+
+      private final int value1;
+      private final int value2;
+      private final int value3;
+      private final String name;
+
+      private static B createB(int value1, int value2, int value3, String name) {
+        return new B(value1, value2, value3, name);
+      }
+
+      private B(int value1, int value2, int value3, String name) {
+        this.value1 = value1;
+        this.value2 = value2;
+        this.value3 = value3;
+        this.name = name;
+      }
+
+      int getValue1() {
+        return value1;
+      }
+
+      int getVlaue2() {
+        return value2;
+      }
+
+      int getValue3() {
+        return value3;
+      }
+
+      String getName() {
+        return name;
+      }
+    }
+  */
+  public static byte[] dumpMoreConsts() {
+
+    ClassWriter classWriter = new ClassWriter(0);
+    FieldVisitor fieldVisitor;
+    MethodVisitor methodVisitor;
+
+    classWriter.visit(V11, ACC_SUPER, "B", null, "java/lang/Object", null);
+
+    classWriter.visitSource("B.java", null);
+
+    {
+      fieldVisitor =
+          classWriter.visitField(ACC_PUBLIC | ACC_FINAL | ACC_STATIC, "B_1", "LB;", null, null);
+      fieldVisitor.visitEnd();
+    }
+    {
+      fieldVisitor =
+          classWriter.visitField(ACC_PUBLIC | ACC_FINAL | ACC_STATIC, "B_2", "LB;", null, null);
+      fieldVisitor.visitEnd();
+    }
+    {
+      fieldVisitor =
+          classWriter.visitField(ACC_PUBLIC | ACC_FINAL | ACC_STATIC, "B_3", "LB;", null, null);
+      fieldVisitor.visitEnd();
+    }
+    {
+      fieldVisitor = classWriter.visitField(ACC_PRIVATE | ACC_FINAL, "value1", "I", null, null);
+      fieldVisitor.visitEnd();
+    }
+    {
+      fieldVisitor = classWriter.visitField(ACC_PRIVATE | ACC_FINAL, "value2", "I", null, null);
+      fieldVisitor.visitEnd();
+    }
+    {
+      fieldVisitor = classWriter.visitField(ACC_PRIVATE | ACC_FINAL, "value3", "I", null, null);
+      fieldVisitor.visitEnd();
+    }
+    {
+      fieldVisitor =
+          classWriter.visitField(ACC_PRIVATE | ACC_FINAL, "name", "Ljava/lang/String;", null, null);
+      fieldVisitor.visitEnd();
+    }
+    {
+      methodVisitor =
+          classWriter.visitMethod(
+              ACC_PRIVATE | ACC_STATIC, "createB", "(IIILjava/lang/String;)LB;", null, null);
+      methodVisitor.visitCode();
+      Label label0 = new Label();
+      methodVisitor.visitLabel(label0);
+      methodVisitor.visitLineNumber(16, label0);
+      methodVisitor.visitTypeInsn(NEW, "B");
+      methodVisitor.visitInsn(DUP);
+      methodVisitor.visitVarInsn(ILOAD, 0);
+      methodVisitor.visitVarInsn(ILOAD, 1);
+      methodVisitor.visitVarInsn(ILOAD, 2);
+      methodVisitor.visitVarInsn(ALOAD, 3);
+      methodVisitor.visitMethodInsn(
+          INVOKESPECIAL, "B", "<init>", "(IIILjava/lang/String;)V", false);
+      methodVisitor.visitInsn(ARETURN);
+      Label label1 = new Label();
+      methodVisitor.visitLabel(label1);
+      methodVisitor.visitLocalVariable("value1", "I", null, label0, label1, 0);
+      methodVisitor.visitLocalVariable("value2", "I", null, label0, label1, 1);
+      methodVisitor.visitLocalVariable("value3", "I", null, label0, label1, 2);
+      methodVisitor.visitLocalVariable("name", "Ljava/lang/String;", null, label0, label1, 3);
+      methodVisitor.visitMaxs(6, 4);
+      methodVisitor.visitEnd();
+    }
+    {
+      methodVisitor =
+          classWriter.visitMethod(ACC_PRIVATE, "<init>", "(IIILjava/lang/String;)V", null, null);
+      methodVisitor.visitCode();
+      Label label0 = new Label();
+      methodVisitor.visitLabel(label0);
+      methodVisitor.visitLineNumber(19, label0);
+      methodVisitor.visitVarInsn(ALOAD, 0);
+      methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+      Label label1 = new Label();
+      methodVisitor.visitLabel(label1);
+      methodVisitor.visitLineNumber(20, label1);
+      methodVisitor.visitVarInsn(ALOAD, 0);
+      methodVisitor.visitVarInsn(ILOAD, 1);
+      methodVisitor.visitFieldInsn(PUTFIELD, "B", "value1", "I");
+      Label label2 = new Label();
+      methodVisitor.visitLabel(label2);
+      methodVisitor.visitLineNumber(21, label2);
+      methodVisitor.visitVarInsn(ALOAD, 0);
+      methodVisitor.visitVarInsn(ILOAD, 2);
+      methodVisitor.visitFieldInsn(PUTFIELD, "B", "value2", "I");
+      Label label3 = new Label();
+      methodVisitor.visitLabel(label3);
+      methodVisitor.visitLineNumber(22, label3);
+      methodVisitor.visitVarInsn(ALOAD, 0);
+      methodVisitor.visitVarInsn(ILOAD, 3);
+      methodVisitor.visitFieldInsn(PUTFIELD, "B", "value3", "I");
+      Label label4 = new Label();
+      methodVisitor.visitLabel(label4);
+      methodVisitor.visitLineNumber(23, label4);
+      methodVisitor.visitVarInsn(ALOAD, 0);
+      methodVisitor.visitVarInsn(ALOAD, 4);
+      methodVisitor.visitFieldInsn(PUTFIELD, "B", "name", "Ljava/lang/String;");
+      Label label5 = new Label();
+      methodVisitor.visitLabel(label5);
+      methodVisitor.visitLineNumber(24, label5);
+      methodVisitor.visitInsn(RETURN);
+      Label label6 = new Label();
+      methodVisitor.visitLabel(label6);
+      methodVisitor.visitLocalVariable("this", "LB;", null, label0, label6, 0);
+      methodVisitor.visitLocalVariable("value1", "I", null, label0, label6, 1);
+      methodVisitor.visitLocalVariable("value2", "I", null, label0, label6, 2);
+      methodVisitor.visitLocalVariable("value3", "I", null, label0, label6, 3);
+      methodVisitor.visitLocalVariable("name", "Ljava/lang/String;", null, label0, label6, 4);
+      methodVisitor.visitMaxs(2, 5);
+      methodVisitor.visitEnd();
+    }
+    {
+      methodVisitor = classWriter.visitMethod(0, "getValue1", "()I", null, null);
+      methodVisitor.visitCode();
+      Label label0 = new Label();
+      methodVisitor.visitLabel(label0);
+      methodVisitor.visitLineNumber(27, label0);
+      methodVisitor.visitVarInsn(ALOAD, 0);
+      methodVisitor.visitFieldInsn(GETFIELD, "B", "value1", "I");
+      methodVisitor.visitInsn(IRETURN);
+      Label label1 = new Label();
+      methodVisitor.visitLabel(label1);
+      methodVisitor.visitLocalVariable("this", "LB;", null, label0, label1, 0);
+      methodVisitor.visitMaxs(1, 1);
+      methodVisitor.visitEnd();
+    }
+    {
+      methodVisitor = classWriter.visitMethod(0, "getVlaue2", "()I", null, null);
+      methodVisitor.visitCode();
+      Label label0 = new Label();
+      methodVisitor.visitLabel(label0);
+      methodVisitor.visitLineNumber(31, label0);
+      methodVisitor.visitVarInsn(ALOAD, 0);
+      methodVisitor.visitFieldInsn(GETFIELD, "B", "value2", "I");
+      methodVisitor.visitInsn(IRETURN);
+      Label label1 = new Label();
+      methodVisitor.visitLabel(label1);
+      methodVisitor.visitLocalVariable("this", "LB;", null, label0, label1, 0);
+      methodVisitor.visitMaxs(1, 1);
+      methodVisitor.visitEnd();
+    }
+    {
+      methodVisitor = classWriter.visitMethod(0, "getValue3", "()I", null, null);
+      methodVisitor.visitCode();
+      Label label0 = new Label();
+      methodVisitor.visitLabel(label0);
+      methodVisitor.visitLineNumber(35, label0);
+      methodVisitor.visitVarInsn(ALOAD, 0);
+      methodVisitor.visitFieldInsn(GETFIELD, "B", "value3", "I");
+      methodVisitor.visitInsn(IRETURN);
+      Label label1 = new Label();
+      methodVisitor.visitLabel(label1);
+      methodVisitor.visitLocalVariable("this", "LB;", null, label0, label1, 0);
+      methodVisitor.visitMaxs(1, 1);
+      methodVisitor.visitEnd();
+    }
+    {
+      methodVisitor = classWriter.visitMethod(0, "getName", "()Ljava/lang/String;", null, null);
+      methodVisitor.visitCode();
+      Label label0 = new Label();
+      methodVisitor.visitLabel(label0);
+      methodVisitor.visitLineNumber(39, label0);
+      methodVisitor.visitVarInsn(ALOAD, 0);
+      methodVisitor.visitFieldInsn(GETFIELD, "B", "name", "Ljava/lang/String;");
+      methodVisitor.visitInsn(ARETURN);
+      Label label1 = new Label();
+      methodVisitor.visitLabel(label1);
+      methodVisitor.visitLocalVariable("this", "LB;", null, label0, label1, 0);
+      methodVisitor.visitMaxs(1, 1);
+      methodVisitor.visitEnd();
+    }
+    {
+      methodVisitor = classWriter.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
+      methodVisitor.visitCode();
+      Label label0 = new Label();
+      methodVisitor.visitLabel(label0);
+      methodVisitor.visitLineNumber(3, label0);
+      methodVisitor.visitInsn(ICONST_1);
+      methodVisitor.visitInsn(ICONST_2);
+      methodVisitor.visitInsn(ICONST_3);
+      methodVisitor.visitLdcInsn("FIRST");
+      Label label1 = new Label();
+      methodVisitor.visitLabel(label1);
+      methodVisitor.visitLineNumber(4, label1);
+      methodVisitor.visitMethodInsn(
+          INVOKESTATIC, "B", "createB", "(IIILjava/lang/String;)LB;", false);
+      methodVisitor.visitFieldInsn(PUTSTATIC, "B", "B_1", "LB;");
+      Label label2 = new Label();
+      methodVisitor.visitLabel(label2);
+      methodVisitor.visitLineNumber(5, label2);
+      methodVisitor.visitInsn(ICONST_1);
+      methodVisitor.visitInsn(ICONST_2);
+      methodVisitor.visitInsn(ICONST_3);
+      methodVisitor.visitLdcInsn("SECOND");
+      Label label3 = new Label();
+      methodVisitor.visitLabel(label3);
+      methodVisitor.visitLineNumber(6, label3);
+      methodVisitor.visitMethodInsn(
+          INVOKESTATIC, "B", "createB", "(IIILjava/lang/String;)LB;", false);
+      methodVisitor.visitFieldInsn(PUTSTATIC, "B", "B_2", "LB;");
+      Label label4 = new Label();
+      methodVisitor.visitLabel(label4);
+      methodVisitor.visitLineNumber(7, label4);
+      methodVisitor.visitInsn(ICONST_1);
+      methodVisitor.visitInsn(ICONST_2);
+      methodVisitor.visitInsn(ICONST_3);
+      methodVisitor.visitLdcInsn("THIRD");
+      Label label5 = new Label();
+      methodVisitor.visitLabel(label5);
+      methodVisitor.visitLineNumber(8, label5);
+      methodVisitor.visitMethodInsn(
+          INVOKESTATIC, "B", "createB", "(IIILjava/lang/String;)LB;", false);
+      methodVisitor.visitFieldInsn(PUTSTATIC, "B", "B_3", "LB;");
+      Label label6 = new Label();
+      methodVisitor.visitLabel(label6);
+      methodVisitor.visitLineNumber(7, label6);
+      methodVisitor.visitInsn(RETURN);
+      methodVisitor.visitMaxs(4, 0);
+      methodVisitor.visitEnd();
+    }
+    classWriter.visitEnd();
+
+    return classWriter.toByteArray();
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/InvokeStaticInterfaceNestedTest.java b/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/InvokeStaticInterfaceNestedTest.java
new file mode 100644
index 0000000..667116e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/InvokeStaticInterfaceNestedTest.java
@@ -0,0 +1,110 @@
+// Copyright (c) 2020, 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.desugar.staticinterfacemethod;
+
+import static com.android.tools.r8.desugar.staticinterfacemethod.InvokeStaticInterfaceNestedTest.Library.foo;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.SingleTestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class InvokeStaticInterfaceNestedTest extends TestBase {
+
+  private final TestParameters parameters;
+  private final boolean cfToCfDesugar;
+  private final String EXPECTED = "Hello World!";
+
+  @Parameters(name = "{0}, desugar: {1}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
+  }
+
+  public InvokeStaticInterfaceNestedTest(TestParameters parameters, boolean cfToCfDesugar) {
+    this.parameters = parameters;
+    this.cfToCfDesugar = cfToCfDesugar;
+  }
+
+  @Test
+  public void testRuntime() throws Exception {
+    final SingleTestRunResult<?> runResult =
+        testForRuntime(parameters)
+            .addProgramClassFileData(
+                rewriteToUseNonInterfaceMethodReference(Main.class, "main"),
+                rewriteToUseNonInterfaceMethodReference(Library.class, "foo"))
+            .run(parameters.getRuntime(), Main.class);
+    if (parameters.isCfRuntime() && parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK9)) {
+      runResult.assertFailureWithErrorThatMatches(containsString("IncompatibleClassChangeError"));
+    } else {
+      // TODO(b/166247515): This should be ICCE.
+      runResult.assertSuccessWithOutputLines(EXPECTED);
+    }
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    final R8FullTestBuilder testBuilder =
+        testForR8(parameters.getBackend())
+            .addProgramClassFileData(
+                rewriteToUseNonInterfaceMethodReference(Main.class, "main"),
+                rewriteToUseNonInterfaceMethodReference(Library.class, "foo"))
+            .addKeepAllClassesRule()
+            .setMinApi(parameters.getApiLevel())
+            .addKeepMainRule(Main.class)
+            .addOptionsModification(
+                options -> {
+                  options.cfToCfDesugar = cfToCfDesugar;
+                });
+    if (parameters.isCfRuntime()) {
+      // TODO(b/166213037): Should not throw an error.
+      assertThrows(CompilationFailedException.class, testBuilder::compile);
+    } else {
+      testBuilder.run(parameters.getRuntime(), Main.class).assertSuccessWithOutputLines(EXPECTED);
+    }
+  }
+
+  private byte[] rewriteToUseNonInterfaceMethodReference(Class<?> clazz, String methodName)
+      throws Exception {
+    return transformer(clazz)
+        .transformMethodInsnInMethod(
+            methodName,
+            (opcode, owner, name, descriptor, isInterface, visitor) -> {
+              assertTrue(isInterface);
+              visitor.visitMethodInsn(opcode, owner, name, descriptor, false);
+            })
+        .transform();
+  }
+
+  public interface Library {
+
+    static void foo() {
+      bar(); // <-- will be rewritten to invoke-static Library::bar();
+    }
+
+    static void bar() {
+      System.out.println("Hello World!");
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      foo(); // <-- will be rewritten to invoke-static Library::foo();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumWithNonDefaultForwardingConstructorTest.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumWithNonDefaultForwardingConstructorTest.java
new file mode 100644
index 0000000..92b0b54
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumWithNonDefaultForwardingConstructorTest.java
@@ -0,0 +1,96 @@
+// Copyright (c) 2020, 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.enumunboxing;
+
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.transformers.MethodTransformer;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class EnumWithNonDefaultForwardingConstructorTest extends TestBase {
+
+  private final boolean enableEnumUnboxing;
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{1}, enum unboxing: {0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
+  }
+
+  public EnumWithNonDefaultForwardingConstructorTest(
+      boolean enableEnumUnboxing, TestParameters parameters) {
+    this.enableEnumUnboxing = enableEnumUnboxing;
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testJvm() throws Exception {
+    assumeFalse(enableEnumUnboxing);
+    assumeTrue(parameters.isCfRuntime());
+    testForJvm()
+        .apply(this::addProgramClasses)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("42");
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .apply(this::addProgramClasses)
+        .addKeepMainRule(TestClass.class)
+        .addOptionsModification(options -> options.enableEnumUnboxing = enableEnumUnboxing)
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .run(parameters.getRuntime(), TestClass.class)
+        // TODO(b/160939354): Should succeed with 42.
+        .assertSuccessWithOutputLines(enableEnumUnboxing ? "0" : "42");
+  }
+
+  private void addProgramClasses(TestBuilder<?, ?> builder) throws Exception {
+    builder
+        .addProgramClasses(TestClass.class)
+        .addProgramClassFileData(
+            transformer(MyEnum.class)
+                .addMethodTransformer(
+                    new MethodTransformer() {
+                      @Override
+                      public void visitVarInsn(int opcode, int var) {
+                        if (getContext().getReference().getMethodName().equals("<init>")) {
+                          // Pass the value of the third argument (ignoring the receiver) to the
+                          // parent constructor instead of the value of the second argument.
+                          super.visitVarInsn(opcode, var == 2 ? 3 : var);
+                          return;
+                        }
+                        super.visitVarInsn(opcode, var);
+                      }
+                    })
+                .transform());
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      MyEnum instance = System.currentTimeMillis() > 0 ? MyEnum.A : MyEnum.B;
+      System.out.println(instance.ordinal());
+    }
+  }
+
+  enum MyEnum {
+    A(42),
+    B(43);
+
+    MyEnum(int ordinal) {}
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/graph/DexDebugEventsTest.java b/src/test/java/com/android/tools/r8/graph/DexDebugEventsTest.java
index b6e6c2e..75eb79f 100644
--- a/src/test/java/com/android/tools/r8/graph/DexDebugEventsTest.java
+++ b/src/test/java/com/android/tools/r8/graph/DexDebugEventsTest.java
@@ -137,7 +137,8 @@
         nextPc,
         new Position(nextLine, null, method, null),
         events,
-        factory);
+        factory,
+        false);
     consumer.accept(events);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialForInvokeVirtualTest.java b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialForInvokeVirtualTest.java
index eaf7d56..2b0324c 100644
--- a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialForInvokeVirtualTest.java
+++ b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialForInvokeVirtualTest.java
@@ -4,17 +4,16 @@
 
 package com.android.tools.r8.graph.invokespecial;
 
+import static com.android.tools.r8.utils.DescriptorUtils.getBinaryNameFromJavaType;
 import static org.junit.Assert.assertEquals;
 import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
 import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
 
-import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.utils.StringUtils;
 import java.io.IOException;
-import java.util.concurrent.ExecutionException;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -24,6 +23,8 @@
 @RunWith(Parameterized.class)
 public class InvokeSpecialForInvokeVirtualTest extends TestBase {
 
+  private static final String EXPECTED = StringUtils.lines("Hello World!");
+
   private final TestParameters parameters;
 
   @Parameters(name = "{0}")
@@ -36,13 +37,24 @@
   }
 
   @Test
-  public void testRuntime() throws IOException, CompilationFailedException, ExecutionException {
-    TestRunResult<?> runResult =
-        testForRuntime(parameters.getRuntime(), parameters.getApiLevel())
-            .addProgramClasses(A.class, Main.class)
-            .addProgramClassFileData(getClassBWithTransformedInvoked())
-            .run(parameters.getRuntime(), Main.class)
-            .assertSuccessWithOutputLines("Hello World!");
+  public void testRuntime() throws Exception {
+    testForRuntime(parameters.getRuntime(), parameters.getApiLevel())
+        .addProgramClasses(A.class, Main.class)
+        .addProgramClassFileData(getClassBWithTransformedInvoked())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(A.class, Main.class)
+        .addProgramClassFileData(getClassBWithTransformedInvoked())
+        .addKeepMainRule(Main.class)
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Main.class)
+        // TODO(b/166210854): Fails but should not.
+        .assertFailure();
   }
 
   private byte[] getClassBWithTransformedInvoked() throws IOException {
@@ -51,6 +63,7 @@
             "bar",
             (opcode, owner, name, descriptor, isInterface, continuation) -> {
               assertEquals(INVOKEVIRTUAL, opcode);
+              assertEquals(getBinaryNameFromJavaType(B.class.getTypeName()), owner);
               continuation.visitMethodInsn(INVOKESPECIAL, owner, name, descriptor, isInterface);
             })
         .transform();
@@ -66,7 +79,7 @@
   public static class B extends A {
 
     void bar() {
-      foo();
+      foo(); // Will be invoke-special B::foo.
     }
   }
 
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialForNonDeclaredInvokeVirtualTest.java b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialForNonDeclaredInvokeVirtualTest.java
index 9b9a8eb..b9156fb 100644
--- a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialForNonDeclaredInvokeVirtualTest.java
+++ b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialForNonDeclaredInvokeVirtualTest.java
@@ -4,17 +4,16 @@
 
 package com.android.tools.r8.graph.invokespecial;
 
+import static com.android.tools.r8.utils.DescriptorUtils.getBinaryNameFromJavaType;
 import static org.junit.Assert.assertEquals;
 import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
 import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
 
-import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.utils.StringUtils;
 import java.io.IOException;
-import java.util.concurrent.ExecutionException;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -24,6 +23,8 @@
 @RunWith(Parameterized.class)
 public class InvokeSpecialForNonDeclaredInvokeVirtualTest extends TestBase {
 
+  private static final String EXPECTED = StringUtils.lines("Hello World!");
+
   private final TestParameters parameters;
 
   @Parameters(name = "{0}")
@@ -36,13 +37,24 @@
   }
 
   @Test
-  public void testRuntime() throws IOException, CompilationFailedException, ExecutionException {
-    TestRunResult<?> runResult =
-        testForRuntime(parameters.getRuntime(), parameters.getApiLevel())
-            .addProgramClasses(A.class, B.class, Main.class)
-            .addProgramClassFileData(getClassCWithTransformedInvoked())
-            .run(parameters.getRuntime(), Main.class)
-            .assertSuccessWithOutputLines("Hello World!");
+  public void testRuntime() throws Exception {
+    testForRuntime(parameters.getRuntime(), parameters.getApiLevel())
+        .addProgramClasses(A.class, B.class, Main.class)
+        .addProgramClassFileData(getClassCWithTransformedInvoked())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(A.class, B.class, Main.class)
+        .addProgramClassFileData(getClassCWithTransformedInvoked())
+        .addKeepMainRule(Main.class)
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Main.class)
+        // TODO(b/166210854): Fails but should not.
+        .assertFailure();
   }
 
   private byte[] getClassCWithTransformedInvoked() throws IOException {
@@ -51,6 +63,7 @@
             "bar",
             (opcode, owner, name, descriptor, isInterface, continuation) -> {
               assertEquals(INVOKEVIRTUAL, opcode);
+              assertEquals(getBinaryNameFromJavaType(C.class.getTypeName()), owner);
               continuation.visitMethodInsn(INVOKESPECIAL, owner, name, descriptor, isInterface);
             })
         .transform();
@@ -68,7 +81,7 @@
   public static class C extends B {
 
     void bar() {
-      foo();
+      foo(); // Will be invoke-special C::foo
     }
   }
 
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceTest.java b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceTest.java
index 7a51803..5119689 100644
--- a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceTest.java
@@ -57,6 +57,8 @@
     }
   }
 
+  // TODO(b/166210854): Test behavior on R8 too.
+
   private byte[] getClassWithTransformedInvoked() throws IOException {
     return transformer(B.class)
         .transformMethodInsnInMethod(
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceWithBridge2Test.java b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceWithBridge2Test.java
new file mode 100644
index 0000000..7d666aa
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceWithBridge2Test.java
@@ -0,0 +1,96 @@
+// Copyright (c) 2019, 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.invokespecial;
+
+import static com.android.tools.r8.utils.DescriptorUtils.getBinaryNameFromJavaType;
+import static org.junit.Assert.assertEquals;
+import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
+import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import java.io.IOException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class InvokeSpecialInterfaceWithBridge2Test extends TestBase {
+
+  private static final String EXPECTED = StringUtils.lines("Hello World!");
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public InvokeSpecialInterfaceWithBridge2Test(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testRuntime() throws Exception {
+    testForRuntime(parameters.getRuntime(), parameters.getApiLevel())
+        .addProgramClasses(I.class, A.class, Main.class)
+        .addProgramClassFileData(getClassWithTransformedInvoked())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(I.class, A.class, Main.class)
+        .addProgramClassFileData(getClassWithTransformedInvoked())
+        .addKeepMainRule(Main.class)
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  private byte[] getClassWithTransformedInvoked() throws IOException {
+    return transformer(B.class)
+        .transformMethodInsnInMethod(
+            "bar",
+            (opcode, owner, name, descriptor, isInterface, continuation) -> {
+              assertEquals(INVOKEVIRTUAL, opcode);
+              assertEquals(owner, getBinaryNameFromJavaType(B.class.getTypeName()));
+              continuation.visitMethodInsn(
+                  INVOKESPECIAL,
+                  getBinaryNameFromJavaType(A.class.getTypeName()),
+                  name,
+                  descriptor,
+                  isInterface);
+            })
+        .transform();
+  }
+
+  public interface I {
+    default void foo() {
+      System.out.println("Hello World!");
+    }
+  }
+
+  public static class A implements I {}
+
+  public static class B extends A {
+
+    public void bar() {
+      foo(); // Will be rewritten to invoke-special A.foo()
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      new B().bar();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceWithBridge3Test.java b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceWithBridge3Test.java
new file mode 100644
index 0000000..e5b0bb3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceWithBridge3Test.java
@@ -0,0 +1,108 @@
+// Copyright (c) 2019, 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.invokespecial;
+
+import static com.android.tools.r8.utils.DescriptorUtils.getBinaryNameFromJavaType;
+import static org.junit.Assert.assertEquals;
+import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
+import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import java.io.IOException;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class InvokeSpecialInterfaceWithBridge3Test extends TestBase {
+
+  private static final String EXPECTED = StringUtils.lines("Hello World!");
+
+  private final TestParameters parameters;
+  private final boolean isInterface;
+
+  @Parameters(name = "{0}, itf:{1}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
+  }
+
+  public InvokeSpecialInterfaceWithBridge3Test(TestParameters parameters, boolean isInterface) {
+    this.parameters = parameters;
+    this.isInterface = isInterface;
+  }
+
+  @Test
+  public void testRuntime() throws Exception {
+    TestRunResult<?> result =
+        testForRuntime(parameters.getRuntime(), parameters.getApiLevel())
+            .addProgramClasses(I.class, A.class, Main.class)
+            .addProgramClassFileData(getClassWithTransformedInvoked())
+            .run(parameters.getRuntime(), Main.class);
+    if (parameters.isDexRuntime()) {
+      // TODO(b/166210854): Runs but should have failed.
+      result.assertSuccessWithOutput(EXPECTED);
+    } else {
+      result.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
+    }
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(I.class, A.class, Main.class)
+        .addProgramClassFileData(getClassWithTransformedInvoked())
+        .addKeepMainRule(Main.class)
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Main.class)
+        // TODO(b/166210854): Runs but should have failed.
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  private byte[] getClassWithTransformedInvoked() throws IOException {
+    return transformer(B.class)
+        .transformMethodInsnInMethod(
+            "bar",
+            (opcode, owner, name, descriptor, isInterface, continuation) -> {
+              assertEquals(INVOKEVIRTUAL, opcode);
+              assertEquals(owner, getBinaryNameFromJavaType(B.class.getTypeName()));
+              continuation.visitMethodInsn(
+                  INVOKESPECIAL,
+                  getBinaryNameFromJavaType(I.class.getTypeName()),
+                  name,
+                  descriptor,
+                  isInterface);
+            })
+        .transform();
+  }
+
+  public interface I {
+    default void foo() {
+      System.out.println("Hello World!");
+    }
+  }
+
+  public static class A implements I {}
+
+  public static class B extends A {
+
+    public void bar() {
+      foo(); // Will be rewritten to invoke-special I.foo()
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      new B().bar();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceWithBridgeTest.java b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceWithBridgeTest.java
index 3536adb..31ec2c6 100644
--- a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceWithBridgeTest.java
+++ b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceWithBridgeTest.java
@@ -8,14 +8,11 @@
 import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
 import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
 
-import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.TestRunResult;
 import com.android.tools.r8.utils.DescriptorUtils;
 import java.io.IOException;
-import java.util.concurrent.ExecutionException;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -37,13 +34,24 @@
   }
 
   @Test
-  public void testRuntime() throws IOException, CompilationFailedException, ExecutionException {
-    TestRunResult<?> runResult =
-        testForRuntime(parameters.getRuntime(), parameters.getApiLevel())
-            .addProgramClasses(I.class, A.class, Main.class)
-            .addProgramClassFileData(getClassWithTransformedInvoked())
-            .run(parameters.getRuntime(), Main.class)
-            .assertSuccessWithOutputLines("Hello World!");
+  public void testRuntime() throws Exception {
+    testForRuntime(parameters.getRuntime(), parameters.getApiLevel())
+        .addProgramClasses(I.class, A.class, Main.class)
+        .addProgramClassFileData(getClassWithTransformedInvoked())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Hello World!");
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(I.class, A.class, Main.class)
+        .addProgramClassFileData(getClassWithTransformedInvoked())
+        .addKeepMainRule(Main.class)
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Main.class)
+        // TODO(b/166210854): Fails but should not.
+        .assertFailure();
   }
 
   private byte[] getClassWithTransformedInvoked() throws IOException {
@@ -69,7 +77,7 @@
   public static class B extends A {
 
     public void bar() {
-      foo(); // Will be rewritten to invoke-special A.foo()
+      foo(); // Will be rewritten to invoke-special B.foo()
     }
   }
 
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialMissingInvokeVirtualTest.java b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialMissingInvokeVirtualTest.java
index 93a94e9..fe03361 100644
--- a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialMissingInvokeVirtualTest.java
+++ b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialMissingInvokeVirtualTest.java
@@ -9,14 +9,11 @@
 import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
 import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
 
-import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.TestRunResult;
 import com.android.tools.r8.utils.DescriptorUtils;
 import java.io.IOException;
-import java.util.concurrent.ExecutionException;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -38,13 +35,23 @@
   }
 
   @Test
-  public void testRuntime() throws IOException, CompilationFailedException, ExecutionException {
-    TestRunResult<?> runResult =
-        testForRuntime(parameters.getRuntime(), parameters.getApiLevel())
-            .addProgramClasses(A.class, Main.class)
-            .addProgramClassFileData(getClassWithTransformedInvoked())
-            .run(parameters.getRuntime(), Main.class)
-            .assertFailureWithErrorThatMatches(containsString("NoSuchMethodError"));
+  public void testRuntime() throws Exception {
+    testForRuntime(parameters.getRuntime(), parameters.getApiLevel())
+        .addProgramClasses(A.class, Main.class)
+        .addProgramClassFileData(getClassWithTransformedInvoked())
+        .run(parameters.getRuntime(), Main.class)
+        .assertFailureWithErrorThatMatches(containsString("NoSuchMethodError"));
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(A.class, Main.class)
+        .addProgramClassFileData(getClassWithTransformedInvoked())
+        .addKeepMainRule(Main.class)
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Main.class)
+        .assertFailureWithErrorThatMatches(containsString("NoSuchMethodError"));
   }
 
   private byte[] getClassWithTransformedInvoked() throws IOException {
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialOnOtherInterfaceTest.java b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialOnOtherInterfaceTest.java
new file mode 100644
index 0000000..183ae6b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialOnOtherInterfaceTest.java
@@ -0,0 +1,93 @@
+// Copyright (c) 2019, 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.invokespecial;
+
+import static com.android.tools.r8.utils.DescriptorUtils.getBinaryNameFromJavaType;
+import static org.junit.Assert.assertEquals;
+import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import java.io.IOException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class InvokeSpecialOnOtherInterfaceTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public InvokeSpecialOnOtherInterfaceTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testRuntime() throws Exception {
+    testForRuntime(parameters.getRuntime(), parameters.getApiLevel())
+        .addProgramClasses(I.class, Main.class)
+        .addProgramClassFileData(getClassWithTransformedInvoked())
+        .run(parameters.getRuntime(), Main.class)
+        .assertFailureWithErrorThatThrows(
+            parameters.isCfRuntime()
+                ? VerifyError.class
+                // TODO(b/144410139): Consider making this a compilation failure.
+                : NoClassDefFoundError.class);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(I.class, Main.class)
+        .addProgramClassFileData(getClassWithTransformedInvoked())
+        .addKeepMainRule(Main.class)
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), Main.class)
+        .assertFailureWithErrorThatThrows(
+            parameters.isCfRuntime()
+                ? VerifyError.class
+                // TODO(b/144410139): Consider making this a compilation failure.
+                : NoClassDefFoundError.class);
+  }
+
+  private byte[] getClassWithTransformedInvoked() throws IOException {
+    return transformer(A.class)
+        .transformMethodInsnInMethod(
+            "bar",
+            (opcode, owner, name, descriptor, isInterface, continuation) -> {
+              assertEquals(getBinaryNameFromJavaType(I.class.getTypeName()), owner);
+              continuation.visitMethodInsn(INVOKESPECIAL, owner, name, descriptor, isInterface);
+            })
+        .transform();
+  }
+
+  public interface I {
+
+    default void foo() {
+      System.out.println("Hello World!");
+    }
+  }
+
+  public static class A {
+
+    public void bar(I i) {
+      i.foo(); // Will be rewritten to invoke-special I.foo()
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      new A().bar(new I() {});
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialOnSameClassTest.java b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialOnSameClassTest.java
index 5bc6d22..48790e3 100644
--- a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialOnSameClassTest.java
+++ b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialOnSameClassTest.java
@@ -14,7 +14,6 @@
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import java.io.IOException;
-import java.util.concurrent.ExecutionException;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -35,7 +34,7 @@
   }
 
   @Test
-  public void testRuntime() throws IOException, CompilationFailedException, ExecutionException {
+  public void testRuntime() throws Exception {
     try {
       testForRuntime(parameters.getRuntime(), parameters.getApiLevel())
           .addProgramClasses(Main.class)
@@ -51,6 +50,25 @@
     }
   }
 
+  @Test
+  public void testR8() throws Exception {
+    try {
+      testForR8(parameters.getBackend())
+          .addProgramClasses(Main.class)
+          .addProgramClassFileData(getClassWithTransformedInvoked())
+          .addKeepMainRule(Main.class)
+          .setMinApi(parameters.getApiLevel())
+          .run(parameters.getRuntime(), Main.class)
+          .assertSuccessWithOutputLines("Hello World!");
+      // TODO(b/110175213): Remove when fixed.
+      assertTrue(parameters.isCfRuntime());
+    } catch (CompilationFailedException compilation) {
+      assertThat(
+          compilation.getCause().getMessage(),
+          containsString("Failed to compile unsupported use of invokespecial"));
+    }
+  }
+
   private byte[] getClassWithTransformedInvoked() throws IOException {
     return transformer(A.class)
         .transformMethodInsnInMethod(
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialToVirtualMethodTest.java b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialToVirtualMethodTest.java
index 730ae81..400e015 100644
--- a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialToVirtualMethodTest.java
+++ b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialToVirtualMethodTest.java
@@ -47,6 +47,7 @@
         .assertSuccessWithOutput(EXPECTED);
   }
 
+
   @Test(expected = CompilationFailedException.class)
   public void testD8() throws Exception {
     assumeTrue(parameters.isDexRuntime());
@@ -62,6 +63,21 @@
                         diagnosticMessage(containsString("unsupported use of invokespecial"))));
   }
 
+  @Test(expected = CompilationFailedException.class)
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(Base.class, Bar.class, TestClass.class)
+        .addProgramClassFileData(getFooTransform())
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(TestClass.class)
+        .compileWithExpectedDiagnostics(
+            diagnostics ->
+                diagnostics
+                    .assertOnlyErrors()
+                    .assertErrorsMatch(
+                        diagnosticMessage(containsString("unsupported use of invokespecial"))));
+  }
+
   @Test
   public void testDX() throws Exception {
     assumeTrue(parameters.isDexRuntime());
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineMappingOnSameLineTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineMappingOnSameLineTest.java
new file mode 100644
index 0000000..a8abf65
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineMappingOnSameLineTest.java
@@ -0,0 +1,82 @@
+// Copyright (c) 2020, 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.optimize.inliner;
+
+import static com.android.tools.r8.naming.retrace.StackTrace.isSame;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.naming.retrace.RetraceTestBase;
+import com.android.tools.r8.naming.retrace.StackTrace;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class InlineMappingOnSameLineTest extends RetraceTestBase {
+
+  @Parameters(name = "{0}, mode: {1}, compat: {2}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withDexRuntimes().withAllApiLevels().build(),
+        CompilationMode.values(),
+        BooleanUtils.values());
+  }
+
+  public InlineMappingOnSameLineTest(
+      TestParameters parameters, CompilationMode mode, boolean isCompat) {
+    super(parameters, mode, isCompat);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    runTest(
+        ImmutableList.of("-keepattributes SourceFile,LineNumberTable"),
+        (StackTrace actualStackTrace, StackTrace retracedStackTrace) -> {
+          assertThat(retracedStackTrace, isSame(expectedStackTrace));
+        });
+  }
+
+  @Override
+  public Class<?> getMainClass() {
+    return Main.class;
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      foo(args.length);
+    }
+
+    @NeverInline
+    public static void foo(int arg) {
+      bar(arg);
+      f(arg);
+    }
+
+    public static void bar(int arg) {
+      f(arg);
+      g(arg);
+    }
+
+    public static void f(int arg) {
+      if (arg == 0) {
+        throw new RuntimeException("In f()");
+      }
+    }
+
+    public static void g(int arg) {
+      if (arg == 1) {
+        throw new RuntimeException("In g()");
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/DesugarStaticInterfaceMethodsRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/DesugarStaticInterfaceMethodDirectRetraceTest.java
similarity index 71%
copy from src/test/java/com/android/tools/r8/naming/retrace/DesugarStaticInterfaceMethodsRetraceTest.java
copy to src/test/java/com/android/tools/r8/naming/retrace/DesugarStaticInterfaceMethodDirectRetraceTest.java
index f049056..e505e23 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/DesugarStaticInterfaceMethodsRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/DesugarStaticInterfaceMethodDirectRetraceTest.java
@@ -9,6 +9,7 @@
 
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestBuilder;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.google.common.collect.ImmutableList;
@@ -19,7 +20,7 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class DesugarStaticInterfaceMethodsRetraceTest extends RetraceTestBase {
+public class DesugarStaticInterfaceMethodDirectRetraceTest extends RetraceTestBase {
 
   @Parameters(name = "{0}, mode: {1}, compat: {2}")
   public static Collection<Object[]> data() {
@@ -29,20 +30,24 @@
         BooleanUtils.values());
   }
 
-  public DesugarStaticInterfaceMethodsRetraceTest(
+  public DesugarStaticInterfaceMethodDirectRetraceTest(
       TestParameters parameters, CompilationMode mode, boolean compat) {
     super(parameters, mode, compat);
   }
 
   @Override
   public Collection<Class<?>> getClasses() {
-    return ImmutableList.of(
-        getMainClass(), InterfaceWithStaticMethod1.class, InterfaceWithStaticMethod2.class);
+    return ImmutableList.of(getMainClass(), InterfaceWithStaticMethod.class);
   }
 
   @Override
   public Class<?> getMainClass() {
-    return MainDesugarStaticInterfaceMethodsRetraceTest.class;
+    return MainDesugarStaticInterfaceMethodRetraceTest.class;
+  }
+
+  @Override
+  public void configure(R8TestBuilder<?> builder) {
+    builder.enableInliningAnnotations();
   }
 
   @Test
@@ -52,33 +57,26 @@
         // For the desugaring to companion classes the retrace stacktrace is still the same
         // as the mapping file has a fully qualified class name in the method mapping, e.g.:
         //
-        // com.android.tools.r8.naming.retrace.InterfaceWithDefaultMethod1$-CC
+        // com.android.tools.r8.naming.retrace.InterfaceWithStaticMethod$-CC
         //   -> com.android.tools.r8.naming.retrace.a:1:1:void
-        // com.android.tools.r8.naming.retrace.InterfaceWithDefaultMethod1.defaultMethod1():80:80
+        // com.android.tools.r8.naming.retrace.InterfaceWithStaticMethod$.staticMethod():80:80
         //   -> a
         (StackTrace actualStackTrace, StackTrace retracedStackTrace) ->
             assertThat(retracedStackTrace, isSameExceptForFileName(expectedStackTrace)));
   }
 }
 
-interface InterfaceWithStaticMethod2 {
+interface InterfaceWithStaticMethod {
 
-  static void staticMethod1() {
+  @NeverInline
+  static void staticMethod() {
     throw null;
   }
 }
 
-interface InterfaceWithStaticMethod1 {
-
-  @NeverInline
-  static void staticMethod2() {
-    InterfaceWithStaticMethod2.staticMethod1();
-  }
-}
-
-class MainDesugarStaticInterfaceMethodsRetraceTest {
+class MainDesugarStaticInterfaceMethodRetraceTest {
 
   public static void main(String[] args) {
-    InterfaceWithStaticMethod1.staticMethod2();
+    InterfaceWithStaticMethod.staticMethod();
   }
 }
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/DesugarStaticInterfaceMethodsRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/DesugarStaticInterfaceMethodsInlineIntoStaticRetraceTest.java
similarity index 86%
rename from src/test/java/com/android/tools/r8/naming/retrace/DesugarStaticInterfaceMethodsRetraceTest.java
rename to src/test/java/com/android/tools/r8/naming/retrace/DesugarStaticInterfaceMethodsInlineIntoStaticRetraceTest.java
index f049056..7904ede 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/DesugarStaticInterfaceMethodsRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/DesugarStaticInterfaceMethodsInlineIntoStaticRetraceTest.java
@@ -9,6 +9,7 @@
 
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestBuilder;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.google.common.collect.ImmutableList;
@@ -19,7 +20,7 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class DesugarStaticInterfaceMethodsRetraceTest extends RetraceTestBase {
+public class DesugarStaticInterfaceMethodsInlineIntoStaticRetraceTest extends RetraceTestBase {
 
   @Parameters(name = "{0}, mode: {1}, compat: {2}")
   public static Collection<Object[]> data() {
@@ -29,12 +30,17 @@
         BooleanUtils.values());
   }
 
-  public DesugarStaticInterfaceMethodsRetraceTest(
+  public DesugarStaticInterfaceMethodsInlineIntoStaticRetraceTest(
       TestParameters parameters, CompilationMode mode, boolean compat) {
     super(parameters, mode, compat);
   }
 
   @Override
+  public void configure(R8TestBuilder<?> builder) {
+    builder.enableInliningAnnotations();
+  }
+
+  @Override
   public Collection<Class<?>> getClasses() {
     return ImmutableList.of(
         getMainClass(), InterfaceWithStaticMethod1.class, InterfaceWithStaticMethod2.class);
@@ -63,7 +69,7 @@
 
 interface InterfaceWithStaticMethod2 {
 
-  static void staticMethod1() {
+  static void staticMethod2() {
     throw null;
   }
 }
@@ -71,14 +77,14 @@
 interface InterfaceWithStaticMethod1 {
 
   @NeverInline
-  static void staticMethod2() {
-    InterfaceWithStaticMethod2.staticMethod1();
+  static void staticMethod1() {
+    InterfaceWithStaticMethod2.staticMethod2();
   }
 }
 
 class MainDesugarStaticInterfaceMethodsRetraceTest {
 
   public static void main(String[] args) {
-    InterfaceWithStaticMethod1.staticMethod2();
+    InterfaceWithStaticMethod1.staticMethod1();
   }
 }
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java b/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
index 4dd2fb0..0c4f597 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
@@ -233,23 +233,6 @@
     // \tat com.android.tools.r8.naming.retrace.Main.a(:150)
     // \tat com.android.tools.r8.naming.retrace.Main.a(:156)
     // \tat com.android.tools.r8.naming.retrace.Main.main(:162)
-    // TODO(122940268): Remove test code when fixed.
-    System.out.println("TOTAL STDERR LINES: " + stderrLines.size());
-    for (int i = 0; i < stderrLines.size(); i++) {
-      System.out.print("LINE " + i + ": " + stderrLines.get(i));
-      if (stderrLines.get(i).length() > 3) {
-        System.out.print(" (" + ((int) stderrLines.get(i).charAt(0)));
-        System.out.print(", " + ((int) stderrLines.get(i).charAt(1)));
-        System.out.print(", " + ((int) stderrLines.get(i).charAt(2) + ")"));
-      } else {
-        System.out.print(" (less than three chars)");
-      }
-      if (stderrLines.get(i).startsWith(TAB_AT_PREFIX)) {
-        System.out.println(" IS STACKTRACE LINE");
-      } else {
-        System.out.println(" IS NOT STACKTRACE LINE");
-      }
-    }
     for (int i = 0; i < stderrLines.size(); i++) {
       String line = stderrLines.get(i);
       // Find all lines starting with "\tat" except "dalvik.system.NativeStart.main" frame
diff --git a/src/test/java/com/android/tools/r8/regress/b163264839/Regress163264839Test.java b/src/test/java/com/android/tools/r8/regress/b163264839/Regress163264839Test.java
new file mode 100644
index 0000000..0d2f1d5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b163264839/Regress163264839Test.java
@@ -0,0 +1,112 @@
+// Copyright (c) 2020, 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.regress.b163264839;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.transformers.MethodTransformer;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.objectweb.asm.Handle;
+
+@RunWith(Parameterized.class)
+public class Regress163264839Test extends TestBase {
+
+  static final String EXPECTED = StringUtils.lines("Hello, world");
+
+  private final TestParameters parameters;
+  private final boolean isInterface;
+
+  @Parameterized.Parameters(name = "{0}, itf:{1}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimes().withApiLevel(AndroidApiLevel.L).build(),
+        BooleanUtils.values());
+  }
+
+  public Regress163264839Test(TestParameters parameters, boolean isInterface) {
+    this.parameters = parameters;
+    this.isInterface = isInterface;
+  }
+
+  @Test
+  public void test() throws Exception {
+    TestRunResult<?> result =
+        testForRuntime(parameters)
+            .addProgramClassFileData(getFunctionClass())
+            .addProgramClasses(TestClass.class)
+            .run(parameters.getRuntime(), TestClass.class);
+
+    if (isInterface
+        || (parameters.isCfRuntime() && parameters.getRuntime().asCf().getVm().equals(CfVm.JDK8))) {
+      // JDK 8 allows mismatched method references in this case.
+      result.assertSuccessWithOutput(EXPECTED);
+    } else {
+      result.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
+    }
+  }
+
+  private byte[] getFunctionClass() throws Exception {
+    // HACK: Use a new name for the lambda implementation method as otherwise ASM will silently
+    // change isInterface to the "right" value and we can't test the error case.
+    String oldLambdaName = "lambda$identity$0";
+    String newLambdaName = "lambda$identity$foo";
+    return transformer(Function.class)
+        .renameMethod(oldLambdaName, newLambdaName)
+        .addMethodTransformer(
+            new MethodTransformer() {
+              @Override
+              public void visitInvokeDynamicInsn(
+                  String name,
+                  String descriptor,
+                  Handle bootstrapMethodHandle,
+                  Object... bootstrapMethodArguments) {
+                assertEquals(3, bootstrapMethodArguments.length);
+                Handle handle = (Handle) bootstrapMethodArguments[1];
+                assertTrue(handle.isInterface());
+                assertEquals(oldLambdaName, handle.getName());
+                Handle newHandle =
+                    new Handle(
+                        handle.getTag(),
+                        handle.getOwner(),
+                        newLambdaName,
+                        handle.getDesc(),
+                        isInterface);
+                super.visitInvokeDynamicInsn(
+                    name,
+                    descriptor,
+                    bootstrapMethodHandle,
+                    bootstrapMethodArguments[0],
+                    newHandle,
+                    bootstrapMethodArguments[2]);
+              }
+            })
+        .transform();
+  }
+
+  interface Function<R, T> {
+    R apply(T t);
+
+    static <T> Function<T, T> identity() {
+      return t -> t;
+    }
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      System.out.println(Function.identity().apply("Hello, world"));
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/regress/b165825758/Regress165825758Test.java b/src/test/java/com/android/tools/r8/regress/b165825758/Regress165825758Test.java
new file mode 100644
index 0000000..bf95231
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b165825758/Regress165825758Test.java
@@ -0,0 +1,125 @@
+// Copyright (c) 2020, 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.regress.b165825758;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionOffsetSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.android.tools.r8.utils.codeinspector.RangeSubject;
+import com.android.tools.r8.utils.codeinspector.TryCatchSubject;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class Regress165825758Test extends TestBase {
+
+  static final String EXPECTED = StringUtils.lines("Hello, world");
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+  }
+
+  public Regress165825758Test(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testReference() throws Exception {
+    testForRuntime(parameters)
+        .addInnerClasses(Regress165825758Test.class)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .enableInliningAnnotations()
+        .addInnerClasses(Regress165825758Test.class)
+        .addKeepMainRule(TestClass.class)
+        .addKeepClassRules(A.class)
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED)
+        .inspect(this::checkDeadCodeThrowInTryRange);
+  }
+
+  private void checkDeadCodeThrowInTryRange(CodeInspector inspector) {
+    ClassSubject classSubject = inspector.clazz(A.class);
+    assertThat(classSubject, isPresent());
+    MethodSubject method = classSubject.uniqueMethodWithName("synchronizedMethod");
+    assertThat(method, isPresent());
+
+    // Ensure that the "throwNpe" method remains and that it was not inlined by checking that no
+    // allocations of NullPointerException are in the method.
+    assertTrue(method.streamInstructions().noneMatch(InstructionSubject::isNewInstance));
+    assertThat(inspector.clazz(TestClass.class).uniqueMethodWithName("throwNpe"), isPresent());
+
+    // Source has 2 catch ranges:
+    // 1st try catch is the source range, 2nd is the compiler inserted catch over monitor-exit.
+    // When compiled with R8 the catch ranges are collapsed.
+    List<TryCatchSubject> tryCatchSubjects = method.streamTryCatches().collect(Collectors.toList());
+    assertEquals(1, tryCatchSubjects.size());
+    TryCatchSubject sourceTry = tryCatchSubjects.get(0);
+
+    // 1st throw is the "dead code" throw, the 2nd is the exceptional rethrow after method exit.
+    List<InstructionSubject> throwInstructions =
+        method
+            .streamInstructions()
+            .filter(InstructionSubject::isThrow)
+            .collect(Collectors.toList());
+    assertEquals(2, throwInstructions.size());
+    InstructionSubject deadCodeThrow = throwInstructions.get(0);
+    InstructionOffsetSubject throwOffset = deadCodeThrow.getOffset(method);
+    RangeSubject range = sourceTry.getRange();
+    assertTrue(
+        "Expected throw@" + throwOffset + " to be in try-range " + range,
+        range.includes(throwOffset));
+  }
+
+  static class A {
+
+    @NeverInline
+    void synchronizedMethod() {
+      synchronized (this) {
+        TestClass.throwNpe();
+        System.out.println("Never hit");
+      }
+    }
+  }
+
+  static class TestClass {
+
+    @NeverInline
+    static void throwNpe() {
+      throw new NullPointerException();
+    }
+
+    public static void main(String[] args) {
+      try {
+        new A().synchronizedMethod();
+      } catch (NullPointerException e) {
+        System.out.println("Hello, world");
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java
index f2dc303..acf4aff 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java
@@ -66,8 +66,7 @@
   @Test
   public void testVerbose() throws IOException {
     FoundMethodVerboseStackTrace stackTrace = new FoundMethodVerboseStackTrace();
-    // TODO(b/159562137): Add proper support for -verbose when using regexp.
-    runTestNotEquals(
+    runTest(
         stackTrace.mapping(),
         StringUtils.joinLines(stackTrace.obfuscatedStackTrace()),
         false,
@@ -78,8 +77,7 @@
   @Test
   public void testVerboseSingleHyphen() throws IOException {
     FoundMethodVerboseStackTrace stackTrace = new FoundMethodVerboseStackTrace();
-    // TODO(b/159562137): Add proper support for -verbose when using regexp.
-    runTestNotEquals(
+    runTest(
         stackTrace.mapping(),
         StringUtils.joinLines(stackTrace.obfuscatedStackTrace()),
         false,
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceRegularExpressionTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceRegularExpressionTests.java
index d8768a2..4fb8389 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceRegularExpressionTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceRegularExpressionTests.java
@@ -18,6 +18,7 @@
 import com.google.common.collect.ImmutableList;
 import java.util.Collections;
 import java.util.List;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -394,7 +395,7 @@
   }
 
   @Test
-  public void testNotFoundLineNumberInMethodContext() {
+  public void testNoLineNumberInMethodContext() {
     runRetraceTest(
         "%c\\.%m\\(%l\\)",
         new StackTraceForTest() {
@@ -411,7 +412,35 @@
 
           @Override
           public List<String> retracedStackTrace() {
-            return ImmutableList.of("a.b.c.a()");
+            return ImmutableList.of("com.android.tools.r8.R8.foo()");
+          }
+
+          @Override
+          public int expectedWarnings() {
+            return 0;
+          }
+        });
+  }
+
+  @Test
+  public void testNotFoundLineNumberInMethodContext() {
+    runRetraceTest(
+        "%c\\.%m\\(%l\\)",
+        new StackTraceForTest() {
+          @Override
+          public List<String> obfuscatedStackTrace() {
+            return ImmutableList.of("a.b.c.a(42)");
+          }
+
+          @Override
+          public String mapping() {
+            return StringUtils.lines(
+                "com.android.tools.r8.R8 -> a.b.c:", "  3:3:boolean foo():7 -> a");
+          }
+
+          @Override
+          public List<String> retracedStackTrace() {
+            return ImmutableList.of("com.android.tools.r8.R8.a(42)");
           }
 
           @Override
@@ -539,6 +568,7 @@
   }
 
   @Test
+  @Ignore("b/165782924")
   public void useReturnTypeToNarrowMethodMatches() {
     runRetraceTest(
         "%t %c.%m",
@@ -662,6 +692,7 @@
   }
 
   @Test
+  @Ignore("b/165782924")
   public void testPruningOfMethodsByFormals() {
     runRetraceTest(
         "%c.%m\\(%a\\)",
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/ActualRetraceBotStackTraceWithInfo.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/ActualRetraceBotStackTraceWithInfo.java
index 53aa7c6..d57b024 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/ActualRetraceBotStackTraceWithInfo.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/ActualRetraceBotStackTraceWithInfo.java
@@ -45,59 +45,6 @@
   @Override
   public List<String> retracedStackTrace() {
     return Arrays.asList(
-        "Pruning \tat com.android.tools.r8.utils.Reporter.error(Reporter.java:21) from result"
-            + " because method is not defined on line number 21",
-        "Pruning \tat com.android.tools.r8.utils.Reporter.error(Reporter.java:21) from result"
-            + " because method is not defined on line number 21",
-        "Pruning \tat com.android.tools.r8.utils.Reporter.fatalError(Reporter.java:21) from result"
-            + " because method is not defined on line number 21",
-        "Pruning \tat"
-            + " com.android.tools.r8.utils.Reporter.addSuppressedExceptions(Reporter.java:21) from"
-            + " result because method is not defined on line number 21",
-        "Pruning \tat"
-            + " com.android.tools.r8.shaking.ProguardConfigurationParser.parse(ProguardConfigurationParser.java:19)"
-            + " from result because method is not defined on line number 19",
-        "Pruning \tat"
-            + " com.android.tools.r8.shaking.ProguardConfigurationParser.parse(ProguardConfigurationParser.java:19)"
-            + " from result because method is not defined on line number 19",
-        "Pruning \tat"
-            + " com.android.tools.r8.shaking.ProguardConfigurationParser.parse(ProguardConfigurationParser.java:19)"
-            + " from result because method is not in range on line number 19",
-        "Pruning \tat"
-            + " com.android.tools.r8.shaking.ProguardConfigurationParser.parse(ProguardConfigurationParser.java:19)"
-            + " from result because method is not in range on line number 19",
-        "Pruning \tat com.android.tools.r8.R8Command$Builder.makeCommand(R8Command.java:11) from"
-            + " result because method is not defined on line number 11",
-        "Pruning \tat"
-            + " com.android.tools.r8.R8Command$Builder.setDisableVerticalClassMerging(R8Command.java:11)"
-            + " from result because method is not defined on line number 11",
-        "Pruning \tat"
-            + " com.android.tools.r8.R8Command$Builder.lambda$addProguardConfigurationFiles$4(R8Command.java:11)"
-            + " from result because method is not defined on line number 11",
-        "Pruning \tat"
-            + " com.android.tools.r8.R8Command$Builder.lambda$addProguardConfiguration$6(R8Command.java:11)"
-            + " from result because method is not defined on line number 11",
-        "Pruning \tat"
-            + " com.android.tools.r8.R8Command$Builder.lambda$addProguardConfiguration$6(R8Command.java:11)"
-            + " from result because method is not defined on line number 11",
-        "Pruning \tat com.android.tools.r8.R8Command$Builder.makeCommand(R8Command.java:11) from"
-            + " result because method is not in range on line number 11",
-        "Pruning \tat"
-            + " com.android.tools.r8.R8Command$Builder.setDisableVerticalClassMerging(R8Command.java:1)"
-            + " from result because method is not defined on line number 1",
-        "Pruning \tat"
-            + " com.android.tools.r8.R8Command$Builder.lambda$addProguardConfigurationFiles$4(R8Command.java:1)"
-            + " from result because method is not defined on line number 1",
-        "Pruning \tat"
-            + " com.android.tools.r8.R8Command$Builder.lambda$addProguardConfiguration$6(R8Command.java:1)"
-            + " from result because method is not defined on line number 1",
-        "Pruning \tat"
-            + " com.android.tools.r8.R8Command$Builder.lambda$addProguardConfiguration$6(R8Command.java:1)"
-            + " from result because method is not defined on line number 1",
-        "Pruning \tat com.android.tools.r8.R8Command$Builder.makeCommand(R8Command.java:1) from"
-            + " result because method is not defined on line number 1",
-        "Pruning \tat com.android.tools.r8.R8Command$Builder.makeCommand(R8Command.java:1) from"
-            + " result because method is not defined on line number 1",
         "com.android.tools.r8.CompilationFailedException: Compilation failed to complete",
         "\tat com.android.tools.r8.BaseCommand$Builder.build(BaseCommand.java:143)",
         "\tat com.android.tools.r8.R8TestBuilder.internalCompile(R8TestBuilder.java:104)",
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/FoundMethodVerboseStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/FoundMethodVerboseStackTrace.java
index 4d9418c..ce7a8f5 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/FoundMethodVerboseStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/FoundMethodVerboseStackTrace.java
@@ -27,7 +27,7 @@
   public List<String> retracedStackTrace() {
     return Arrays.asList(
         "Exception in thread \"main\" java.lang.NullPointerException",
-        "\tat com.android.Foo com.android.tools.r8.naming.retrace.Main.main(java.lang.String[],"
+        "\tat com.android.tools.r8.naming.retrace.Main.com.android.Foo main(java.lang.String[],"
             + "com.android.Bar)(Main.java:102)");
   }
 
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/UnknownMethodVerboseStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/UnknownMethodVerboseStackTrace.java
index 3eb9710..5a47079 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/UnknownMethodVerboseStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/UnknownMethodVerboseStackTrace.java
@@ -25,7 +25,7 @@
         "com.android.tools.r8.naming.retrace.Main -> a.a:",
         "    com.android.Foo main(java.lang.String[],com.android.Bar) -> a",
         "    com.android.Foo main(java.lang.String[]) -> b",
-        "    com.android.Bar main(com.android.Bar) -> b");
+        "    void main(com.android.Bar) -> b");
   }
 
   @Override
@@ -33,11 +33,11 @@
     return Arrays.asList(
         "Exception in thread \"main\" java.lang.NullPointerException",
         "\tat com.android.tools.r8.naming.retrace.Main.c(Main.java)",
-        "\tat com.android.Foo com.android.tools.r8.naming.retrace.Main.main("
+        "\tat com.android.tools.r8.naming.retrace.Main.com.android.Foo main("
             + "java.lang.String[])(Main.java)",
-        "\t<OR> at com.android.Bar com.android.tools.r8.naming.retrace.Main.main("
+        "\t<OR> at com.android.tools.r8.naming.retrace.Main.void main("
             + "com.android.Bar)(Main.java)",
-        "\tat com.android.Foo com.android.tools.r8.naming.retrace.Main.main("
+        "\tat com.android.tools.r8.naming.retrace.Main.com.android.Foo main("
             + "java.lang.String[],com.android.Bar)(Main.java)");
   }
 
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
index c34dc98..c84415e 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -448,6 +448,18 @@
         });
   }
 
+  public ClassFileTransformer renameMethod(String oldName, String newName) {
+    return addClassTransformer(
+        new ClassTransformer() {
+          @Override
+          public MethodVisitor visitMethod(
+              int access, String name, String descriptor, String signature, String[] exceptions) {
+            return super.visitMethod(
+                access, name.equals(oldName) ? newName : name, descriptor, signature, exceptions);
+          }
+        });
+  }
+
   /** Abstraction of the MethodVisitor.visitMethodInsn method with its sub visitor. */
   @FunctionalInterface
   public interface MethodInsnTransform {
diff --git a/src/test/java/com/android/tools/r8/transformers/MethodTransformer.java b/src/test/java/com/android/tools/r8/transformers/MethodTransformer.java
index a5ffa82..cb05520 100644
--- a/src/test/java/com/android/tools/r8/transformers/MethodTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/MethodTransformer.java
@@ -17,7 +17,7 @@
  */
 public class MethodTransformer extends MethodVisitor {
 
-  static class MethodContext {
+  public static class MethodContext {
     public final MethodReference method;
     public final int accessFlags;
 
@@ -25,6 +25,10 @@
       this.method = method;
       this.accessFlags = accessFlags;
     }
+
+    public MethodReference getReference() {
+      return method;
+    }
   }
 
   private MethodContext context;
@@ -43,7 +47,7 @@
 
   // Package internals.
 
-  MethodContext getContext() {
+  protected MethodContext getContext() {
     return context;
   }
 
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
index 2387655..6cf80fb 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
@@ -31,12 +31,15 @@
 import com.android.tools.r8.cf.code.CfReturn;
 import com.android.tools.r8.cf.code.CfReturnVoid;
 import com.android.tools.r8.cf.code.CfStackInstruction;
+import com.android.tools.r8.cf.code.CfStore;
 import com.android.tools.r8.cf.code.CfSwitch;
 import com.android.tools.r8.cf.code.CfThrow;
+import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.ir.code.Monitor.Type;
 import com.android.tools.r8.ir.code.ValueType;
+import java.util.Iterator;
 import org.objectweb.asm.Opcodes;
 
 public class CfInstructionSubject implements InstructionSubject {
@@ -55,6 +58,11 @@
   }
 
   @Override
+  public CfInstructionSubject asCfInstruction() {
+    return this;
+  }
+
+  @Override
   public boolean isFieldAccess() {
     return instruction instanceof CfFieldInstruction;
   }
@@ -297,6 +305,10 @@
     return instruction instanceof CfLoad;
   }
 
+  public boolean isStore() {
+    return instruction instanceof CfStore;
+  }
+
   @Override
   public boolean isMultiplication() {
     if (!(instruction instanceof CfArithmeticBinop)) {
@@ -348,8 +360,17 @@
 
   @Override
   public InstructionOffsetSubject getOffset(MethodSubject methodSubject) {
-    // TODO(b/122302789): CfInstruction#getOffset()
-    throw new UnsupportedOperationException("CfInstruction doesn't have offset yet.");
+    // TODO(b/122302789): Update this if 'offset' is introduced.
+    Iterator<InstructionSubject> it = methodSubject.iterateInstructions();
+    int bci = 0;
+    while (it.hasNext()) {
+      ++bci;
+      InstructionSubject next = it.next();
+      if (next.asCfInstruction().instruction == instruction) {
+        return new InstructionOffsetSubject(bci);
+      }
+    }
+    throw new Unreachable();
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
index 0335a5d..26944f9 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
@@ -121,6 +121,11 @@
   }
 
   @Override
+  public CfInstructionSubject asCfInstruction() {
+    return null;
+  }
+
+  @Override
   public boolean isFieldAccess() {
     return isInstanceGet() || isInstancePut() || isStaticGet() || isStaticPut();
   }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionOffsetSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionOffsetSubject.java
index 232884b..58e41ff 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionOffsetSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionOffsetSubject.java
@@ -12,4 +12,8 @@
     this.offset = offset;
   }
 
+  @Override
+  public String toString() {
+    return "" + offset;
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
index ad9b5c2..513de02 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
@@ -18,6 +18,8 @@
 
   DexInstructionSubject asDexInstruction();
 
+  CfInstructionSubject asCfInstruction();
+
   boolean isFieldAccess();
 
   boolean isInstancePut();
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/RangeSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/RangeSubject.java
index 12e3a4e..18c024a 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/RangeSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/RangeSubject.java
@@ -18,4 +18,9 @@
   public boolean includes(InstructionOffsetSubject offsetSubject) {
     return this.start <= offsetSubject.offset && offsetSubject.offset <= this.end;
   }
+
+  @Override
+  public String toString() {
+    return "[" + start + ":" + end + "]";
+  }
 }
diff --git a/tools/archive.py b/tools/archive.py
index a3e15ba..f1410a5 100755
--- a/tools/archive.py
+++ b/tools/archive.py
@@ -233,13 +233,15 @@
 
       # Upload desugar_jdk_libs configuration to a maven compatible location.
       if file == utils.DESUGAR_CONFIGURATION:
-        jar_name = 'desugar_jdk_libs_configuration-%s.jar' % version
+        jar_basename = 'desugar_jdk_libs_configuration.jar'
+        jar_version_name = 'desugar_jdk_libs_configuration-%s.jar' % version
         maven_dst = GetUploadDestination(
             utils.get_maven_path('desugar_jdk_libs_configuration', version),
-                                 jar_name, is_master)
+                                 jar_version_name, is_master)
 
         with utils.TempDir() as tmp_dir:
-          desugar_jdk_libs_configuration_jar = os.path.join(tmp_dir, jar_name)
+          desugar_jdk_libs_configuration_jar = os.path.join(tmp_dir,
+                                                            jar_version_name)
           create_maven_release.generate_jar_with_desugar_configuration(
               utils.DESUGAR_CONFIGURATION,
               utils.LIBRARY_DESUGAR_CONVERSIONS_ZIP,
@@ -251,11 +253,17 @@
             if options.dry_run_output:
               shutil.copyfile(
                   desugar_jdk_libs_configuration_jar,
-                  os.path.join(options.dry_run_output, jar_name))
+                  os.path.join(options.dry_run_output, jar_version_name))
           else:
             utils.upload_file_to_cloud_storage(
                 desugar_jdk_libs_configuration_jar, maven_dst)
             print('Maven repo root available at: %s' % GetMavenUrl(is_master))
+            # Also archive the jar as non maven destination for Google3
+            jar_destination = GetUploadDestination(
+                version, jar_basename, is_master)
+            utils.upload_file_to_cloud_storage(
+                desugar_jdk_libs_configuration_jar, jar_destination)
+
 
 if __name__ == '__main__':
   sys.exit(Main())
diff --git a/tools/create_maven_release.py b/tools/create_maven_release.py
index 42dcc63..f92afde 100755
--- a/tools/create_maven_release.py
+++ b/tools/create_maven_release.py
@@ -312,21 +312,22 @@
     gradle.RunGradle([utils.R8LIB, '-Pno_internal'])
 
   version = determine_version()
-  # Generate the pom file.
-  pom_file = 'r8.pom'
-  write_pom_file(
-      R8_POMTEMPLATE,
-      pom_file,
-      version,
-      "" if is_r8lib else generate_dependencies(),
-      generate_library_licenses() if is_r8lib else "")
-  # Write the maven zip file.
-  generate_maven_zip(
-      'r8',
-      version,
-      pom_file,
-      utils.R8LIB_JAR if is_r8lib else utils.R8_JAR,
-      out)
+  with utils.TempDir() as tmp_dir:
+    # Generate the pom file.
+    pom_file = join(tmp_dir, 'r8.pom')
+    write_pom_file(
+        R8_POMTEMPLATE,
+        pom_file,
+        version,
+        "" if is_r8lib else generate_dependencies(),
+        generate_library_licenses() if is_r8lib else "")
+    # Write the maven zip file.
+    generate_maven_zip(
+        'r8',
+        version,
+        pom_file,
+        utils.R8LIB_JAR if is_r8lib else utils.R8_JAR,
+        out)
 
 # Write the desugaring configuration of a jar file with the following content:
 #  java/
@@ -369,20 +370,20 @@
 
 # Generate the maven zip for the configuration to desugar desugar_jdk_libs.
 def generate_desugar_configuration_maven_zip(out):
-  version = utils.desugar_configuration_version()
-  # Generate the pom file.
-  pom_file = 'desugar_configuration.pom'
-  write_pom_file(DESUGAR_CONFIGUATION_POMTEMPLATE, pom_file, version)
-  # Generate the jar with the configuration file.
-  jar_file = 'desugar_configuration.jar'
-  generate_jar_with_desugar_configuration(
-      utils.DESUGAR_CONFIGURATION,
-      utils.LIBRARY_DESUGAR_CONVERSIONS_ZIP,
-      jar_file)
-  # Write the maven zip file.
-  generate_maven_zip(
-      'desugar_jdk_libs_configuration', version, pom_file, jar_file, out)
-
+  with utils.TempDir() as tmp_dir:
+    version = utils.desugar_configuration_version()
+    # Generate the pom file.
+    pom_file = join(tmp_dir, 'desugar_configuration.pom')
+    write_pom_file(DESUGAR_CONFIGUATION_POMTEMPLATE, pom_file, version)
+    # Generate the jar with the configuration file.
+    jar_file = join(tmp_dir, 'desugar_configuration.jar')
+    generate_jar_with_desugar_configuration(
+        utils.DESUGAR_CONFIGURATION,
+        utils.LIBRARY_DESUGAR_CONVERSIONS_ZIP,
+        jar_file)
+    # Write the maven zip file.
+    generate_maven_zip(
+        'desugar_jdk_libs_configuration', version, pom_file, jar_file, out)
 
 def main(argv):
   options = parse_options(argv)
diff --git a/tools/r8_release.py b/tools/r8_release.py
index e813756..dce7dad 100755
--- a/tools/r8_release.py
+++ b/tools/r8_release.py
@@ -357,11 +357,17 @@
       g4_open('src.jar')
       g4_open('lib.jar')
       g4_open('lib.jar.map')
+      g4_open('desugar_jdk_libs.json')
+      g4_open('desugar_jdk_libs_configuration.jar')
       download_file(options.version, 'r8-full-exclude-deps.jar', 'full.jar')
       download_file(options.version, 'r8-src.jar', 'src.jar')
       download_file(options.version, 'r8lib-exclude-deps.jar', 'lib.jar')
       download_file(
           options.version, 'r8lib-exclude-deps.jar.map', 'lib.jar.map')
+      download_file(options.version, 'desugar_jdk_libs.json',
+                    'desugar_jdk_libs.json')
+      download_file(options.version, 'desugar_jdk_libs_configuration.jar',
+                    'desugar_jdk_libs_configuration.jar')
       g4_open('METADATA')
       sed(r'[1-9]\.[0-9]{1,2}\.[0-9]{1,3}-dev',
           options.version,