Merge "Remove package map distributor."
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index dce4879..0b16963 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -23,7 +23,6 @@
 import com.android.tools.r8.naming.Minifier;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.optimize.BridgeMethodAnalysis;
-import com.android.tools.r8.optimize.DebugStripper;
 import com.android.tools.r8.optimize.MemberRebindingAnalysis;
 import com.android.tools.r8.optimize.VisibilityBridgeRemover;
 import com.android.tools.r8.shaking.AbstractMethodRemover;
@@ -138,17 +137,6 @@
       timing.end();
     }
 
-    if (!options.skipDebugInfoOpt && (application.getProguardMap() != null)) {
-      try {
-        timing.begin("DebugStripper");
-        DebugStripper stripper =
-            new DebugStripper(application.getProguardMap(), options, appInfo.dexItemFactory);
-        application.classes().forEach(stripper::processClass);
-      } finally {
-        timing.end();
-      }
-    }
-
     if (options.printCfg) {
       if (options.printCfgFile == null || options.printCfgFile.isEmpty()) {
         System.out.print(printer.toString());
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 2053abd..7bae1a1 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -263,7 +263,6 @@
       "  --min-api                # Minimum Android API level compatibility.",
       "  --pg-conf <file>         # Proguard configuration <file> (implies tree",
       "                           # shaking/minification).",
-      "  --pg-map <file>          # Proguard map <file>.",
       "  --print-mapping <file>   # Write name/line mapping to <file>.",
       "  --no-tree-shaking        # Force disable tree shaking of unreachable classes.",
       "  --no-discarded-checker   # Force disable the discarded checker (when tree shaking).",
@@ -350,8 +349,6 @@
         builder.setMainDexListOutputPath(Paths.get(args[++i]));
       } else if (arg.equals("--pg-conf")) {
         builder.addProguardConfigurationFiles(Paths.get(args[++i]));
-      } else if (arg.equals("--pg-map")) {
-        builder.setProguardMapFile(Paths.get(args[++i]));
       } else if (arg.equals("--ignore-missing-classes")) {
         builder.setIgnoreMissingClasses(true);
       } else if (arg.equals("--print-mapping")) {
diff --git a/src/main/java/com/android/tools/r8/annotations/SynthesizedClassMap.java b/src/main/java/com/android/tools/r8/annotations/SynthesizedClassMap.java
new file mode 100644
index 0000000..b46a2ba
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/annotations/SynthesizedClassMap.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.TYPE)
+public @interface SynthesizedClassMap {
+  Class<?>[] value() default {};
+}
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index 1e70b9b..eba5613 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -133,7 +133,7 @@
 
     // Sort the class members.
     // Needed before adding static-value arrays and writing annotation directories and classes.
-    sortClassData(mixedSectionOffsets.getClassesWithData());
+    mixedSectionOffsets.getClassesWithData().forEach(DexProgramClass::sortMembers);
 
     // Add the static values for all fields now that we have committed to their sorting.
     mixedSectionOffsets.getClassesWithData().forEach(this::addStaticFieldValues);
@@ -254,23 +254,6 @@
     return Arrays.copyOf(dest.asArray(), layout.getEndOfFile());
   }
 
-  private void sortClassData(Collection<DexProgramClass> classesWithData) {
-    for (DexProgramClass clazz : classesWithData) {
-      sortEncodedFields(clazz.instanceFields());
-      sortEncodedFields(clazz.staticFields());
-      sortEncodedMethods(clazz.directMethods());
-      sortEncodedMethods(clazz.virtualMethods());
-    }
-  }
-
-  private void sortEncodedFields(DexEncodedField[] fields) {
-    Arrays.sort(fields, (DexEncodedField a, DexEncodedField b) -> a.field.compareTo(b.field));
-  }
-
-  private void sortEncodedMethods(DexEncodedMethod[] methods) {
-    Arrays.sort(methods, (DexEncodedMethod a, DexEncodedMethod b) -> a.method.compareTo(b.method));
-  }
-
   private void checkInterfaceMethods() throws ApiLevelException {
     for (DexProgramClass clazz : mapping.getClasses()) {
       if (clazz.isInterface()) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
index 1afd77e..3a8d56b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
@@ -13,7 +13,11 @@
 import com.android.tools.r8.graph.DexValue.DexValueString;
 import com.android.tools.r8.graph.DexValue.DexValueType;
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 public class DexAnnotation extends DexItem {
   public static final int VISIBILITY_BUILD = 0x00;
@@ -228,4 +232,37 @@
   private static DexValue toDexValue(String string, DexItemFactory factory) {
     return new DexValueString(factory.createString(string));
   }
+
+  public static Collection<DexType> readAnnotationSynthesizedClassMap(
+      DexProgramClass programClass,
+      DexItemFactory dexItemFactory) {
+    DexAnnotation foundAnnotation = programClass.annotations.getFirstMatching(
+        dexItemFactory.annotationSynthesizedClassMap);
+    if (foundAnnotation != null) {
+      DexAnnotationElement value = foundAnnotation.annotation.elements[0];
+      assert value.name.toSourceString().equals("value");
+      DexValueArray existingList = (DexValueArray) value.value;
+      Collection<DexType> synthesized = new ArrayList<>(existingList.values.length);
+      for (DexValue element : existingList.getValues()) {
+        synthesized.add(((DexValueType) element).value);
+      }
+      return synthesized;
+    }
+    return Collections.emptyList();
+  }
+
+  public static DexAnnotation createAnnotationSynthesizedClassMap(
+      Set<DexType> synthesized,
+      DexItemFactory dexItemFactory) {
+    DexValueType[] values = synthesized.stream()
+        .map(type -> new DexValueType(type))
+        .toArray(length -> new DexValueType[length]);
+    DexValueArray array = new DexValueArray(values);
+    DexAnnotationElement pair =
+        new DexAnnotationElement(dexItemFactory.createString("value"), array);
+    return new DexAnnotation(
+        VISIBILITY_BUILD,
+        new DexEncodedAnnotation(
+            dexItemFactory.annotationSynthesizedClassMap, new DexAnnotationElement[]{pair}));
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
index 00b99fa..93e103d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
@@ -74,8 +74,44 @@
     return null;
   }
 
+  public DexAnnotationSet getWithout(DexType annotationType) {
+    int index = 0;
+    for (DexAnnotation annotation : annotations) {
+      if (annotation.annotation.type == annotationType) {
+        DexAnnotation[] reducedArray = new DexAnnotation[annotations.length - 1];
+        System.arraycopy(annotations, 0, reducedArray, 0, index);
+        if (index < reducedArray.length) {
+          System.arraycopy(annotations, index + 1, reducedArray, index, reducedArray.length - index);
+        }
+        return new DexAnnotationSet(reducedArray);
+      }
+      ++index;
+    }
+    return this;
+  }
+
   private int sortedHashCode() {
     int hashCode = hashCode();
     return hashCode == UNSORTED ? 1 : hashCode;
   }
+
+  public DexAnnotationSet getWithAddedOrReplaced(DexAnnotation newAnnotation) {
+
+    // Check existing annotation for replacement.
+    int index = 0;
+    for (DexAnnotation annotation : annotations) {
+      if (annotation.annotation.type == newAnnotation.annotation.type) {
+        DexAnnotation[] modifiedArray = annotations.clone();
+        modifiedArray[index] = newAnnotation;
+        return new DexAnnotationSet(modifiedArray);
+      }
+      ++index;
+    }
+
+    // No existing annotation, append.
+    DexAnnotation[] extendedArray = new DexAnnotation[annotations.length + 1];
+    System.arraycopy(annotations, 0, extendedArray, 0, annotations.length);
+    extendedArray[annotations.length] = newAnnotation;
+    return new DexAnnotationSet(extendedArray);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/DexApplication.java b/src/main/java/com/android/tools/r8/graph/DexApplication.java
index 0d2edcb..294b031 100644
--- a/src/main/java/com/android/tools/r8/graph/DexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DexApplication.java
@@ -315,6 +315,7 @@
     DexString highestSortingString;
     private byte[] deadCode;
     private final Set<DexType> mainDexList = Sets.newIdentityHashSet();
+    private final Collection<DexProgramClass> synthesizedClasses;
 
     public Builder(DexItemFactory dexItemFactory, Timing timing) {
       this.programClasses = new ArrayList<>();
@@ -323,6 +324,7 @@
       this.deadCode = null;
       this.classpathClasses = null;
       this.libraryClasses = null;
+      this.synthesizedClasses = new ArrayList<>();
     }
 
     public Builder(DexApplication application) {
@@ -335,6 +337,7 @@
       dexItemFactory = application.dexItemFactory;
       mainDexList.addAll(application.mainDexList);
       deadCode = application.deadCode;
+      synthesizedClasses = new ArrayList<>();
     }
 
     public synchronized Builder setProguardMap(ClassNameMapper proguardMap) {
@@ -387,6 +390,7 @@
         DexProgramClass synthesizedClass, boolean addToMainDexList) {
       assert synthesizedClass.isProgramClass() : "All synthesized classes must be program classes";
       addProgramClass(synthesizedClass);
+      synthesizedClasses.add(synthesizedClass);
       if (addToMainDexList && !mainDexList.isEmpty()) {
         mainDexList.add(synthesizedClass.type);
       }
@@ -397,6 +401,14 @@
       return programClasses;
     }
 
+    public Collection<DexProgramClass> getSynthesizedClasses() {
+      return synthesizedClasses;
+    }
+
+    public Set<DexType> getMainDexList() {
+      return mainDexList;
+    }
+
     public Builder addToMainDexList(Collection<DexType> mainDexList) {
       this.mainDexList.addAll(mainDexList);
       return this;
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 c1dad50..14e5288 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
@@ -63,11 +63,6 @@
     this.factory = factory;
   }
 
-  // Public method for the DebugStripper.
-  public void setPosition(int pc, int line) {
-    emitDebugPosition(pc, line, null);
-  }
-
   /** Add events at pc for instruction. */
   public void add(int pc, Instruction instruction) {
     // Initialize locals state on block entry.
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 527f27b..b7e41fe 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -161,6 +161,8 @@
   public final DexType annotationSourceDebugExtension = createType(
       "Ldalvik/annotation/SourceDebugExtension;");
   public final DexType annotationThrows = createType("Ldalvik/annotation/Throws;");
+  public final DexType annotationSynthesizedClassMap =
+      createType("Lcom/android/tools/r8/annotations/SynthesizedClassMap;");
 
   public synchronized void clearSubtypeInformation() {
     types.values().forEach(DexType::clearSubtypeInformation);
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index d27a3be..4332df8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -9,6 +9,7 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashSet;
 import java.util.Set;
 import java.util.function.Supplier;
@@ -213,6 +214,21 @@
     directMethods = newDirectMethods;
   }
 
+  public synchronized void sortMembers() {
+    sortEncodedFields(staticFields);
+    sortEncodedFields(instanceFields);
+    sortEncodedMethods(directMethods);
+    sortEncodedMethods(virtualMethods);
+  }
+
+  private void sortEncodedFields(DexEncodedField[] fields) {
+    Arrays.sort(fields, Comparator.comparing(a -> a.field));
+  }
+
+  private void sortEncodedMethods(DexEncodedMethod[] methods) {
+    Arrays.sort(methods, Comparator.comparing(a -> a.method));
+  }
+
   @Override
   public DexProgramClass get() {
     return this;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index c67e229..7dca5bd 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexApplication.Builder;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -39,16 +40,22 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ListMultimap;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 import java.util.function.BiConsumer;
+import java.util.function.Function;
 import java.util.function.Predicate;
+import java.util.stream.Collectors;
 
 public class IRConverter {
 
@@ -225,9 +232,72 @@
     synthesizeLambdaClasses(builder);
     desugarInterfaceMethods(builder, ExcludeDexResources);
 
+    if (options.intermediate) {
+      updateSynthesizedClassMapping(builder);
+    }
+
+    updateMainDexListWithSynthesizedClassMap(builder);
+
+    if (!options.intermediate) {
+      clearSynthesizedClassMapping(builder);
+    }
+
     return builder.build();
   }
 
+  private void updateMainDexListWithSynthesizedClassMap(Builder builder) {
+    Set<DexType> inputMainDexList = builder.getMainDexList();
+    if (!inputMainDexList.isEmpty()) {
+      Map<DexType, DexProgramClass> programClasses = builder.getProgramClasses().stream()
+          .collect(Collectors.toMap(
+            programClass -> programClass.type,
+            Function.identity()));
+      Collection<DexType> synthesized = new ArrayList<>();
+      for (DexType dexType : inputMainDexList) {
+        DexProgramClass programClass = programClasses.get(dexType);
+        if (programClass != null) {
+          synthesized.addAll(DexAnnotation.readAnnotationSynthesizedClassMap(
+              programClass, builder.dexItemFactory));
+        }
+      }
+      builder.addToMainDexList(synthesized);
+    }
+  }
+
+  private void clearSynthesizedClassMapping(Builder builder) {
+    for (DexProgramClass programClass : builder.getProgramClasses()) {
+      programClass.annotations =
+          programClass.annotations.getWithout(builder.dexItemFactory.annotationSynthesizedClassMap);
+    }
+  }
+
+  private void updateSynthesizedClassMapping(Builder builder) {
+    ListMultimap<DexProgramClass, DexProgramClass> originalToSynthesized =
+        ArrayListMultimap.create();
+    for (DexProgramClass synthesized : builder.getSynthesizedClasses()) {
+      for (DexProgramClass original : synthesized.getSynthesizedFrom()) {
+        originalToSynthesized.put(original, synthesized);
+      }
+    }
+
+    for (Map.Entry<DexProgramClass, Collection<DexProgramClass>> entry :
+        originalToSynthesized.asMap().entrySet()) {
+      DexProgramClass original = entry.getKey();
+      Set<DexType> synthesized = new HashSet<>();
+      entry.getValue()
+        .stream()
+        .map(dexProgramClass -> dexProgramClass.type)
+        .forEach(synthesized::add);
+      synthesized.addAll(
+          DexAnnotation.readAnnotationSynthesizedClassMap(original, builder.dexItemFactory));
+
+      DexAnnotation updatedAnnotation =
+          DexAnnotation.createAnnotationSynthesizedClassMap(synthesized, builder.dexItemFactory);
+
+      original.annotations = original.annotations.getWithAddedOrReplaced(updatedAnnotation);
+    }
+  }
+
   private void convertClassesToDex(Iterable<DexProgramClass> classes,
       ExecutorService executor) throws ExecutionException {
     List<Future<?>> futures = new ArrayList<>();
diff --git a/src/main/java/com/android/tools/r8/optimize/DebugStripper.java b/src/main/java/com/android/tools/r8/optimize/DebugStripper.java
deleted file mode 100644
index 0eda1e5..0000000
--- a/src/main/java/com/android/tools/r8/optimize/DebugStripper.java
+++ /dev/null
@@ -1,186 +0,0 @@
-// Copyright (c) 2016, 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.optimize;
-
-import com.android.tools.r8.graph.DexCode;
-import com.android.tools.r8.graph.DexDebugEntry;
-import com.android.tools.r8.graph.DexDebugEventBuilder;
-import com.android.tools.r8.graph.DexDebugInfo;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.naming.ClassNameMapper;
-import com.android.tools.r8.naming.ClassNaming;
-import com.android.tools.r8.naming.MemberNaming;
-import com.android.tools.r8.naming.MemberNaming.Range;
-import com.android.tools.r8.naming.MemberNaming.Signature;
-import com.android.tools.r8.utils.InternalOptions;
-
-import it.unimi.dsi.fastutil.objects.Reference2IntMap;
-import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
-
-import java.util.List;
-
-public class DebugStripper {
-
-  private static final int USED_MORE_THAN_ONCE = 0;
-  private static final int USED_ONCE = -1;
-
-  private final ClassNameMapper classNameMapper;
-  private final InternalOptions options;
-  private final DexItemFactory dexItemFactory;
-
-  public DebugStripper(
-      ClassNameMapper classNameMapper, InternalOptions options, DexItemFactory dexItemFactory) {
-    this.classNameMapper = classNameMapper;
-    this.options = options;
-    this.dexItemFactory = dexItemFactory;
-  }
-
-  private String descriptorToName(String descriptor) {
-    // The format is L<name>; and '/' is used as package separator.
-    return descriptor.substring(1, descriptor.length() - 1).replace('/', '.');
-  }
-
-  private Range findRange(int value, List<Range> ranges, Range defaultRange) {
-    for (Range range : ranges) {
-      if (range.contains(value)) {
-        return range;
-      }
-    }
-    return defaultRange;
-  }
-
-  private static class NumberedDebugInfo {
-
-    final int numberOfEntries;
-    final DexDebugInfo info;
-
-    public NumberedDebugInfo(int numberOfEntries, DexDebugInfo info) {
-      this.numberOfEntries = numberOfEntries;
-      this.info = info;
-    }
-  }
-
-  private NumberedDebugInfo processDebugInfo(
-      DexEncodedMethod method, DexDebugInfo info, MemberNaming naming, int startLine) {
-    if (info == null || naming == null) {
-      return new NumberedDebugInfo(0, null);
-    }
-    List<Range> ranges = naming.getInlineRanges();
-    // Maintain line and address but only when entering or leaving a range of line numbers
-    // that pertains to a different method body.
-    Range currentRange = naming.topLevelRange;
-    DexDebugEventBuilder builder = new DexDebugEventBuilder(method, dexItemFactory);
-    // Below we insert the start-line at pc-0 except if another entry already defines pc-0.
-    int entryCount = 0;
-    for (DexDebugEntry entry : info.computeEntries()) {
-      boolean addEntry = false;
-      // We are in a range, check whether we have left it.
-      if (currentRange != null && !currentRange.contains(entry.line)) {
-        currentRange = null;
-        addEntry = true;
-      }
-      // We have no range (because we left the old one or never were in a range).
-      if (currentRange == null) {
-        currentRange = findRange(entry.line, ranges, naming.topLevelRange);
-        // We entered a new Range, emit this entry.
-        if (currentRange != null) {
-          addEntry = true;
-        }
-      }
-      if (addEntry) {
-        if (entryCount == 0 && entry.address > 0) {
-          ++entryCount;
-          builder.setPosition(0, startLine);
-        }
-        int line = options.skipDebugLineNumberOpt
-            ? entry.line
-            : startLine + ranges.indexOf(currentRange) + 1;
-        builder.setPosition(entry.address, line);
-        ++entryCount;
-      }
-    }
-    if (entryCount == 0) {
-      ++entryCount;
-      builder.setPosition(0, startLine);
-    }
-    return new NumberedDebugInfo(entryCount, builder.build());
-  }
-
-  private void processCode(DexEncodedMethod encodedMethod, MemberNaming naming,
-      Reference2IntMap<DexString> nameCounts) {
-    if (encodedMethod.getCode() == null) {
-      return;
-    }
-    DexCode code = encodedMethod.getCode().asDexCode();
-    DexString name = encodedMethod.method.name;
-    DexDebugInfo originalInfo = code.getDebugInfo();
-    if (originalInfo == null) {
-      return;
-    }
-    int startLine;
-    boolean isUsedOnce = false;
-    if (options.skipDebugLineNumberOpt) {
-      startLine = originalInfo.startLine;
-    } else {
-      int nameCount = nameCounts.getInt(name);
-      if (nameCount == USED_ONCE) {
-        isUsedOnce = true;
-        startLine = 0;
-      } else {
-        startLine = nameCount;
-      }
-    }
-
-    NumberedDebugInfo numberedInfo =
-        processDebugInfo(encodedMethod, originalInfo, naming, startLine);
-    DexDebugInfo newInfo = numberedInfo.info;
-    if (!options.skipDebugLineNumberOpt) {
-      // Fix up the line information.
-      int previousCount = nameCounts.getInt(name);
-      nameCounts.put(name, previousCount + numberedInfo.numberOfEntries);
-      // If we don't actually need line information and there are no debug entries, throw it away.
-      if (newInfo != null && isUsedOnce && newInfo.events.length == 0) {
-        newInfo = null;
-      } else if (naming != null && newInfo != null) {
-        naming.setCollapsedStartLineNumber(startLine);
-        // Preserve the line number information we had.
-        naming.setOriginalStartLineNumber(originalInfo.startLine);
-      }
-    }
-    code.setDebugInfo(newInfo);
-  }
-
-  private void processMethod(DexEncodedMethod method, ClassNaming classNaming,
-      Reference2IntMap<DexString> nameCounts) {
-    MemberNaming naming = null;
-    if (classNaming != null) {
-      Signature renamedSignature = classNameMapper.getRenamedMethodSignature(method.method);
-      naming = classNaming.lookup(renamedSignature);
-    }
-    processCode(method, naming, nameCounts);
-  }
-
-  public void processClass(DexProgramClass clazz) {
-    if (!clazz.hasMethodsOrFields()) {
-      return;
-    }
-    String name = descriptorToName(clazz.type.toDescriptorString());
-    ClassNaming naming = classNameMapper == null ? null : classNameMapper.getClassNaming(name);
-    Reference2IntMap<DexString> nameCounts = new Reference2IntOpenHashMap<>();
-    clazz.forEachMethod(method -> setIntialNameCounts(nameCounts, method));
-    clazz.forEachMethod(method -> processMethod(method, naming, nameCounts));
-  }
-
-  private void setIntialNameCounts(Reference2IntMap<DexString> nameCounts,
-      DexEncodedMethod method) {
-    if (nameCounts.containsKey(method.method.name)) {
-      nameCounts.put(method.method.name, USED_MORE_THAN_ONCE);
-    } else {
-      nameCounts.put(method.method.name, USED_ONCE);
-    }
-  }
-}
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 c03c3d6..a2a0446 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -46,7 +46,6 @@
 
   public boolean printTimes = false;
   // Skipping optimizations.
-  public boolean skipDebugInfoOpt = false;
   public boolean skipDebugLineNumberOpt = false;
   public boolean skipClassMerging = true;
 
diff --git a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
index 7a56e3c..a2e64e1 100644
--- a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
@@ -6,14 +6,20 @@
 
 import static com.android.tools.r8.dex.Constants.ANDROID_K_API;
 import static com.android.tools.r8.dex.Constants.ANDROID_O_API;
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+import static com.android.tools.r8.utils.FileUtils.ZIP_EXTENSION;
 
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.InternalCompilerError;
 import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.utils.DexInspector;
 import com.android.tools.r8.utils.OffOrAuto;
+import com.android.tools.r8.utils.OutputMode;
 import java.io.IOException;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.Collection;
+import java.util.HashSet;
 import java.util.function.UnaryOperator;
 import org.hamcrest.core.CombinableMatcher;
 import org.hamcrest.core.IsInstanceOf;
@@ -66,6 +72,10 @@
       }
     }
 
+    D8TestRunner withIntermediate(boolean intermediate) {
+      return withBuilderTransformation(builder -> builder.setIntermediate(intermediate));
+    }
+
     @Override
     D8TestRunner self() {
       return this;
@@ -519,6 +529,133 @@
         new Path[] {lib1Dex, lib3Dex, lib4Dex, testDex});
 
   }
+
+  @Test
+  public void testLambdaDesugaringWithMainDexList1() throws Throwable {
+    // Minimal case: there are synthesized classes but not form the main dex class.
+    testIntermediateWithMainDexList(
+        "lambdadesugaring",
+        1,
+        "lambdadesugaring.LambdaDesugaring$I");
+  }
+
+  @Test
+  public void testLambdaDesugaringWithMainDexList2() throws Throwable {
+    // Main dex class has many lambdas.
+    testIntermediateWithMainDexList("lambdadesugaring",
+        33,
+        "lambdadesugaring.LambdaDesugaring$Refs$B");
+  }
+
+  @Test
+  public void testInterfaceDesugaringWithMainDexList1() throws Throwable {
+    // Main dex interface has one static method.
+    testIntermediateWithMainDexList(
+        "interfacemethods",
+        Paths.get(ToolHelper.EXAMPLES_ANDROID_N_BUILD_DIR, "interfacemethods" + JAR_EXTENSION),
+        2,
+        "interfacemethods.I1");
+  }
+
+
+  @Test
+  public void testInterfaceDesugaringWithMainDexList2() throws Throwable {
+    // Main dex interface has one default method.
+    testIntermediateWithMainDexList(
+        "interfacemethods",
+        Paths.get(ToolHelper.EXAMPLES_ANDROID_N_BUILD_DIR, "interfacemethods" + JAR_EXTENSION),
+        2,
+        "interfacemethods.I2");
+  }
+
+  private void testIntermediateWithMainDexList(
+      String packageName,
+      int expectedMainDexListSize,
+      String... mainDexClasses)
+      throws Throwable {
+    testIntermediateWithMainDexList(
+        packageName,
+        Paths.get(EXAMPLE_DIR, packageName + JAR_EXTENSION),
+        expectedMainDexListSize,
+        mainDexClasses);
+  }
+
+    private void testIntermediateWithMainDexList(
+        String packageName,
+        Path input,
+        int expectedMainDexListSize,
+        String... mainDexClasses)
+        throws Throwable {
+    int minApi = ANDROID_K_API;
+
+    // Full build, will be used as reference.
+    TestRunner<?> full =
+        test(packageName + "full", packageName, "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withMinApiLevel(minApi)
+            .withOptionConsumer(option -> option.minimalMainDex = true)
+            .withMainDexClass(mainDexClasses);
+    Path fullDexes = temp.getRoot().toPath().resolve(packageName + "full" + ZIP_EXTENSION);
+    full.build(input, fullDexes);
+
+    // Builds with intermediate in both output mode.
+    Path dexesThroughIndexedIntermediate =
+        buildDexThroughIntermediate(packageName, input, OutputMode.Indexed, minApi, mainDexClasses);
+    Path dexesThroughFilePerInputClassIntermediate =
+        buildDexThroughIntermediate(packageName, input, OutputMode.FilePerInputClass, minApi,
+            mainDexClasses);
+
+    // Collect main dex types.
+    DexInspector fullInspector =  getMainDexInspector(fullDexes);
+    DexInspector indexedIntermediateInspector =
+        getMainDexInspector(dexesThroughIndexedIntermediate);
+    DexInspector filePerInputClassIntermediateInspector =
+        getMainDexInspector(dexesThroughFilePerInputClassIntermediate);
+    Collection<String> fullMainClasses = new HashSet<>();
+    fullInspector.forAllClasses(
+        clazz -> fullMainClasses.add(clazz.getFinalDescriptor()));
+    Collection<String> indexedIntermediateMainClasses = new HashSet<>();
+    indexedIntermediateInspector.forAllClasses(
+        clazz -> indexedIntermediateMainClasses.add(clazz.getFinalDescriptor()));
+    Collection<String> filePerInputClassIntermediateMainClasses = new HashSet<>();
+    filePerInputClassIntermediateInspector.forAllClasses(
+        clazz -> filePerInputClassIntermediateMainClasses.add(clazz.getFinalDescriptor()));
+
+    // Check.
+    Assert.assertEquals(expectedMainDexListSize, fullMainClasses.size());
+    Assert.assertEquals(fullMainClasses, indexedIntermediateMainClasses);
+    Assert.assertEquals(fullMainClasses, filePerInputClassIntermediateMainClasses);
+  }
+
+  private Path buildDexThroughIntermediate(
+      String packageName,
+      Path input,
+      OutputMode outputMode,
+      int minApi,
+      String... mainDexClasses)
+      throws Throwable {
+    TestRunner<?> intermediate =
+        test(packageName + "intermediate", packageName, "N/A")
+            .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+            .withMinApiLevel(minApi)
+            .withOptionConsumer(option -> option.outputMode = outputMode)
+            .withIntermediate(true);
+    Path intermediateDex =
+        temp.getRoot().toPath().resolve(packageName + "intermediate" + ZIP_EXTENSION);
+    intermediate.build(input, intermediateDex);
+
+    TestRunner<?> end =
+        test(packageName + "dex", packageName, "N/A")
+            .withOptionConsumer(option -> option.minimalMainDex = true)
+            .withMainDexClass(mainDexClasses)
+            .withMinApiLevel(minApi);
+
+    Path dexesThroughIntermediate =
+        temp.getRoot().toPath().resolve(packageName + "dex" + ZIP_EXTENSION);
+    end.build(intermediateDex, dexesThroughIntermediate);
+    return dexesThroughIntermediate;
+  }
+
   @Override
   D8TestRunner test(String testName, String packageName, String mainClass) {
     return new D8TestRunner(testName, packageName, mainClass);
diff --git a/src/test/java/com/android/tools/r8/JctfTestSpecifications.java b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
index bbf149b..932a61f 100644
--- a/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
+++ b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
@@ -1888,7 +1888,8 @@
           // 1) t03
           // java.lang.AssertionError: expected null, but was:<1>
 
-          .put("lang.ref.PhantomReference.isEnqueued.PhantomReference_isEnqueued_A01", any())
+          .put("lang.ref.PhantomReference.isEnqueued.PhantomReference_isEnqueued_A01",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
           // 1) t04
           // java.lang.AssertionError: reference is not enqueued after 2 sec
 
@@ -1912,7 +1913,8 @@
           // 1) t03
           // java.lang.AssertionError: expected null, but was:<[I@1b32f32>
 
-          .put("lang.ref.WeakReference.isEnqueued.WeakReference_isEnqueued_A01", any())
+          .put("lang.ref.WeakReference.isEnqueued.WeakReference_isEnqueued_A01",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
           // 1) t03
           // java.lang.AssertionError: reference is not enqueued after 2 sec
 
@@ -2730,8 +2732,7 @@
           // 2) t05
           // java.lang.AssertionError: Should throws IndexOutOfBoundsException: 0
 
-          .put("lang.String.getBytesII_BI.String_getBytes_A02",
-              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          .put("lang.String.getBytesII_BI.String_getBytes_A02", any())
           // 1) t01
           // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
 
@@ -4018,6 +4019,11 @@
           // 1) t02
           // java.lang.AssertionError: Exception is not thrown: field: intPublicField, object: com.google.jctf.test.lib.java.lang.reflect.Field.TestStaticFinalPrimitiveField@94fbd35
 
+          .put("lang.reflect.Field.setDoubleLjava_lang_ObjectD.Field_setDouble_A02",
+              match(runtimes(DexVm.ART_4_4_4)))
+          // 1) t01
+          // java.lang.AssertionError: Illegal exception is thrown: java.lang.IllegalAccessException: field is marked 'final', field: doublePublicField, class: class com.google.jctf.test.lib.java.lang.reflect.Field.TestFinalPrimitiveField
+
           .put("lang.reflect.Field.setDoubleLjava_lang_ObjectD.Field_setDouble_A05", any())
           // 1) t01
           // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
@@ -4521,7 +4527,7 @@
           // java.lang.AssertionError: no IllegalThreadStateException 1
 
           .put("lang.String.getBytesLjava_lang_String.String_getBytes_A02",
-              match(runtimes(DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+              match(runtimes(DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1, DexVm.ART_4_4_4)))
           // 1) t01(com.google.jctf.test.lib.java.lang.String.getBytesLjava_lang_String.String_getBytes_A02)
           // java.lang.Exception: Unexpected exception, expected<java.lang.NullPointerException> but was<java.io.UnsupportedEncodingException>
 
@@ -4787,6 +4793,12 @@
           // 1) t02
           // java.lang.AssertionError: java.lang.AssertionError: expected:<7> but was:<6>
 
+          .put("lang.ref.PhantomReference.isEnqueued.PhantomReference_isEnqueued_A01",
+              match(runtimes(DexVm.ART_4_4_4)))
+          .put("lang.ref.WeakReference.isEnqueued.WeakReference_isEnqueued_A01",
+              match(runtimes(DexVm.ART_4_4_4)))
+          // Passes or fails randomly. Check that something is enqueued after 2 seconds.
+
           .build(); // end of flakyWithArt
 
   public static final Multimap<String, TestCondition> timeoutsWithArt =
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
index 33f43a6..0e0fd9d 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
@@ -12,17 +12,20 @@
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.ToolHelper.DexVm;
-import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.DexInspector;
 import com.android.tools.r8.utils.DexInspector.FoundClassSubject;
 import com.android.tools.r8.utils.DexInspector.FoundMethodSubject;
 import com.android.tools.r8.utils.DexInspector.InstructionSubject;
 import com.android.tools.r8.utils.DexInspector.InvokeInstructionSubject;
+import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.OffOrAuto;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.io.ByteStreams;
 import java.io.IOException;
+import java.io.InputStream;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
@@ -30,10 +33,13 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.ExecutionException;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 import java.util.function.UnaryOperator;
 import java.util.stream.Collectors;
+import java.util.zip.ZipException;
+import java.util.zip.ZipFile;
 import org.junit.Assert;
 import org.junit.Rule;
 import org.junit.Test;
@@ -94,6 +100,10 @@
       return self();
     }
 
+    C withMainDexClass(String... classes) {
+      return withBuilderTransformation(builder -> builder.addMainDexClasses(classes));
+    }
+
     C withInterfaceMethodDesugaring(OffOrAuto behavior) {
       return withOptionConsumer(o -> o.interfaceMethodDesugaring = behavior);
     }
@@ -391,4 +401,14 @@
     }
   }
 
+  protected DexInspector getMainDexInspector(Path zip)
+      throws ZipException, IOException, ExecutionException {
+    try (ZipFile zipFile = new ZipFile(zip.toFile())) {
+      try (InputStream in =
+          zipFile.getInputStream(zipFile.getEntry(FileUtils.DEFAULT_DEX_FILENAME))) {
+        return new DexInspector(AndroidApp.fromDexProgramData(ByteStreams.toByteArray(in)));
+      }
+    }
+  }
+
 }
diff --git a/src/test/java/com/android/tools/r8/optimize/R8DebugStrippingTest.java b/src/test/java/com/android/tools/r8/optimize/R8DebugStrippingTest.java
deleted file mode 100644
index 6609ff2..0000000
--- a/src/test/java/com/android/tools/r8/optimize/R8DebugStrippingTest.java
+++ /dev/null
@@ -1,239 +0,0 @@
-// Copyright (c) 2016, 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.optimize;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotSame;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import com.android.tools.r8.CompilationException;
-import com.android.tools.r8.R8Command;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.DexVm;
-import com.android.tools.r8.graph.DexCode;
-import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.naming.ClassNameMapper;
-import com.android.tools.r8.naming.ClassNaming;
-import com.android.tools.r8.naming.MemberNaming;
-import com.android.tools.r8.naming.MemberNaming.InlineInformation;
-import com.android.tools.r8.naming.MemberNaming.MethodSignature;
-import com.android.tools.r8.naming.MemberNaming.Signature;
-import com.android.tools.r8.naming.ProguardMapReader;
-import com.android.tools.r8.shaking.ProguardRuleParserException;
-import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.DexInspector;
-import com.android.tools.r8.utils.DexInspector.ClassSubject;
-import com.google.common.collect.BiMap;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameter;
-import org.junit.runners.Parameterized.Parameters;
-
-@RunWith(Parameterized.class)
-public class R8DebugStrippingTest {
-
-  private static final String ROOT = ToolHelper.EXAMPLES_BUILD_DIR;
-  private static final String EXAMPLE_DEX = "throwing/classes.dex";
-  private static final String EXAMPLE_MAP = "throwing/throwing.map";
-  private static final String EXAMPLE_CLASS = "throwing.Throwing";
-  private static final String EXAMPLE_JAVA = "Throwing.java";
-
-  private static final String MAIN_NAME = "main";
-  private static final String[] MAIN_PARAMETERS = new String[]{"java.lang.String[]"};
-  private static final String VOID_RETURN = "void";
-
-  private static final String OTHER_NAME = "throwInAFunctionThatIsNotInlinedAndCalledTwice";
-  private static final String[] NO_PARAMETERS = new String[0];
-  private static final String INT_RETURN = "int";
-
-  private static final String THIRD_NAME = "aFunctionThatCallsAnInlinedMethodThatThrows";
-  private static final String[] LIST_PARAMETER = new String[]{"java.util.List"};
-
-  private static final String FORTH_NAME = "anotherFunctionThatCallsAnInlinedMethodThatThrows";
-  private static final String[] STRING_PARAMETER = new String[]{"java.lang.String"};
-
-  private static final Map<String, Signature> SIGNATURE_MAP = ImmutableMap.of(
-      MAIN_NAME, new MethodSignature(MAIN_NAME, VOID_RETURN, MAIN_PARAMETERS),
-      OTHER_NAME, new MethodSignature(OTHER_NAME, INT_RETURN, NO_PARAMETERS),
-      THIRD_NAME, new MethodSignature(THIRD_NAME, INT_RETURN, LIST_PARAMETER),
-      FORTH_NAME, new MethodSignature(FORTH_NAME, INT_RETURN, STRING_PARAMETER)
-  );
-
-  private ClassNameMapper mapper;
-
-  @Parameter(0)
-  public boolean compressRanges;
-
-  @Rule
-  public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
-
-  @Before
-  public void loadRangeInformation() throws IOException {
-    mapper = ProguardMapReader.mapperFromFile(Paths.get(ROOT, EXAMPLE_MAP));
-  }
-
-  @Parameters(name = "compressLineNumers={0}")
-  public static Object[] parameters() {
-    return new Object[]{true, false};
-  }
-
-  @Test
-  public void testStackTraces()
-      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
-
-    // Temporary directory for R8 output.
-    Path out = temp.getRoot().toPath();
-
-    R8Command command =
-        R8Command.builder()
-            .addProgramFiles(Paths.get(ROOT, EXAMPLE_DEX))
-            .setOutputPath(out)
-            .setProguardMapFile(Paths.get(ROOT, EXAMPLE_MAP))
-            .build();
-
-    // Generate R8 processed version.
-    AndroidApp result =
-        ToolHelper.runR8(command, (options) -> options.skipDebugLineNumberOpt = !compressRanges);
-
-    ClassNameMapper classNameMapper;
-    try (InputStream is = result.getProguardMap()) {
-      classNameMapper = ProguardMapReader.mapperFromInputStream(is);
-    }
-    if (compressRanges) {
-      classNameMapper.forAllClassNamings(this::ensureRangesAreUniquePerClass);
-    }
-
-    if (!ToolHelper.artSupported()) {
-      return;
-    }
-    // Run art on original.
-    String originalOutput =
-        ToolHelper.runArtNoVerificationErrors(ROOT + EXAMPLE_DEX, EXAMPLE_CLASS);
-    // Run art on R8 processed version.
-    String otherOutput =
-        ToolHelper.runArtNoVerificationErrors(out + "/classes.dex", EXAMPLE_CLASS);
-    // Check that exceptions are in same range
-    assertStacktracesMatchRanges(originalOutput, otherOutput, classNameMapper);
-
-    // Check that we have debug information in all the places required.
-    DexInspector inspector = new DexInspector(out.resolve("classes.dex"));
-    BiMap<String, String> obfuscationMap
-        = classNameMapper.getObfuscatedToOriginalMapping().inverse();
-    ClassSubject overloaded = inspector.clazz(obfuscationMap.get("throwing.Overloaded"));
-    assertTrue(overloaded.isPresent());
-    ensureDebugInfosExist(overloaded);
-  }
-
-  private void ensureDebugInfosExist(ClassSubject overloaded) {
-    final Map<DexString, Boolean> hasDebugInfo = Maps.newIdentityHashMap();
-    overloaded.forAllMethods(method -> {
-          if (!method.isAbstract()) {
-            DexCode code = method.getMethod().getCode().asDexCode();
-            DexString name = method.getMethod().method.name;
-            Boolean previous = hasDebugInfo.get(name);
-            boolean current = code.getDebugInfo() != null;
-            // If we have seen one before, it should be the same as now.
-            assertTrue(previous == null || (previous == current));
-            hasDebugInfo.put(name, current);
-          }
-        }
-    );
-  }
-
-  private void ensureRangesAreUniquePerClass(ClassNaming naming) {
-    final Map<String, Set<Integer>> rangeMap = new HashMap<>();
-    naming.forAllMemberNaming(memberNaming -> {
-      if (memberNaming.isMethodNaming()) {
-        if (memberNaming.topLevelRange != MemberNaming.fakeZeroRange) {
-          int startLine = memberNaming.topLevelRange.from;
-          Set<Integer> used = rangeMap
-              .computeIfAbsent(memberNaming.getRenamedName(), any -> new HashSet<>());
-          assertFalse(used.contains(startLine));
-          used.add(startLine);
-        }
-      }
-    });
-  }
-
-  private String extractRangeIndex(String line, ClassNameMapper mapper) {
-    int position = line.lastIndexOf(EXAMPLE_JAVA);
-    if (position == -1 && ToolHelper.getDexVm() == DexVm.ART_4_4_4) {
-      assert line.contains("dalvik.system.NativeStart.main(Native Method)");
-      return "Native Method";
-    }
-    assertNotSame("Malformed stackframe: " + line, -1, position);
-    String numberPart = line.substring(position + EXAMPLE_JAVA.length() + 1, line.lastIndexOf(')'));
-    int number = Integer.parseInt(numberPart);
-    // Search the signature map for all signatures that actually match. We do this by first looking
-    // up the renamed signature and then checking whether it is contained in the line. We prepend
-    // the class name to make sure we do not match random characters in the line.
-    for (Entry<String, Signature> entry : SIGNATURE_MAP.entrySet()) {
-      MemberNaming naming = mapper.getClassNaming(EXAMPLE_CLASS)
-          .lookupByOriginalSignature(entry.getValue());
-      if (!line.contains(EXAMPLE_CLASS + "." + naming.getRenamedName())) {
-        continue;
-      }
-      if (naming.topLevelRange.contains(number)) {
-        return entry.getKey() + ":" + 0;
-      }
-      int rangeNo = 1;
-      for (InlineInformation inlineInformation : naming.inlineInformation) {
-        if (inlineInformation.inlinedRange.contains(number)) {
-          return entry.getKey() + ":" + rangeNo;
-        }
-        rangeNo++;
-      }
-    }
-    fail("Number not in any range " + number);
-    return null;
-  }
-
-  private MemberNaming selectRanges(String line, ClassNameMapper mapper) {
-    Signature signature;
-    for (Entry<String, Signature> entry : SIGNATURE_MAP.entrySet()) {
-      if (line.contains(entry.getKey())) {
-        return mapper.getClassNaming(EXAMPLE_CLASS).lookup(entry.getValue());
-      }
-    }
-    Assert.fail("unknown method in line " + line);
-    return null;
-  }
-
-  private void assertStacktracesMatchRanges(String before, String after,
-      ClassNameMapper newMapper) {
-    String[] beforeLines = before.split("\n");
-    String[] afterLines = after.split("\n");
-    assertEquals("Output length differs", beforeLines.length,
-        afterLines.length);
-    for (int i = 0; i < beforeLines.length; i++) {
-      if (!beforeLines[i].startsWith("FRAME:")) {
-        continue;
-      }
-      String beforeLine = beforeLines[i];
-      String expected = extractRangeIndex(beforeLine, mapper);
-      String afterLine = afterLines[i];
-      String generated = extractRangeIndex(afterLine, newMapper);
-      assertEquals("Ranges match", expected, generated);
-    }
-  }
-}