Merge commit 'd00587f9f67424f38d5fd601da0574091b238b22' into dev-release
diff --git a/build.gradle b/build.gradle
index f4881c2..1077080 100644
--- a/build.gradle
+++ b/build.gradle
@@ -108,12 +108,6 @@
         }
         output.resourcesDir = 'build/classes/debugTestResourcesJava8'
     }
-    debugTestResourcesKotlin {
-        java {
-            srcDirs = ['src/test/debugTestResourcesKotlin']
-        }
-        output.resourcesDir = 'build/classes/debugTestResourcesKotlin'
-    }
     examples {
         java {
             srcDirs = ['src/test/examples']
@@ -280,7 +274,6 @@
     supportLibs "com.android.support.test.espresso:espresso-core:$espressoVersion"
     apiUsageSampleCompile sourceSets.main.output
     apiUsageSampleCompile "com.google.guava:guava:$guavaVersion"
-    debugTestResourcesKotlinCompileOnly "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
     examplesKotlinCompileOnly "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
     kotlinR8TestResourcesCompileOnly "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
     errorprone("com.google.errorprone:error_prone_core:$errorproneVersion")
@@ -1222,16 +1215,9 @@
         from "build/test/debugTestResourcesJava8/classes"
         include "**/*.class"
     }
-    def kotlinResourcesDir = file("src/test/debugTestResourcesKotlin")
-    def kotlinHostJar = "debug_test_resources_kotlin.jar"
-    task "jar_debugTestResourcesKotlin"(type: kotlin.Kotlinc, dependsOn: downloadDeps) {
-        source = fileTree(dir: kotlinResourcesDir, include: '**/*.kt')
-        destination = file("build/test/${kotlinHostJar}")
-    }
     dependsOn downloadDeps
     dependsOn jar_debugTestResources
     dependsOn jar_debugTestResourcesJava8
-    dependsOn jar_debugTestResourcesKotlin
 }
 
 // Examples used by tests, where Android specific APIs are used.
diff --git a/src/library_desugar/desugar_jdk_libs.json b/src/library_desugar/desugar_jdk_libs.json
index a859aa1..dc7ac7b 100644
--- a/src/library_desugar/desugar_jdk_libs.json
+++ b/src/library_desugar/desugar_jdk_libs.json
@@ -2,7 +2,7 @@
   "configuration_format_version": 3,
   "group_id" : "com.tools.android",
   "artifact_id" : "desugar_jdk_libs",
-  "version": "1.1.3",
+  "version": "1.1.4",
   "required_compilation_api_level": 26,
   "synthesized_library_classes_package_prefix": "j$.",
   "support_all_callbacks_from_library": true,
diff --git a/src/library_desugar/desugar_jdk_libs_alternative_3.json b/src/library_desugar/desugar_jdk_libs_alternative_3.json
index cb7f478..e23a9b8 100644
--- a/src/library_desugar/desugar_jdk_libs_alternative_3.json
+++ b/src/library_desugar/desugar_jdk_libs_alternative_3.json
@@ -2,7 +2,7 @@
   "configuration_format_version": 3,
   "group_id" : "com.tools.android",
   "artifact_id" : "desugar_jdk_libs_alternative_3",
-  "version": "1.2.0",
+  "version": "1.2.1",
   "required_compilation_api_level": 26,
   "synthesized_library_classes_package_prefix": "j$.",
   "support_all_callbacks_from_library": false,
@@ -254,6 +254,7 @@
     "-keepclassmembers class j$.util.DoubleSummaryStatistics { long count; double sum; double min; double max; }",
     "-keepattributes Signature",
     "-keepattributes EnclosingMethod",
-    "-keepattributes InnerClasses"
+    "-keepattributes InnerClasses",
+    "-dontwarn sun.misc.Unsafe"
   ]
 }
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index b552bb2..a085428 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -152,6 +152,9 @@
     InternalOptions internal = new InternalOptions(factory, getReporter());
     assert !internal.debug;
     internal.debug = getMode() == CompilationMode.DEBUG;
+    // TODO(b/180903899): Remove once -dontwarn sun.misc.Unsafe is in place.
+    assert !internal.ignoreMissingClasses;
+    internal.ignoreMissingClasses = true;
     assert internal.mainDexListConsumer == null;
     assert !internal.minimalMainDex;
     internal.minApiLevel = getMinApiLevel();
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 126fb87..9158d3b 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -90,7 +90,6 @@
 import com.android.tools.r8.shaking.EnqueuerResult;
 import com.android.tools.r8.shaking.MainDexInfo;
 import com.android.tools.r8.shaking.MainDexListBuilder;
-import com.android.tools.r8.shaking.MissingClasses;
 import com.android.tools.r8.shaking.ProguardConfigurationRule;
 import com.android.tools.r8.shaking.ProguardConfigurationUtils;
 import com.android.tools.r8.shaking.RootSetUtils.MainDexRootSet;
@@ -319,18 +318,6 @@
       RuntimeTypeCheckInfo.Builder classMergingEnqueuerExtensionBuilder =
           new RuntimeTypeCheckInfo.Builder(appView.dexItemFactory());
       try {
-        // TODO(b/154849103): Remove once reported by the Enqueuer.
-        if (!appView.testing().enableExperimentalMissingClassesReporting) {
-          appView.setAppInfo(
-              appView
-                  .appInfo()
-                  .rebuildWithClassHierarchy(
-                      MissingClasses.builderForInitialMissingClasses()
-                          .legacyAddNewMissingClasses(
-                              new SubtypingInfo(appView).getMissingClasses())
-                          .reportMissingClasses(appView)));
-        }
-
         // Add synthesized -assumenosideeffects from min api if relevant.
         if (options.isGeneratingDex()) {
           if (!ProguardConfigurationUtils.hasExplicitAssumeValuesOrAssumeNoSideEffectsRuleForMinSdk(
diff --git a/src/main/java/com/android/tools/r8/diagnostic/internal/MissingClassAccessContexts.java b/src/main/java/com/android/tools/r8/diagnostic/internal/MissingClassAccessContexts.java
deleted file mode 100644
index 23abebb..0000000
--- a/src/main/java/com/android/tools/r8/diagnostic/internal/MissingClassAccessContexts.java
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright (c) 2021, 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.diagnostic.internal;
-
-import static com.android.tools.r8.utils.PredicateUtils.not;
-
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexReference;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.ProgramDerivedContext;
-import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.references.FieldReference;
-import com.android.tools.r8.references.MethodReference;
-import com.android.tools.r8.utils.FieldReferenceUtils;
-import com.android.tools.r8.utils.MethodReferenceUtils;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
-import java.util.function.Function;
-
-public class MissingClassAccessContexts {
-
-  private ImmutableSet<ClassReference> classContexts;
-  private ImmutableSet<FieldReference> fieldContexts;
-  private ImmutableSet<MethodReference> methodContexts;
-
-  private MissingClassAccessContexts(
-      ImmutableSet<ClassReference> classContexts,
-      ImmutableSet<FieldReference> fieldContexts,
-      ImmutableSet<MethodReference> methodContexts) {
-    this.classContexts = classContexts;
-    this.fieldContexts = fieldContexts;
-    this.methodContexts = methodContexts;
-  }
-
-  static Builder builder() {
-    return new Builder();
-  }
-
-  String getReferencedFromMessageSuffix(ClassReference missingClass) {
-    if (!fieldContexts.isEmpty()) {
-      return " (referenced from: "
-          + FieldReferenceUtils.toSourceString(fieldContexts.iterator().next())
-          + ")";
-    }
-    if (!methodContexts.isEmpty()) {
-      return " (referenced from: "
-          + MethodReferenceUtils.toSourceString(methodContexts.iterator().next())
-          + ")";
-    }
-    // TODO(b/175543745): The legacy reporting is context insensitive, and therefore uses the
-    //  missing classes as their own context. Once legacy reporting is removed, this should be
-    //  simplified to taking the first context.
-    Optional<ClassReference> classContext =
-        classContexts.stream().filter(not(missingClass::equals)).findFirst();
-    return classContext
-        .map(classReference -> " (referenced from: " + classReference.getTypeName() + ")")
-        .orElse("");
-  }
-
-  static class Builder {
-
-    private final Set<DexReference> contexts = Sets.newIdentityHashSet();
-
-    Builder addAll(Set<ProgramDerivedContext> contexts) {
-      for (ProgramDerivedContext context : contexts) {
-        this.contexts.add(context.getContext().getReference());
-      }
-      return this;
-    }
-
-    // TODO(b/179249745): Sort on demand in getReferencedFromMessageSuffix() instead.
-    MissingClassAccessContexts build() {
-      // Sort the contexts for deterministic reporting.
-      List<DexType> classContexts = new ArrayList<>();
-      List<DexField> fieldContexts = new ArrayList<>();
-      List<DexMethod> methodContexts = new ArrayList<>();
-      contexts.forEach(
-          context -> context.apply(classContexts::add, fieldContexts::add, methodContexts::add));
-      Collections.sort(classContexts);
-      Collections.sort(fieldContexts);
-      Collections.sort(methodContexts);
-
-      // Build immutable sets (which preserve insertion order) from the sorted lists, mapping each
-      // DexType, DexField, and DexMethod to ClassReference, FieldReference, and MethodReference,
-      // respectively.
-      return new MissingClassAccessContexts(
-          toImmutableSet(classContexts, DexType::asClassReference),
-          toImmutableSet(fieldContexts, DexField::asFieldReference),
-          toImmutableSet(methodContexts, DexMethod::asMethodReference));
-    }
-
-    private <S, T> ImmutableSet<T> toImmutableSet(List<S> list, Function<S, T> fn) {
-      ImmutableSet.Builder<T> builder = ImmutableSet.builder();
-      list.forEach(element -> builder.add(fn.apply(element)));
-      return builder.build();
-    }
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionInfoUtils.java b/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionInfoUtils.java
index 29d008f..b94dbf1 100644
--- a/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionInfoUtils.java
+++ b/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionInfoUtils.java
@@ -29,34 +29,31 @@
           }
           if (other.isMissingField()) {
             return ClassReferenceUtils.compare(
-                classReference, other.asMissingField().getFieldReference().getHolderClass());
+                classReference, other.asMissingField().getFieldReference());
           }
           return ClassReferenceUtils.compare(
-              classReference, other.asMissingMethod().getMethodReference().getHolderClass());
+              classReference, other.asMissingMethod().getMethodReference());
         }
         if (info.isMissingField()) {
           FieldReference fieldReference = info.asMissingField().getFieldReference();
           if (other.isMissingClass()) {
-            return ClassReferenceUtils.compare(
-                fieldReference.getHolderClass(), other.asMissingClass().getClassReference());
+            return FieldReferenceUtils.compare(
+                fieldReference, other.asMissingClass().getClassReference());
           }
           if (other.isMissingField()) {
             return FieldReferenceUtils.compare(
                 fieldReference, other.asMissingField().getFieldReference());
           }
-          return ClassReferenceUtils.compare(
-              fieldReference.getHolderClass(),
-              other.asMissingMethod().getMethodReference().getHolderClass());
+          return FieldReferenceUtils.compare(
+              fieldReference, other.asMissingMethod().getMethodReference());
         }
         MethodReference methodReference = info.asMissingMethod().getMethodReference();
         if (other.isMissingClass()) {
-          return ClassReferenceUtils.compare(
-              methodReference.getHolderClass(), other.asMissingClass().getClassReference());
+          return MethodReferenceUtils.compare(
+              methodReference, other.asMissingClass().getClassReference());
         }
         if (other.isMissingField()) {
-          ClassReferenceUtils.compare(
-              methodReference.getHolderClass(),
-              other.asMissingField().getFieldReference().getHolderClass());
+          MethodReferenceUtils.compare(methodReference, other.asMissingField().getFieldReference());
         }
         return MethodReferenceUtils.compare(
             methodReference, other.asMissingMethod().getMethodReference());
diff --git a/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionsDiagnosticImpl.java b/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionsDiagnosticImpl.java
index 5da21fd..7be87b6 100644
--- a/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionsDiagnosticImpl.java
+++ b/src/main/java/com/android/tools/r8/diagnostic/internal/MissingDefinitionsDiagnosticImpl.java
@@ -118,17 +118,15 @@
                   missingDefinitionMethodContext.getMethodReference(),
                   getMethodReferenceComparator()));
     }
+    assert classContext.isSet() || fieldContext.isSet() || methodContext.isSet();
     if (fieldContext.isSet()) {
       writeReferencedFromSuffix(
           builder, missingDefinitionInfo, FieldReferenceUtils.toSourceString(fieldContext.get()));
     } else if (methodContext.isSet()) {
       writeReferencedFromSuffix(
           builder, missingDefinitionInfo, MethodReferenceUtils.toSourceString(methodContext.get()));
-    } else if (classContext.isSet()) {
-      writeReferencedFromSuffix(builder, missingDefinitionInfo, classContext.get().getTypeName());
     } else {
-      // TODO(b/175543745): Once legacy reporting is removed this should never happen.
-      builder.append(" (referenced from: <not known>)");
+      writeReferencedFromSuffix(builder, missingDefinitionInfo, classContext.get().getTypeName());
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java b/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java
index d9b17f2..2fa3dfb 100644
--- a/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java
@@ -3,13 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
+import com.android.tools.r8.utils.structural.StructuralItem;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
 import java.util.ArrayDeque;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.Deque;
 import java.util.HashSet;
 import java.util.IdentityHashMap;
@@ -37,10 +37,6 @@
   // Map from types to their subtypes.
   private final Map<DexType, ImmutableSet<DexType>> subtypeMap = new IdentityHashMap<>();
 
-  // TODO(b/154849103): Don't compute these here.
-  // Set of missing classes, discovered during subtypeMap computation.
-  private final Set<DexType> missingClasses = Sets.newIdentityHashSet();
-
   public SubtypingInfo(AppView<? extends AppInfoWithClassHierarchy> appView) {
     this(appView.appInfo());
   }
@@ -55,18 +51,6 @@
     populateSubtypeMap(classes, definitions::definitionFor, factory);
   }
 
-  public boolean verifyEquals(Collection<DexClass> classes, DexDefinitionSupplier definitions) {
-    SubtypingInfo subtypingInfo = new SubtypingInfo(classes, definitions);
-    assert typeInfo.equals(subtypingInfo.typeInfo);
-    assert subtypeMap.keySet().equals(subtypingInfo.subtypeMap.keySet());
-    subtypeMap.forEach(
-        (key, value) -> {
-          assert subtypingInfo.subtypeMap.get(key).equals(value);
-        });
-    assert missingClasses.equals(subtypingInfo.missingClasses);
-    return true;
-  }
-
   private void populateSuperType(
       Map<DexType, Set<DexType>> map,
       DexType superType,
@@ -113,9 +97,6 @@
         getTypeInfo(holder).tagAsInterface();
       }
     } else {
-      if (baseClass.isProgramClass() || baseClass.isClasspathClass()) {
-        missingClasses.add(holder);
-      }
       // The subtype chain is broken, at least make this type a subtype of Object.
       if (holder != dexItemFactory().objectType) {
         getTypeInfo(dexItemFactory().objectType).addDirectSubtype(getTypeInfo(holder));
@@ -181,10 +162,6 @@
     return true;
   }
 
-  public Set<DexType> getMissingClasses() {
-    return Collections.unmodifiableSet(missingClasses);
-  }
-
   public Set<DexType> subtypes(DexType type) {
     assert type.isClassType();
     ImmutableSet<DexType> subtypes = subtypeMap.get(type);
@@ -251,7 +228,7 @@
     int hierarchyLevel = UNKNOWN_LEVEL;
     /**
      * Set of direct subtypes. This set has to remain sorted to ensure determinism. The actual
-     * sorting is not important but {@link DexType#slowCompareTo(DexType)} works well.
+     * sorting is not important but {@link DexType#compareTo(StructuralItem)} works well.
      */
     Set<DexType> directSubtypes = NO_DIRECT_SUBTYPE;
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index 998b47c..0d5cd1c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -76,22 +76,16 @@
     }
 
     CfInvoke invoke = instruction.asInvoke();
-    DexMethod invokedMethod = invoke.getMethod();
-    if (!needsDesugaring(invokedMethod)) {
-      return null;
-    }
-
-    return getMethodProviderOrNull(invokedMethod)
-        .rewriteInvoke(invoke, appView, eventConsumer, methodProcessingContext);
+    MethodProvider methodProvider = getMethodProviderOrNull(invoke.getMethod());
+    return methodProvider != null
+        ? methodProvider.rewriteInvoke(invoke, appView, eventConsumer, methodProcessingContext)
+        : null;
   }
 
   @Override
   public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
-    return instruction.isInvoke() && needsDesugaring(instruction.asInvoke().getMethod());
-  }
-
-  public boolean needsDesugaring(DexMethod method) {
-    return getMethodProviderOrNull(method) != null;
+    return instruction.isInvoke()
+        && getMethodProviderOrNull(instruction.asInvoke().getMethod()) != null;
   }
 
   public static List<DexMethod> generateListOfBackportedMethods(
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 d1cd87a..cbab081 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
@@ -237,9 +237,8 @@
       maxLocals += valueType.requiredRegisters();
       DexType expectedParamType = implReceiverAndArgs.get(i + capturedValues);
       maxStack +=
-          valueType.requiredRegisters()
-              + prepareParameterValue(
-                  erasedParams[i], enforcedParams[i], expectedParamType, instructions, factory);
+          prepareParameterValue(
+              erasedParams[i], enforcedParams[i], expectedParamType, instructions, factory);
     }
 
     instructions.add(
@@ -259,13 +258,15 @@
     } else {
       // Either the new instance or the called-method result is on top of stack.
       assert constructorTarget || !methodToCallReturnType.isVoidType();
-      maxStack +=
-          prepareReturnValue(
-              erasedReturnType,
-              enforcedReturnType,
-              constructorTarget ? methodToCall.holder : methodToCallReturnType,
-              instructions,
-              factory);
+      maxStack =
+          Math.max(
+              maxStack,
+              prepareReturnValue(
+                  erasedReturnType,
+                  enforcedReturnType,
+                  constructorTarget ? methodToCall.holder : methodToCallReturnType,
+                  instructions,
+                  factory));
       instructions.add(new CfReturn(ValueType.fromDexType(enforcedReturnType)));
     }
 
@@ -360,12 +361,9 @@
       Builder<CfInstruction> instructions,
       DexItemFactory factory) {
     internalAdjustType(fromType, toType, returnType, instructions, factory);
-    int inSize = ValueType.fromDexType(fromType).requiredRegisters();
-    int outSize = ValueType.fromDexType(toType).requiredRegisters();
-    if (outSize > inSize) {
-      return outSize - inSize;
-    }
-    return 0;
+    return Math.max(
+        ValueType.fromDexType(fromType).requiredRegisters(),
+        ValueType.fromDexType(toType).requiredRegisters());
   }
 
   private static void internalAdjustType(
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 76fad7d..0b02310 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retrace.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.retrace;
 
-import static com.android.tools.r8.retrace.internal.RetraceUtils.firstNonWhiteSpaceCharacterFromIndex;
 import static com.android.tools.r8.utils.ExceptionUtils.failWithFakeEntry;
 
 import com.android.tools.r8.Diagnostic;
@@ -12,12 +11,11 @@
 import com.android.tools.r8.Keep;
 import com.android.tools.r8.Version;
 import com.android.tools.r8.retrace.RetraceCommand.Builder;
-import com.android.tools.r8.retrace.internal.PlainStackTraceVisitor;
+import com.android.tools.r8.retrace.internal.PlainStackTraceLineParser;
 import com.android.tools.r8.retrace.internal.RetraceAbortException;
-import com.android.tools.r8.retrace.internal.RetraceRegularExpression;
-import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy;
-import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.retrace.internal.StackTraceRegularExpressionParser;
 import com.android.tools.r8.utils.ExceptionDiagnostic;
+import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.OptionsParsing;
 import com.android.tools.r8.utils.OptionsParsing.ParseContext;
 import com.android.tools.r8.utils.StringDiagnostic;
@@ -33,13 +31,13 @@
 import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Scanner;
-import java.util.Set;
-import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
 
 /**
  * A retrace tool for obfuscated stack traces.
@@ -48,14 +46,7 @@
  * tool.
  */
 @Keep
-public class Retrace {
-
-  // This is a slight modification of the default regular expression shown for proguard retrace
-  // that allow for retracing classes in the form <class>: lorem ipsum...
-  // Seems like Proguard retrace is expecting the form "Caused by: <class>".
-  public static final String DEFAULT_REGULAR_EXPRESSION =
-      "(?:.*?\\bat\\s+%c\\.%m\\s*\\(%s(?::%l)?\\)\\s*(?:~\\[.*\\])?)"
-          + "|(?:(?:(?:%c|.*)?[:\"]\\s+)?%c(?::.*)?)";
+public class Retrace<T, ST extends StackTraceElementProxy<T, ST>> {
 
   public static final String USAGE_MESSAGE =
       StringUtils.lines(
@@ -68,7 +59,6 @@
     Builder builder = RetraceCommand.builder(diagnosticsHandler);
     boolean hasSetProguardMap = false;
     boolean hasSetStackTrace = false;
-    boolean hasSetRegularExpression = false;
     boolean hasSetQuiet = false;
     while (context.head() != null) {
       Boolean help = OptionsParsing.tryParseBoolean(context, "--help");
@@ -97,7 +87,6 @@
       String regex = OptionsParsing.tryParseSingle(context, "--regex", "r");
       if (regex != null && !regex.isEmpty()) {
         builder.setRegularExpression(regex);
-        hasSetRegularExpression = true;
         continue;
       }
       if (!hasSetProguardMap) {
@@ -123,9 +112,6 @@
     if (!hasSetStackTrace) {
       builder.setStackTrace(getStackTraceFromStandardInput(hasSetQuiet));
     }
-    if (!hasSetRegularExpression) {
-      builder.setRegularExpression(DEFAULT_REGULAR_EXPRESSION);
-    }
     return builder;
   }
 
@@ -158,6 +144,96 @@
     }
   }
 
+  private final StackTraceLineParser<T, ST> stackTraceLineParser;
+  private final StackTraceElementProxyRetracer<T, ST> proxyRetracer;
+  private final DiagnosticsHandler diagnosticsHandler;
+  private final boolean isVerbose;
+
+  Retrace(
+      StackTraceLineParser<T, ST> stackTraceLineParser,
+      StackTraceElementProxyRetracer<T, ST> proxyRetracer,
+      DiagnosticsHandler diagnosticsHandler,
+      boolean isVerbose) {
+    this.stackTraceLineParser = stackTraceLineParser;
+    this.proxyRetracer = proxyRetracer;
+    this.diagnosticsHandler = diagnosticsHandler;
+    this.isVerbose = isVerbose;
+  }
+
+  public static <T, ST extends StackTraceElementProxy<T, ST>> Retrace<T, ST> createRetrace(
+      StackTraceLineParser<T, ST> stackTraceLineParser,
+      StackTraceElementProxyRetracer<T, ST> proxyRetracer,
+      DiagnosticsHandler diagnosticsHandler,
+      boolean isVerbose) {
+    return new Retrace<>(stackTraceLineParser, proxyRetracer, diagnosticsHandler, isVerbose);
+  }
+
+  /**
+   * Retraces a stack frame and calls the consumer for each retraced line
+   *
+   * @param stackTrace the stack trace to be retrace
+   * @param retracedFrameConsumer the consumer to accept the retraced stack trace.
+   */
+  public void retraceStackTrace(List<T> stackTrace, Consumer<List<List<T>>> retracedFrameConsumer) {
+    ListUtils.forEachWithIndex(
+        stackTrace,
+        (line, lineNumber) -> {
+          if (line == null) {
+            diagnosticsHandler.error(
+                RetraceInvalidStackTraceLineDiagnostics.createNull(lineNumber));
+            throw new RetraceAbortException();
+          }
+        });
+    stackTrace.forEach(line -> retracedFrameConsumer.accept(retraceFrame(line)));
+  }
+
+  /**
+   * Retraces a stack trace frame with support for splitting up ambiguous results.
+   *
+   * @param stackTraceFrame The frame to retrace that can give rise to ambiguous results
+   * @return A collection of retraced frame where each entry in the outer list is ambiguous
+   */
+  public List<List<T>> retraceFrame(T stackTraceFrame) {
+    Map<RetraceStackTraceProxy<T, ST>, List<T>> ambiguousBlocks = new HashMap<>();
+    List<RetraceStackTraceProxy<T, ST>> ambiguousKeys = new ArrayList<>();
+    ST parsedLine = stackTraceLineParser.parse(stackTraceFrame);
+    proxyRetracer
+        .retrace(parsedLine)
+        .forEach(
+            retracedElement -> {
+              if (retracedElement.isTopFrame() || !retracedElement.hasRetracedClass()) {
+                ambiguousKeys.add(retracedElement);
+                ambiguousBlocks.put(retracedElement, new ArrayList<>());
+              }
+              ambiguousBlocks
+                  .get(ListUtils.last(ambiguousKeys))
+                  .add(parsedLine.toRetracedItem(retracedElement, isVerbose));
+            });
+    Collections.sort(ambiguousKeys);
+    List<List<T>> retracedList = new ArrayList<>();
+    ambiguousKeys.forEach(key -> retracedList.add(ambiguousBlocks.get(key)));
+    return retracedList;
+  }
+
+  /**
+   * Utility method for tracing a single line that also retraces ambiguous lines without being able
+   * to distinguish them. For retracing with ambiguous results separated, use {@link #retraceFrame}
+   *
+   * @param stackTraceLine the stack trace line to retrace
+   * @return the retraced stack trace line
+   */
+  public List<T> retraceLine(T stackTraceLine) {
+    ST parsedLine = stackTraceLineParser.parse(stackTraceLine);
+    return proxyRetracer
+        .retrace(parsedLine)
+        .map(
+            retraceFrame -> {
+              retraceFrame.getOriginalItem().toRetracedItem(retraceFrame, isVerbose);
+              return parsedLine.toRetracedItem(retraceFrame, isVerbose);
+            })
+        .collect(Collectors.toList());
+  }
+
   /**
    * The main entry point for running retrace.
    *
@@ -167,110 +243,33 @@
     try {
       Timing timing = Timing.create("R8 retrace", command.printMemory());
       timing.begin("Read proguard map");
+      RetraceOptions options = command.getOptions();
+      DiagnosticsHandler diagnosticsHandler = options.getDiagnosticsHandler();
       Retracer retracer =
-          Retracer.createDefault(command.proguardMapProducer, command.diagnosticsHandler);
-      timing.end();
-      timing.begin("Parse and Retrace");
-      StackTraceVisitor<StackTraceElementStringProxy> stackTraceVisitor =
-          command.regularExpression != null
-              ? new RetraceRegularExpression(command.stackTrace, command.regularExpression)
-              : new PlainStackTraceVisitor(command.stackTrace, command.diagnosticsHandler);
+          Retracer.createDefault(options.getProguardMapProducer(), diagnosticsHandler);
       timing.end();
       timing.begin("Report result");
-      command.retracedStackTraceConsumer.accept(
-          runInternal(stackTraceVisitor, retracer, command.isVerbose));
+      StringRetrace stringRetrace =
+          new StringRetrace(
+              options.getRegularExpression() == null
+                  ? new PlainStackTraceLineParser()
+                  : new StackTraceRegularExpressionParser(options.getRegularExpression()),
+              StackTraceElementProxyRetracer.createDefault(retracer),
+              diagnosticsHandler,
+              options.isVerbose());
+      command
+          .getRetracedStackTraceConsumer()
+          .accept(stringRetrace.retrace(command.getStackTrace()));
       timing.end();
       if (command.printTimes()) {
         timing.report();
       }
     } catch (InvalidMappingFileException e) {
-      command.diagnosticsHandler.error(new ExceptionDiagnostic(e));
+      command.getOptions().getDiagnosticsHandler().error(new ExceptionDiagnostic(e));
       throw e;
     }
   }
 
-  /**
-   * Entry point for running retrace with default parsing on a stack trace
-   *
-   * @param retracer the retracer used to parse each stack trace frame.
-   * @param stackTrace the stack trace to be parsed.
-   * @param isVerbose if the output should embed verbose information.
-   * @return the retraced strings in flat format
-   */
-  public static List<String> run(Retracer retracer, List<String> stackTrace, boolean isVerbose) {
-    return runInternal(
-        new RetraceRegularExpression(stackTrace, DEFAULT_REGULAR_EXPRESSION), retracer, isVerbose);
-  }
-
-  private static List<String> runInternal(
-      StackTraceVisitor<StackTraceElementStringProxy> stackTraceVisitor,
-      Retracer retracer,
-      boolean isVerbose) {
-    List<String> retracedStrings = new ArrayList<>();
-    Box<StackTraceElementStringProxy> lastReportedFrame = new Box<>();
-    Set<String> seenSetForLastReportedFrame = new HashSet<>();
-    run(
-        stackTraceVisitor,
-        StackTraceElementProxyRetracer.createDefault(retracer),
-        (stackTraceElement, frames) -> {
-          frames.forEach(
-              frame -> {
-                StackTraceElementStringProxy originalItem = frame.getOriginalItem();
-                boolean newFrame =
-                    lastReportedFrame.getAndSet(stackTraceElement) != stackTraceElement;
-                if (newFrame) {
-                  seenSetForLastReportedFrame.clear();
-                }
-                String retracedString = originalItem.toRetracedItem(frame, isVerbose);
-                if (seenSetForLastReportedFrame.add(retracedString)) {
-                  if (frame.isAmbiguous() && !newFrame) {
-                    int firstCharIndex = firstNonWhiteSpaceCharacterFromIndex(retracedString, 0);
-                    retracedString =
-                        retracedString.substring(0, firstCharIndex)
-                            + "<OR> "
-                            + retracedString.substring(firstCharIndex);
-                  }
-                  retracedStrings.add(retracedString);
-                }
-              });
-        });
-    return retracedStrings;
-  }
-
-  /**
-   * @param stackTraceVisitor the stack trace visitor.
-   * @param proxyRetracer the retracer used to parse each stack trace frame.
-   * @param resultConsumer consumer to accept each parsed stack trace frame. The stack-trace element
-   *     is guaranteed to be unique per line.
-   */
-  public static <T extends StackTraceElementProxy<?>> void run(
-      StackTraceVisitor<T> stackTraceVisitor,
-      StackTraceElementProxyRetracer<T> proxyRetracer,
-      BiConsumer<T, List<RetraceStackTraceProxy<T>>> resultConsumer) {
-    stackTraceVisitor.forEach(
-        stackTraceElement -> {
-          Box<List<RetraceStackTraceProxy<T>>> currentList = new Box<>();
-          Map<RetraceStackTraceProxy<T>, List<RetraceStackTraceProxy<T>>> ambiguousBlocks =
-              new HashMap<>();
-          proxyRetracer
-              .retrace(stackTraceElement)
-              .forEach(
-                  retracedElement -> {
-                    if (retracedElement.isTopFrame() || !retracedElement.hasRetracedClass()) {
-                      List<RetraceStackTraceProxy<T>> block = new ArrayList<>();
-                      ambiguousBlocks.put(retracedElement, block);
-                      currentList.set(block);
-                    }
-                    currentList.get().add(retracedElement);
-                  });
-          ambiguousBlocks.keySet().stream()
-              .sorted()
-              .forEach(
-                  topFrame ->
-                      resultConsumer.accept(stackTraceElement, ambiguousBlocks.get(topFrame)));
-        });
-  }
-
   public static void run(String[] args) throws RetraceFailedException {
     // To be compatible with standard retrace and remapper, we translate -arg into --arg.
     String[] mappedArgs = new String[args.length];
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceCommand.java b/src/main/java/com/android/tools/r8/retrace/RetraceCommand.java
index e346e3c..66819e7 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceCommand.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceCommand.java
@@ -6,35 +6,30 @@
 
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.Keep;
+import com.android.tools.r8.retrace.internal.StackTraceRegularExpressionParser;
 import java.util.List;
 import java.util.function.Consumer;
 
 @Keep
 public class RetraceCommand {
 
-  final boolean isVerbose;
-  final String regularExpression;
-  final DiagnosticsHandler diagnosticsHandler;
-  final ProguardMapProducer proguardMapProducer;
-  final List<String> stackTrace;
-  final Consumer<List<String>> retracedStackTraceConsumer;
+  private final List<String> stackTrace;
+  private final Consumer<List<String>> retracedStackTraceConsumer;
+  // Not inheriting to allow for static builder methods.
+  private final RetraceOptions options;
 
   private RetraceCommand(
-      boolean isVerbose,
       String regularExpression,
       DiagnosticsHandler diagnosticsHandler,
       ProguardMapProducer proguardMapProducer,
       List<String> stackTrace,
-      Consumer<List<String>> retracedStackTraceConsumer) {
-    this.isVerbose = isVerbose;
-    this.regularExpression = regularExpression;
-    this.diagnosticsHandler = diagnosticsHandler;
-    this.proguardMapProducer = proguardMapProducer;
+      Consumer<List<String>> retracedStackTraceConsumer,
+      boolean isVerbose) {
+    options =
+        new RetraceOptions(regularExpression, diagnosticsHandler, proguardMapProducer, isVerbose);
     this.stackTrace = stackTrace;
     this.retracedStackTraceConsumer = retracedStackTraceConsumer;
 
-    assert this.diagnosticsHandler != null;
-    assert this.proguardMapProducer != null;
     assert this.stackTrace != null;
     assert this.retracedStackTraceConsumer != null;
   }
@@ -47,6 +42,18 @@
     return System.getProperty("com.android.tools.r8.printmemory") != null;
   }
 
+  public List<String> getStackTrace() {
+    return stackTrace;
+  }
+
+  public Consumer<List<String>> getRetracedStackTraceConsumer() {
+    return retracedStackTraceConsumer;
+  }
+
+  public RetraceOptions getOptions() {
+    return options;
+  }
+
   /**
    * Utility method for obtaining a RetraceCommand builder.
    *
@@ -61,12 +68,13 @@
     return new Builder(new DiagnosticsHandler() {});
   }
 
+  @Keep
   public static class Builder {
 
     private boolean isVerbose;
     private final DiagnosticsHandler diagnosticsHandler;
     private ProguardMapProducer proguardMapProducer;
-    private String regularExpression;
+    private String regularExpression = StackTraceRegularExpressionParser.DEFAULT_REGULAR_EXPRESSION;
     private List<String> stackTrace;
     private Consumer<List<String>> retracedStackTraceConsumer;
 
@@ -92,7 +100,8 @@
 
     /**
      * Set a regular expression for parsing the incoming text. The Regular expression must not use
-     * naming groups and has special wild cards according to proguard retrace.
+     * naming groups and has special wild cards according to proguard retrace. Note, this will
+     * override the default regular expression.
      *
      * @param regularExpression The regular expression to use.
      */
@@ -136,13 +145,12 @@
         throw new RuntimeException("RetracedStackConsumer not specified");
       }
       return new RetraceCommand(
-          isVerbose,
           regularExpression,
           diagnosticsHandler,
           proguardMapProducer,
           stackTrace,
-          retracedStackTraceConsumer);
+          retracedStackTraceConsumer,
+          isVerbose);
     }
   }
-
 }
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceInvalidStackTraceLineDiagnostics.java b/src/main/java/com/android/tools/r8/retrace/RetraceInvalidStackTraceLineDiagnostics.java
index d2a6827..54c8519 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceInvalidStackTraceLineDiagnostics.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceInvalidStackTraceLineDiagnostics.java
@@ -43,9 +43,4 @@
   public static RetraceInvalidStackTraceLineDiagnostics createNull(int lineNumber) {
     return new RetraceInvalidStackTraceLineDiagnostics(lineNumber, NULL_STACK_TRACE_LINE_MESSAGE);
   }
-
-  static RetraceInvalidStackTraceLineDiagnostics createParse(int lineNumber, String line) {
-    return new RetraceInvalidStackTraceLineDiagnostics(
-        lineNumber, String.format(PARSE_STACK_TRACE_LINE_MESSAGE, line));
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceOptions.java b/src/main/java/com/android/tools/r8/retrace/RetraceOptions.java
new file mode 100644
index 0000000..a8e5394
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceOptions.java
@@ -0,0 +1,114 @@
+// Copyright (c) 2021, 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.retrace;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.retrace.internal.StackTraceRegularExpressionParser;
+
+/**
+ * The base options for running retrace with support for continuously retrace strings without
+ * parsing the proguard map multiple times.
+ */
+@Keep
+public class RetraceOptions {
+
+  private final boolean isVerbose;
+  private final String regularExpression;
+  private final DiagnosticsHandler diagnosticsHandler;
+  private final ProguardMapProducer proguardMapProducer;
+
+  RetraceOptions(
+      String regularExpression,
+      DiagnosticsHandler diagnosticsHandler,
+      ProguardMapProducer proguardMapProducer,
+      boolean isVerbose) {
+    this.regularExpression = regularExpression;
+    this.diagnosticsHandler = diagnosticsHandler;
+    this.proguardMapProducer = proguardMapProducer;
+    this.isVerbose = isVerbose;
+
+    assert diagnosticsHandler != null;
+    assert proguardMapProducer != null;
+  }
+
+  public boolean isVerbose() {
+    return isVerbose;
+  }
+
+  public String getRegularExpression() {
+    return regularExpression;
+  }
+
+  public DiagnosticsHandler getDiagnosticsHandler() {
+    return diagnosticsHandler;
+  }
+
+  public ProguardMapProducer getProguardMapProducer() {
+    return proguardMapProducer;
+  }
+
+  /** Utility method for obtaining a builder with a default diagnostics handler. */
+  public static Builder builder() {
+    return builder(new DiagnosticsHandler() {});
+  }
+
+  /** Utility method for obtaining a builder. */
+  public static Builder builder(DiagnosticsHandler diagnosticsHandler) {
+    return new Builder(diagnosticsHandler);
+  }
+
+  @Keep
+  public static class Builder {
+
+    private boolean isVerbose;
+    private final DiagnosticsHandler diagnosticsHandler;
+    private ProguardMapProducer proguardMapProducer;
+    private String regularExpression = StackTraceRegularExpressionParser.DEFAULT_REGULAR_EXPRESSION;
+
+    Builder(DiagnosticsHandler diagnosticsHandler) {
+      this.diagnosticsHandler = diagnosticsHandler;
+    }
+
+    /** Set if the produced stack trace should have additional information. */
+    public Builder setVerbose(boolean verbose) {
+      this.isVerbose = verbose;
+      return this;
+    }
+
+    /**
+     * Set a producer for the proguard mapping contents.
+     *
+     * @param producer Producer for
+     */
+    public Builder setProguardMapProducer(ProguardMapProducer producer) {
+      this.proguardMapProducer = producer;
+      return this;
+    }
+
+    /**
+     * Set a regular expression for parsing the incoming text. The Regular expression must not use
+     * naming groups and has special wild cards according to proguard retrace. Note, this will
+     * override the default regular expression.
+     *
+     * @param regularExpression The regular expression to use.
+     */
+    public Builder setRegularExpression(String regularExpression) {
+      this.regularExpression = regularExpression;
+      return this;
+    }
+
+    public RetraceOptions build() {
+      if (this.diagnosticsHandler == null) {
+        throw new RuntimeException("DiagnosticsHandler not specified");
+      }
+      if (this.proguardMapProducer == null) {
+        throw new RuntimeException("ProguardMapSupplier not specified");
+      }
+      return new RetraceOptions(
+          regularExpression, diagnosticsHandler, proguardMapProducer, isVerbose);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceStackTraceProxy.java b/src/main/java/com/android/tools/r8/retrace/RetraceStackTraceProxy.java
index d9d1375..4eef2fc 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceStackTraceProxy.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceStackTraceProxy.java
@@ -8,8 +8,8 @@
 import java.util.List;
 
 @Keep
-public interface RetraceStackTraceProxy<T extends StackTraceElementProxy<?>>
-    extends Comparable<RetraceStackTraceProxy<T>> {
+public interface RetraceStackTraceProxy<T, ST extends StackTraceElementProxy<T, ST>>
+    extends Comparable<RetraceStackTraceProxy<T, ST>> {
 
   boolean isAmbiguous();
 
@@ -29,7 +29,7 @@
 
   boolean hasMethodArguments();
 
-  T getOriginalItem();
+  ST getOriginalItem();
 
   RetracedClass getRetracedClass();
 
diff --git a/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxy.java b/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxy.java
index 09c1a8f..7cffce0 100644
--- a/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxy.java
+++ b/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxy.java
@@ -8,7 +8,7 @@
 import com.android.tools.r8.references.ClassReference;
 
 @Keep
-public abstract class StackTraceElementProxy<T> {
+public abstract class StackTraceElementProxy<T, ST extends StackTraceElementProxy<T, ST>> {
 
   public abstract boolean hasClassName();
 
@@ -37,4 +37,6 @@
   public abstract String getFieldOrReturnType();
 
   public abstract String getMethodArguments();
+
+  public abstract T toRetracedItem(RetraceStackTraceProxy<T, ST> retracedProxy, boolean verbose);
 }
diff --git a/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxyRetracer.java b/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxyRetracer.java
index 17de5f1..18be7e7 100644
--- a/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxyRetracer.java
+++ b/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxyRetracer.java
@@ -9,12 +9,12 @@
 import java.util.stream.Stream;
 
 @Keep
-public interface StackTraceElementProxyRetracer<T extends StackTraceElementProxy<?>> {
+public interface StackTraceElementProxyRetracer<T, ST extends StackTraceElementProxy<T, ST>> {
 
-  Stream<RetraceStackTraceProxy<T>> retrace(T element);
+  Stream<RetraceStackTraceProxy<T, ST>> retrace(ST element);
 
-  static <T extends StackTraceElementProxy<?>> StackTraceElementProxyRetracer<T> createDefault(
-      Retracer retracer) {
+  static <T, ST extends StackTraceElementProxy<T, ST>>
+      StackTraceElementProxyRetracer<T, ST> createDefault(Retracer retracer) {
     return new StackTraceElementProxyRetracerImpl<>(retracer);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/retrace/StackTraceLineParser.java b/src/main/java/com/android/tools/r8/retrace/StackTraceLineParser.java
new file mode 100644
index 0000000..92d1dca
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/StackTraceLineParser.java
@@ -0,0 +1,20 @@
+// 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.retrace;
+
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy;
+import com.android.tools.r8.retrace.internal.StackTraceRegularExpressionParser;
+
+@Keep
+public interface StackTraceLineParser<T, ST extends StackTraceElementProxy<T, ST>> {
+
+  ST parse(T stackTraceLine);
+
+  static StackTraceLineParser<String, StackTraceElementStringProxy> createRegularExpressionParser(
+      String regularExpression) {
+    return new StackTraceRegularExpressionParser(regularExpression);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/StackTraceVisitor.java b/src/main/java/com/android/tools/r8/retrace/StackTraceVisitor.java
deleted file mode 100644
index bf2de73..0000000
--- a/src/main/java/com/android/tools/r8/retrace/StackTraceVisitor.java
+++ /dev/null
@@ -1,14 +0,0 @@
-// 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.retrace;
-
-import com.android.tools.r8.Keep;
-import java.util.function.Consumer;
-
-@Keep
-public interface StackTraceVisitor<T extends StackTraceElementProxy<?>> {
-
-  void forEach(Consumer<T> consumer);
-}
diff --git a/src/main/java/com/android/tools/r8/retrace/StringRetrace.java b/src/main/java/com/android/tools/r8/retrace/StringRetrace.java
new file mode 100644
index 0000000..957e357
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/StringRetrace.java
@@ -0,0 +1,101 @@
+// Copyright (c) 2021, 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.retrace;
+
+import static com.android.tools.r8.retrace.internal.RetraceUtils.firstNonWhiteSpaceCharacterFromIndex;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Consumer;
+
+/**
+ * Specialized Retrace class for retracing string retraces, with special handling for appending
+ * additional information into the strings, such as OR's for ambiguous lines.
+ */
+@Keep
+public class StringRetrace extends Retrace<String, StackTraceElementStringProxy> {
+
+  StringRetrace(
+      StackTraceLineParser<String, StackTraceElementStringProxy> stackTraceLineParser,
+      StackTraceElementProxyRetracer<String, StackTraceElementStringProxy> proxyRetracer,
+      DiagnosticsHandler diagnosticsHandler,
+      boolean isVerbose) {
+    super(stackTraceLineParser, proxyRetracer, diagnosticsHandler, isVerbose);
+  }
+
+  /**
+   * Default entry point for creating a retrace designed for string input and output.
+   *
+   * @param command the command with information about creating the StringRetrace
+   * @return a StringRetrace object
+   */
+  public static StringRetrace create(RetraceOptions command) {
+    Retracer retracer =
+        Retracer.createDefault(command.getProguardMapProducer(), command.getDiagnosticsHandler());
+    return new StringRetrace(
+        StackTraceLineParser.createRegularExpressionParser(command.getRegularExpression()),
+        StackTraceElementProxyRetracer.createDefault(retracer),
+        command.getDiagnosticsHandler(),
+        command.isVerbose());
+  }
+
+  /**
+   * Retraces a list of stack-traces strings and returns a list. Ambiguous and inline frames will be
+   * appended automatically to the retraced string.
+   *
+   * @param stackTrace the incoming stack trace
+   * @return the retraced stack trace
+   */
+  public List<String> retrace(List<String> stackTrace) {
+    List<String> retracedStrings = new ArrayList<>();
+    retraceStackTrace(stackTrace, result -> joinAmbiguousLines(result, retracedStrings::add));
+    return retracedStrings;
+  }
+
+  /**
+   * Retraces a single stack trace line and returns the potential list of original frames
+   *
+   * @param stackTraceLine the stack trace line to retrace
+   * @return the retraced frames
+   */
+  public List<String> retrace(String stackTraceLine) {
+    List<String> result = new ArrayList<>();
+    joinAmbiguousLines(retraceFrame(stackTraceLine), result::add);
+    return result;
+  }
+
+  private void joinAmbiguousLines(
+      List<List<String>> retracedResult, Consumer<String> joinedConsumer) {
+    assert !retracedResult.isEmpty();
+    List<String> initialResult = retracedResult.get(0);
+    initialResult.forEach(joinedConsumer);
+    if (retracedResult.size() <= 1) {
+      // The result is not ambiguous.
+      return;
+    }
+    Set<String> reportedFrames = new HashSet<>(initialResult);
+    for (int i = 1; i < retracedResult.size(); i++) {
+      List<String> ambiguousResult = retracedResult.get(i);
+      assert !ambiguousResult.isEmpty();
+      String topFrame = ambiguousResult.get(0);
+      if (reportedFrames.add(topFrame)) {
+        ambiguousResult.forEach(
+            retracedString -> {
+              int firstCharIndex = firstNonWhiteSpaceCharacterFromIndex(retracedString, 0);
+              retracedString =
+                  retracedString.substring(0, firstCharIndex)
+                      + "<OR> "
+                      + retracedString.substring(firstCharIndex);
+              joinedConsumer.accept(retracedString);
+            });
+      }
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/PlainStackTraceVisitor.java b/src/main/java/com/android/tools/r8/retrace/internal/PlainStackTraceLineParser.java
similarity index 87%
rename from src/main/java/com/android/tools/r8/retrace/internal/PlainStackTraceVisitor.java
rename to src/main/java/com/android/tools/r8/retrace/internal/PlainStackTraceLineParser.java
index a632f1c..ddaf675 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/PlainStackTraceVisitor.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/PlainStackTraceLineParser.java
@@ -7,34 +7,21 @@
 import static com.android.tools.r8.retrace.internal.RetraceUtils.firstCharFromIndex;
 import static com.android.tools.r8.retrace.internal.RetraceUtils.firstNonWhiteSpaceCharacterFromIndex;
 
-import com.android.tools.r8.DiagnosticsHandler;
-import com.android.tools.r8.retrace.RetraceInvalidStackTraceLineDiagnostics;
-import com.android.tools.r8.retrace.StackTraceVisitor;
+import com.android.tools.r8.retrace.StackTraceLineParser;
 import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy.ClassNameType;
 import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy.StackTraceElementStringProxyBuilder;
 import com.android.tools.r8.utils.DescriptorUtils;
-import java.util.List;
-import java.util.function.Consumer;
 
-public final class PlainStackTraceVisitor
-    implements StackTraceVisitor<StackTraceElementStringProxy> {
+public final class PlainStackTraceLineParser
+    implements StackTraceLineParser<String, StackTraceElementStringProxy> {
 
-  private final List<String> stackTrace;
-  private final DiagnosticsHandler diagnosticsHandler;
-
-  public PlainStackTraceVisitor(List<String> stackTrace, DiagnosticsHandler diagnosticsHandler) {
-    this.stackTrace = stackTrace;
-    this.diagnosticsHandler = diagnosticsHandler;
-  }
+  public PlainStackTraceLineParser() {}
 
   @Override
-  public void forEach(Consumer<StackTraceElementStringProxy> consumer) {
-    for (int i = 0; i < stackTrace.size(); i++) {
-      consumer.accept(parseLine(i + 1, stackTrace.get(i)));
-    }
+  public StackTraceElementStringProxy parse(String stackTraceLine) {
+    return parseLine(stackTraceLine);
   }
 
-
   /**
    * Captures a stack trace line of the following formats:
    *
@@ -193,11 +180,7 @@
     }
   }
 
-  private StackTraceElementStringProxy parseLine(int lineNumber, String line) {
-    if (line == null) {
-      diagnosticsHandler.error(RetraceInvalidStackTraceLineDiagnostics.createNull(lineNumber));
-      throw new RetraceAbortException();
-    }
+  private StackTraceElementStringProxy parseLine(String line) {
     // Most lines are 'at lines' so attempt to parse it first.
     StackTraceElementStringProxy parsedLine = AtLine.tryParse(line);
     if (parsedLine != null) {
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java
index c3453b5..ad01ee2 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java
@@ -27,8 +27,8 @@
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
-public class StackTraceElementProxyRetracerImpl<T extends StackTraceElementProxy<?>>
-    implements StackTraceElementProxyRetracer<T> {
+public class StackTraceElementProxyRetracerImpl<T, ST extends StackTraceElementProxy<T, ST>>
+    implements StackTraceElementProxyRetracer<T, ST> {
 
   private final Retracer retracer;
 
@@ -37,9 +37,11 @@
   }
 
   @Override
-  public Stream<RetraceStackTraceProxy<T>> retrace(T element) {
+  public Stream<RetraceStackTraceProxy<T, ST>> retrace(ST element) {
     if (!element.hasClassName()) {
-      return Stream.of(RetraceStackTraceProxyImpl.builder(element).build());
+      RetraceStackTraceProxyImpl.Builder<T, ST> builder =
+          RetraceStackTraceProxyImpl.builder(element);
+      return Stream.of(builder.build());
     }
     RetraceClassResult classResult = retracer.retraceClass(element.getClassReference());
     if (element.hasMethodName()) {
@@ -51,8 +53,8 @@
     }
   }
 
-  private Stream<RetraceStackTraceProxy<T>> retraceClassOrType(
-      T element, RetraceClassResult classResult) {
+  private Stream<RetraceStackTraceProxy<T, ST>> retraceClassOrType(
+      ST element, RetraceClassResult classResult) {
     return classResult.stream()
         .flatMap(
             classElement ->
@@ -62,7 +64,7 @@
                             retracedMethodArguments(element)
                                 .map(
                                     argumentsConsumer -> {
-                                      RetraceStackTraceProxyImpl.Builder<T> proxy =
+                                      RetraceStackTraceProxyImpl.Builder<T, ST> proxy =
                                           RetraceStackTraceProxyImpl.builder(element)
                                               .setRetracedClass(classElement.getRetracedClass())
                                               .setAmbiguous(classResult.isAmbiguous())
@@ -79,8 +81,8 @@
                                     })));
   }
 
-  private Stream<RetraceStackTraceProxy<T>> retraceMethod(
-      T element, RetraceClassResult classResult) {
+  private Stream<RetraceStackTraceProxy<T, ST>> retraceMethod(
+      ST element, RetraceClassResult classResult) {
     return retraceFieldOrReturnType(element)
         .flatMap(
             fieldOrReturnTypeConsumer ->
@@ -95,11 +97,11 @@
                           return frameResult.stream()
                               .flatMap(
                                   frameElement -> {
-                                    List<RetraceStackTraceProxyImpl<T>> retracedProxies =
+                                    List<RetraceStackTraceProxy<T, ST>> retracedProxies =
                                         new ArrayList<>();
                                     frameElement.visitFrames(
                                         (frame, index) -> {
-                                          RetraceStackTraceProxyImpl.Builder<T> proxy =
+                                          RetraceStackTraceProxyImpl.Builder<T, ST> proxy =
                                               RetraceStackTraceProxyImpl.builder(element)
                                                   .setRetracedClass(frame.getHolderClass())
                                                   .setRetracedMethod(frame)
@@ -126,8 +128,8 @@
                         }));
   }
 
-  private Stream<RetraceStackTraceProxy<T>> retraceField(
-      T element, RetraceClassResult classResult) {
+  private Stream<RetraceStackTraceProxy<T, ST>> retraceField(
+      ST element, RetraceClassResult classResult) {
     return retraceFieldOrReturnType(element)
         .flatMap(
             fieldOrReturnTypeConsumer ->
@@ -139,7 +141,7 @@
                           return retraceFieldResult.stream()
                               .map(
                                   fieldElement -> {
-                                    RetraceStackTraceProxyImpl.Builder<T> proxy =
+                                    RetraceStackTraceProxyImpl.Builder<T, ST> proxy =
                                         RetraceStackTraceProxyImpl.builder(element)
                                             .setRetracedClass(
                                                 fieldElement.getField().getHolderClass())
@@ -159,8 +161,8 @@
                         }));
   }
 
-  private Stream<Consumer<RetraceStackTraceProxyImpl.Builder<T>>> retraceFieldOrReturnType(
-      T element) {
+  private Stream<Consumer<RetraceStackTraceProxyImpl.Builder<T, ST>>> retraceFieldOrReturnType(
+      ST element) {
     if (!element.hasFieldOrReturnType()) {
       return Stream.of(noEffect -> {});
     }
@@ -182,8 +184,8 @@
     }
   }
 
-  private Stream<Consumer<RetraceStackTraceProxyImpl.Builder<T>>> retracedMethodArguments(
-      T element) {
+  private Stream<Consumer<RetraceStackTraceProxyImpl.Builder<T, ST>>> retracedMethodArguments(
+      ST element) {
     if (!element.hasMethodArguments()) {
       return Stream.of(noEffect -> {});
     }
@@ -225,10 +227,10 @@
                 });
   }
 
-  public static class RetraceStackTraceProxyImpl<T extends StackTraceElementProxy<?>>
-      implements RetraceStackTraceProxy<T> {
+  public static class RetraceStackTraceProxyImpl<T, ST extends StackTraceElementProxy<T, ST>>
+      implements RetraceStackTraceProxy<T, ST> {
 
-    private final T originalItem;
+    private final ST originalItem;
     private final RetracedClass retracedClass;
     private final RetracedMethod retracedMethod;
     private final RetracedField retracedField;
@@ -240,7 +242,7 @@
     private final boolean isTopFrame;
 
     private RetraceStackTraceProxyImpl(
-        T originalItem,
+        ST originalItem,
         RetracedClass retracedClass,
         RetracedMethod retracedMethod,
         RetracedField retracedField,
@@ -309,7 +311,7 @@
     }
 
     @Override
-    public T getOriginalItem() {
+    public ST getOriginalItem() {
       return originalItem;
     }
 
@@ -343,7 +345,8 @@
       return sourceFile;
     }
 
-    private static <T extends StackTraceElementProxy<?>> Builder<T> builder(T originalElement) {
+    private static <T, ST extends StackTraceElementProxy<T, ST>> Builder<T, ST> builder(
+        ST originalElement) {
       return new Builder<>(originalElement);
     }
 
@@ -353,7 +356,7 @@
     }
 
     @Override
-    public int compareTo(RetraceStackTraceProxy<T> other) {
+    public int compareTo(RetraceStackTraceProxy<T, ST> other) {
       int classCompare = Boolean.compare(hasRetracedClass(), other.hasRetracedClass());
       if (classCompare != 0) {
         return classCompare;
@@ -395,9 +398,9 @@
       return 0;
     }
 
-    private static class Builder<T extends StackTraceElementProxy<?>> {
+    private static class Builder<T, ST extends StackTraceElementProxy<T, ST>> {
 
-      private final T originalElement;
+      private final ST originalElement;
       private RetracedClass classContext;
       private RetracedMethod methodContext;
       private RetracedField retracedField;
@@ -408,56 +411,56 @@
       private boolean isAmbiguous;
       private boolean isTopFrame;
 
-      private Builder(T originalElement) {
+      private Builder(ST originalElement) {
         this.originalElement = originalElement;
       }
 
-      private Builder<T> setRetracedClass(RetracedClass retracedClass) {
+      private Builder<T, ST> setRetracedClass(RetracedClass retracedClass) {
         this.classContext = retracedClass;
         return this;
       }
 
-      private Builder<T> setRetracedMethod(RetracedMethod methodElement) {
+      private Builder<T, ST> setRetracedMethod(RetracedMethod methodElement) {
         this.methodContext = methodElement;
         return this;
       }
 
-      private Builder<T> setRetracedField(RetracedField retracedField) {
+      private Builder<T, ST> setRetracedField(RetracedField retracedField) {
         this.retracedField = retracedField;
         return this;
       }
 
-      private Builder<T> setRetracedFieldOrReturnType(RetracedType retracedType) {
+      private Builder<T, ST> setRetracedFieldOrReturnType(RetracedType retracedType) {
         this.fieldOrReturnType = retracedType;
         return this;
       }
 
-      private Builder<T> setRetracedMethodArguments(List<RetracedType> arguments) {
+      private Builder<T, ST> setRetracedMethodArguments(List<RetracedType> arguments) {
         this.methodArguments = arguments;
         return this;
       }
 
-      private Builder<T> setSourceFile(String sourceFile) {
+      private Builder<T, ST> setSourceFile(String sourceFile) {
         this.sourceFile = sourceFile;
         return this;
       }
 
-      private Builder<T> setLineNumber(int lineNumber) {
+      private Builder<T, ST> setLineNumber(int lineNumber) {
         this.lineNumber = lineNumber;
         return this;
       }
 
-      private Builder<T> setAmbiguous(boolean ambiguous) {
+      private Builder<T, ST> setAmbiguous(boolean ambiguous) {
         this.isAmbiguous = ambiguous;
         return this;
       }
 
-      private Builder<T> setTopFrame(boolean topFrame) {
+      private Builder<T, ST> setTopFrame(boolean topFrame) {
         isTopFrame = topFrame;
         return this;
       }
 
-      private RetraceStackTraceProxyImpl<T> build() {
+      private RetraceStackTraceProxy<T, ST> build() {
         RetracedClass retracedClass = classContext;
         if (methodContext != null) {
           retracedClass = methodContext.getHolderClass();
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementStringProxy.java b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementStringProxy.java
index 6decd5e..272f19d 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementStringProxy.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementStringProxy.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.retrace.internal;
 
 import static com.android.tools.r8.retrace.internal.RetraceUtils.methodDescriptionFromRetraceMethod;
+import static com.android.tools.r8.retrace.internal.StackTraceElementStringProxy.ClassStringIndex.NO_INDEX;
 import static com.android.tools.r8.retrace.internal.StackTraceElementStringProxy.StringIndex.noIndex;
 
 import com.android.tools.r8.references.ClassReference;
@@ -19,7 +20,8 @@
 import java.util.ArrayList;
 import java.util.List;
 
-public final class StackTraceElementStringProxy extends StackTraceElementProxy<String> {
+public final class StackTraceElementStringProxy
+    extends StackTraceElementProxy<String, StackTraceElementStringProxy> {
 
   private final String line;
   private final List<StringIndex> orderedIndices;
@@ -133,8 +135,9 @@
     return hasMethodArguments() ? getEntryInLine(methodArguments) : null;
   }
 
+  @Override
   public String toRetracedItem(
-      RetraceStackTraceProxy<StackTraceElementStringProxy> retracedProxy, boolean verbose) {
+      RetraceStackTraceProxy<String, StackTraceElementStringProxy> retracedProxy, boolean verbose) {
     StringBuilder sb = new StringBuilder();
     int lastSeenIndex = 0;
     for (StringIndex index : orderedIndices) {
@@ -315,9 +318,6 @@
 
   static class StringIndex {
 
-    private static final ClassStringIndex NO_INDEX =
-        new ClassStringIndex(-1, -1, null, ClassNameType.TYPENAME);
-
     static ClassStringIndex noIndex() {
       return NO_INDEX;
     }
@@ -325,20 +325,14 @@
     protected final int startIndex;
     protected final int endIndex;
     private final TriFunction<
-            RetraceStackTraceProxy<StackTraceElementStringProxy>,
-            StackTraceElementStringProxy,
-            Boolean,
-            String>
+            RetraceStackTraceProxy<String, ?>, StackTraceElementStringProxy, Boolean, String>
         retracedString;
 
     private StringIndex(
         int startIndex,
         int endIndex,
         TriFunction<
-                RetraceStackTraceProxy<StackTraceElementStringProxy>,
-                StackTraceElementStringProxy,
-                Boolean,
-                String>
+                RetraceStackTraceProxy<String, ?>, StackTraceElementStringProxy, Boolean, String>
             retracedString) {
       this.startIndex = startIndex;
       this.endIndex = endIndex;
@@ -352,16 +346,16 @@
 
   static final class ClassStringIndex extends StringIndex {
 
+    static final ClassStringIndex NO_INDEX =
+        new ClassStringIndex(-1, -1, null, ClassNameType.TYPENAME);
+
     private final ClassNameType classNameType;
 
     private ClassStringIndex(
         int startIndex,
         int endIndex,
         TriFunction<
-                RetraceStackTraceProxy<StackTraceElementStringProxy>,
-                StackTraceElementStringProxy,
-                Boolean,
-                String>
+                RetraceStackTraceProxy<String, ?>, StackTraceElementStringProxy, Boolean, String>
             retracedString,
         ClassNameType classNameType) {
       super(startIndex, endIndex, retracedString);
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceRegularExpression.java b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceRegularExpressionParser.java
similarity index 85%
rename from src/main/java/com/android/tools/r8/retrace/internal/RetraceRegularExpression.java
rename to src/main/java/com/android/tools/r8/retrace/internal/StackTraceRegularExpressionParser.java
index 5790d52..fc8d5d9 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceRegularExpression.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceRegularExpressionParser.java
@@ -5,24 +5,31 @@
 package com.android.tools.r8.retrace.internal;
 
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.retrace.StackTraceVisitor;
+import com.android.tools.r8.retrace.StackTraceLineParser;
 import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy.ClassNameType;
 import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy.StackTraceElementStringProxyBuilder;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.function.Consumer;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-public class RetraceRegularExpression implements StackTraceVisitor<StackTraceElementStringProxy> {
+public class StackTraceRegularExpressionParser
+    implements StackTraceLineParser<String, StackTraceElementStringProxy> {
 
-  private final List<String> stackTrace;
-  private final String regularExpression;
+  // This is a slight modification of the default regular expression shown for proguard retrace
+  // that allow for retracing classes in the form <class>: lorem ipsum...
+  // Seems like Proguard retrace is expecting the form "Caused by: <class>".
+  public static final String DEFAULT_REGULAR_EXPRESSION =
+      "(?:.*?\\bat\\s+%c\\.%m\\s*\\(%s(?::%l)?\\)\\s*(?:~\\[.*\\])?)"
+          + "|(?:(?:(?:%c|.*)?[:\"]\\s+)?%c(?::.*)?)";
+
+  private final Pattern compiledPattern;
 
   private static final int NO_MATCH = -1;
 
   private final SourceFileLineNumberGroup sourceFileLineNumberGroup =
       new SourceFileLineNumberGroup();
+  private final List<RegularExpressionGroupHandler> handlers;
   private final TypeNameGroup typeNameGroup = new TypeNameGroup();
   private final BinaryNameGroup binaryNameGroup = new BinaryNameGroup();
   private final SourceFileGroup sourceFileGroup = new SourceFileGroup();
@@ -35,39 +42,35 @@
   private static final String CAPTURE_GROUP_PREFIX = "captureGroup";
   private static final int FIRST_CAPTURE_GROUP_INDEX = 0;
 
-  public RetraceRegularExpression(List<String> stackTrace, String regularExpression) {
-    this.stackTrace = stackTrace;
-    this.regularExpression = regularExpression;
+  public StackTraceRegularExpressionParser() {
+    this(DEFAULT_REGULAR_EXPRESSION);
+  }
+
+  public StackTraceRegularExpressionParser(String regularExpression) {
+    handlers = new ArrayList<>();
+    StringBuilder refinedRegularExpressionBuilder = new StringBuilder();
+    registerGroups(
+        regularExpression, refinedRegularExpressionBuilder, handlers, FIRST_CAPTURE_GROUP_INDEX);
+    compiledPattern = Pattern.compile(refinedRegularExpressionBuilder.toString());
   }
 
   @Override
-  public void forEach(Consumer<StackTraceElementStringProxy> consumer) {
-    List<RegularExpressionGroupHandler> handlers = new ArrayList<>();
-    StringBuilder refinedRegularExpressionBuilder = new StringBuilder();
-    registerGroups(
-        this.regularExpression,
-        refinedRegularExpressionBuilder,
-        handlers,
-        FIRST_CAPTURE_GROUP_INDEX);
-    String refinedRegularExpression = refinedRegularExpressionBuilder.toString();
-    Pattern compiledPattern = Pattern.compile(refinedRegularExpression);
-    for (String string : stackTrace) {
-      StackTraceElementStringProxyBuilder proxyBuilder =
-          StackTraceElementStringProxy.builder(string);
-      Matcher matcher = compiledPattern.matcher(string);
-      if (matcher.matches()) {
-        boolean seenMatchedClassHandler = false;
-        for (RegularExpressionGroupHandler handler : handlers) {
-          if (seenMatchedClassHandler && handler.isClassHandler()) {
-            continue;
-          }
-          if (handler.matchHandler(proxyBuilder, matcher)) {
-            seenMatchedClassHandler |= handler.isClassHandler();
-          }
+  public StackTraceElementStringProxy parse(String stackTraceLine) {
+    StackTraceElementStringProxyBuilder proxyBuilder =
+        StackTraceElementStringProxy.builder(stackTraceLine);
+    Matcher matcher = compiledPattern.matcher(stackTraceLine);
+    if (matcher.matches()) {
+      boolean seenMatchedClassHandler = false;
+      for (RegularExpressionGroupHandler handler : handlers) {
+        if (seenMatchedClassHandler && handler.isClassHandler()) {
+          continue;
+        }
+        if (handler.matchHandler(proxyBuilder, matcher)) {
+          seenMatchedClassHandler |= handler.isClassHandler();
         }
       }
-      consumer.accept(proxyBuilder.build());
     }
+    return proxyBuilder.build();
   }
 
   private int registerGroups(
@@ -166,9 +169,6 @@
     }
   }
 
-  private static final String anyNonDigitLetterCharWithMarkers = "\\p{L}\\p{M}*+";
-  private static final String anyDigit = "\\p{N}";
-
   // TODO(b/145731185): Extend support for identifiers with strings inside back ticks.
   private static final String javaIdentifierSegment =
       "\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*";
@@ -376,4 +376,3 @@
     }
   }
 }
-
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 0d7fe26..fd3c069 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -3307,12 +3307,10 @@
             appInfo.getClassToFeatureSplitMap(),
             appInfo.getMainDexInfo(),
             deadProtoTypes,
-            appView.testing().enableExperimentalMissingClassesReporting
-                ? (mode.isInitialTreeShaking()
-                    ? missingClassesBuilder.reportMissingClasses(
-                        appView, lambdaSynthesizingContextOracle)
-                    : missingClassesBuilder.assertNoMissingClasses(appView))
-                : missingClassesBuilder.ignoreMissingClasses(),
+            mode.isInitialTreeShaking()
+                ? missingClassesBuilder.reportMissingClasses(
+                    appView, lambdaSynthesizingContextOracle)
+                : missingClassesBuilder.assertNoMissingClasses(appView),
             SetUtils.mapIdentityHashSet(liveTypes.getItems(), DexProgramClass::getType),
             Enqueuer.toDescriptorSet(targetedMethods.getItems()),
             Collections.unmodifiableSet(failedMethodResolutionTargets),
diff --git a/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java b/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java
index 459dea6..7760062 100644
--- a/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java
+++ b/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java
@@ -180,7 +180,7 @@
 
     @Override
     public void registerInstanceOf(DexType type) {
-      registerTypeReference(type);
+      consumer.accept(type);
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/shaking/MissingClasses.java b/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
index 3670701..de249d8 100644
--- a/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
+++ b/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
@@ -48,10 +48,6 @@
     return new Builder(missingClasses);
   }
 
-  public static Builder builderForInitialMissingClasses() {
-    return new Builder();
-  }
-
   public static MissingClasses empty() {
     return new MissingClasses(Sets.newIdentityHashSet());
   }
@@ -60,7 +56,7 @@
     return builder()
         // TODO(b/175542052): Synthetic types should not be reported as missing in the first place.
         .removeAlreadyMissingClasses(committedItems.getLegacySyntheticTypes())
-        .ignoreMissingClasses();
+        .build();
   }
 
   public boolean contains(DexType type) {
@@ -131,15 +127,6 @@
       return build();
     }
 
-    @Deprecated
-    public MissingClasses ignoreMissingClasses() {
-      return build();
-    }
-
-    public MissingClasses reportMissingClasses(AppView<?> appView) {
-      return reportMissingClasses(appView, clazz -> ImmutableSet.of(clazz.getType()));
-    }
-
     public MissingClasses reportMissingClasses(
         AppView<?> appView, SynthesizingContextOracle synthesizingContextOracle) {
       Map<DexType, Set<ProgramDerivedContext>> missingClassesToBeReported =
@@ -247,13 +234,6 @@
               return;
             }
 
-            // TODO(b/175543745): This is a legacy reported missing class; remove once no longer
-            //  supported.
-            if (contexts.isEmpty()) {
-              missingClassesToBeReported.put(missingClass, contexts);
-              return;
-            }
-
             // Remove all contexts that are matched by a -dontwarn rule (a missing class should not
             // be reported if it os only referenced from contexts that are matched by a -dontwarn).
             contexts.removeIf(
diff --git a/src/main/java/com/android/tools/r8/utils/ClassReferenceUtils.java b/src/main/java/com/android/tools/r8/utils/ClassReferenceUtils.java
index 171344e..fa2a18c 100644
--- a/src/main/java/com/android/tools/r8/utils/ClassReferenceUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ClassReferenceUtils.java
@@ -5,6 +5,8 @@
 package com.android.tools.r8.utils;
 
 import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.MethodReference;
 import java.util.Comparator;
 
 public class ClassReferenceUtils {
@@ -16,6 +18,18 @@
     return getClassReferenceComparator().compare(classReference, other);
   }
 
+  public static int compare(ClassReference classReference, FieldReference other) {
+    int comparisonResult =
+        getClassReferenceComparator().compare(classReference, other.getHolderClass());
+    return comparisonResult != 0 ? comparisonResult : -1;
+  }
+
+  public static int compare(ClassReference classReference, MethodReference other) {
+    int comparisonResult =
+        getClassReferenceComparator().compare(classReference, other.getHolderClass());
+    return comparisonResult != 0 ? comparisonResult : -1;
+  }
+
   public static Comparator<ClassReference> getClassReferenceComparator() {
     return COMPARATOR;
   }
diff --git a/src/main/java/com/android/tools/r8/utils/FieldReferenceUtils.java b/src/main/java/com/android/tools/r8/utils/FieldReferenceUtils.java
index ebf7821..d7b0439 100644
--- a/src/main/java/com/android/tools/r8/utils/FieldReferenceUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/FieldReferenceUtils.java
@@ -7,7 +7,9 @@
 import static com.android.tools.r8.utils.ClassReferenceUtils.getClassReferenceComparator;
 import static com.android.tools.r8.utils.TypeReferenceUtils.getTypeReferenceComparator;
 
+import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.references.Reference;
 import java.util.Comparator;
 
@@ -29,10 +31,20 @@
         return getTypeReferenceComparator().compare(field.getFieldType(), other.getFieldType());
       };
 
+  public static int compare(FieldReference fieldReference, ClassReference other) {
+    return ClassReferenceUtils.compare(other, fieldReference) * -1;
+  }
+
   public static int compare(FieldReference fieldReference, FieldReference other) {
     return getFieldReferenceComparator().compare(fieldReference, other);
   }
 
+  public static int compare(FieldReference fieldReference, MethodReference other) {
+    int comparisonResult =
+        ClassReferenceUtils.compare(fieldReference.getHolderClass(), other.getHolderClass());
+    return comparisonResult != 0 ? comparisonResult : -1;
+  }
+
   public static FieldReference fieldFromField(Class<?> clazz, String name) {
     try {
       return Reference.fieldFromField(clazz.getDeclaredField(name));
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 b60a0e3..4e787b6 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1223,10 +1223,6 @@
 
   public static class TestingOptions {
 
-    public static void enableExperimentalMissingClassesReporting(InternalOptions options) {
-      options.testing.enableExperimentalMissingClassesReporting = true;
-    }
-
     public static void allowExperimentClassFileVersion(InternalOptions options) {
       options.reportedExperimentClassFileVersion.set(true);
     }
@@ -1291,7 +1287,6 @@
     public boolean alwaysUsePessimisticRegisterAllocation = false;
     public boolean enableCheckCastAndInstanceOfRemoval = true;
     public boolean enableDeadSwitchCaseElimination = true;
-    public boolean enableExperimentalMissingClassesReporting = true;
     public boolean enableInvokeSuperToInvokeVirtualRewriting = true;
     public boolean enableSwitchToIfRewriting = true;
     public boolean enableEnumUnboxingDebugLogs = false;
diff --git a/src/main/java/com/android/tools/r8/utils/ListUtils.java b/src/main/java/com/android/tools/r8/utils/ListUtils.java
index 10cd22a..f4c8deb 100644
--- a/src/main/java/com/android/tools/r8/utils/ListUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ListUtils.java
@@ -145,4 +145,14 @@
     }
     return result;
   }
+
+  public static <T> void forEachWithIndex(List<T> items, ReferenceAndIntConsumer<T> consumer) {
+    for (int i = 0; i < items.size(); i++) {
+      consumer.accept(items.get(i), i);
+    }
+  }
+
+  public interface ReferenceAndIntConsumer<T> {
+    void accept(T item, int index);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java b/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java
index 4d36fb9..37deccc 100644
--- a/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java
@@ -9,6 +9,7 @@
 
 import com.android.tools.r8.references.ArrayReference;
 import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.FieldReference;
 import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.references.TypeReference;
@@ -52,6 +53,14 @@
         return method.getFormalTypes().size() - other.getFormalTypes().size();
       };
 
+  public static int compare(MethodReference methodReference, ClassReference other) {
+    return ClassReferenceUtils.compare(other, methodReference) * -1;
+  }
+
+  public static int compare(MethodReference methodReference, FieldReference other) {
+    return FieldReferenceUtils.compare(other, methodReference) * -1;
+  }
+
   public static int compare(MethodReference methodReference, MethodReference other) {
     return getMethodReferenceComparator().compare(methodReference, other);
   }
diff --git a/src/test/java/com/android/tools/r8/AndroidAppDumpsTest.java b/src/test/java/com/android/tools/r8/AndroidAppDumpsTest.java
index 8b7d21e..7a35696 100644
--- a/src/test/java/com/android/tools/r8/AndroidAppDumpsTest.java
+++ b/src/test/java/com/android/tools/r8/AndroidAppDumpsTest.java
@@ -29,15 +29,13 @@
 @RunWith(Parameterized.class)
 public class AndroidAppDumpsTest extends TestBase {
 
-  private final TestParameters parameters;
-
   @Parameterized.Parameters(name = "{0}")
   public static TestParametersCollection data() {
     return getTestParameters().withNoneRuntime().build();
   }
 
   public AndroidAppDumpsTest(TestParameters parameters) {
-    this.parameters = parameters;
+    parameters.assertNoneRuntime();
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/D8CommandTest.java b/src/test/java/com/android/tools/r8/D8CommandTest.java
index 3bcb3a7..cd1ee66 100644
--- a/src/test/java/com/android/tools/r8/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/D8CommandTest.java
@@ -50,7 +50,9 @@
     return getTestParameters().withNoneRuntime().build();
   }
 
-  public D8CommandTest(TestParameters parameters) {}
+  public D8CommandTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
 
   @Test(expected = CompilationFailedException.class)
   public void emptyBuilder() throws Throwable {
diff --git a/src/test/java/com/android/tools/r8/KotlinTestBase.java b/src/test/java/com/android/tools/r8/KotlinTestBase.java
index f018f74..37f4222 100644
--- a/src/test/java/com/android/tools/r8/KotlinTestBase.java
+++ b/src/test/java/com/android/tools/r8/KotlinTestBase.java
@@ -66,11 +66,11 @@
     return Paths.get(ToolHelper.TESTS_DIR, "java", folder, fileName + FileUtils.KT_EXTENSION);
   }
 
-  protected static Path getKotlinFileInResource(String folder, String fileName) {
+  public static Path getKotlinFileInResource(String folder, String fileName) {
     return Paths.get(ToolHelper.TESTS_DIR, RSRC, folder, fileName + FileUtils.KT_EXTENSION);
   }
 
-  protected static List<Path> getKotlinFilesInResource(String folder) {
+  public static List<Path> getKotlinFilesInResource(String folder) {
     try {
       return Files.walk(Paths.get(ToolHelper.TESTS_DIR, RSRC, folder))
           .filter(path -> path.toString().endsWith(".kt") || path.toString().endsWith(".java"))
diff --git a/src/test/java/com/android/tools/r8/L8CommandTest.java b/src/test/java/com/android/tools/r8/L8CommandTest.java
index 978c382..bb32740 100644
--- a/src/test/java/com/android/tools/r8/L8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/L8CommandTest.java
@@ -43,7 +43,9 @@
     return getTestParameters().withNoneRuntime().build();
   }
 
-  public L8CommandTest(TestParameters parameters) {}
+  public L8CommandTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
 
   @Test(expected = CompilationFailedException.class)
   public void emptyBuilder() throws Throwable {
diff --git a/src/test/java/com/android/tools/r8/MarkersTest.java b/src/test/java/com/android/tools/r8/MarkersTest.java
index bad77b5..1a7c92a 100644
--- a/src/test/java/com/android/tools/r8/MarkersTest.java
+++ b/src/test/java/com/android/tools/r8/MarkersTest.java
@@ -50,7 +50,7 @@
 
   public MarkersTest(
       TestParameters parameters, CompilationMode compilationMode, boolean shrinkDesugaredLibrary) {
-    assert parameters.isNoneRuntime();
+    parameters.assertNoneRuntime();
     this.compilationMode = compilationMode;
     this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
   }
diff --git a/src/test/java/com/android/tools/r8/R8CfVersionTest.java b/src/test/java/com/android/tools/r8/R8CfVersionTest.java
index 4f051ad..8e9b11c 100644
--- a/src/test/java/com/android/tools/r8/R8CfVersionTest.java
+++ b/src/test/java/com/android/tools/r8/R8CfVersionTest.java
@@ -24,7 +24,9 @@
     return getTestParameters().withNoneRuntime().build();
   }
 
-  public R8CfVersionTest(TestParameters parameters) {}
+  public R8CfVersionTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
 
   @Test
   public void testCfVersionR8() throws IOException {
diff --git a/src/test/java/com/android/tools/r8/R8CommandTest.java b/src/test/java/com/android/tools/r8/R8CommandTest.java
index 2f94aeb..03f13ec 100644
--- a/src/test/java/com/android/tools/r8/R8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/R8CommandTest.java
@@ -52,7 +52,9 @@
     return getTestParameters().withNoneRuntime().build();
   }
 
-  public R8CommandTest(TestParameters parameters) {}
+  public R8CommandTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
 
   @Test(expected = CompilationFailedException.class)
   public void emptyBuilder() throws Throwable {
diff --git a/src/test/java/com/android/tools/r8/SwitchDebugLocalsConflictTest.java b/src/test/java/com/android/tools/r8/SwitchDebugLocalsConflictTest.java
index d324f38..355920f 100644
--- a/src/test/java/com/android/tools/r8/SwitchDebugLocalsConflictTest.java
+++ b/src/test/java/com/android/tools/r8/SwitchDebugLocalsConflictTest.java
@@ -18,15 +18,13 @@
 @RunWith(Parameterized.class)
 public class SwitchDebugLocalsConflictTest extends TestBase {
 
-  private final TestParameters parameters;
-
   @Parameterized.Parameters(name = "{0}")
   public static TestParametersCollection data() {
     return getTestParameters().withNoneRuntime().build();
   }
 
   public SwitchDebugLocalsConflictTest(TestParameters parameters) {
-    this.parameters = parameters;
+    parameters.assertNoneRuntime();
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index cf27791..e638abf 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -404,7 +404,7 @@
     return KotlinTestParameters.builder();
   }
 
-  protected static <S, T, E extends Throwable> Function<S, T> memoizeFunction(
+  public static <S, T, E extends Throwable> Function<S, T> memoizeFunction(
       ThrowingFunction<S, T, E> fn) {
     return CacheBuilder.newBuilder()
         .build(
@@ -418,7 +418,7 @@
                 }));
   }
 
-  protected static <S, T, U, E extends Throwable> BiFunction<S, T, U> memoizeBiFunction(
+  public static <S, T, U, E extends Throwable> BiFunction<S, T, U> memoizeBiFunction(
       ThrowingBiFunction<S, T, U, E> fn) {
     class Pair {
       final S first;
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index ea3f393..26477d3 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -50,7 +50,6 @@
       options -> {
         options.testing.allowUnusedDontWarnRules = false;
         options.testing.allowUnnecessaryDontWarnWildcards = false;
-        options.testing.enableExperimentalMissingClassesReporting = true;
         options.testing.reportUnusedProguardConfigurationRules = true;
         options.horizontalClassMergerOptions().enable();
       };
diff --git a/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java b/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
index e2ea100..04eb3c8 100644
--- a/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
+++ b/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
@@ -30,6 +30,7 @@
   }
 
   @SafeVarargs
+  @SuppressWarnings("unchecked")
   public final TestDiagnosticMessages inspectWarnings(
       ThrowableConsumer<DiagnosticSubject>... consumers) {
     return inspectWarnings(Arrays.asList(consumers));
@@ -51,6 +52,7 @@
   public abstract List<Diagnostic> getErrors();
 
   @SafeVarargs
+  @SuppressWarnings("unchecked")
   public final TestDiagnosticMessages inspectErrors(
       ThrowableConsumer<DiagnosticSubject>... consumers) {
     return inspectErrors(Arrays.asList(consumers));
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index 78ac691..1ad5db8 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -136,7 +136,11 @@
   }
 
   public T addIgnoreWarnings() {
-    return addKeepRules("-ignorewarnings");
+    return addIgnoreWarnings(true);
+  }
+
+  public T addIgnoreWarnings(boolean condition) {
+    return condition ? addKeepRules("-ignorewarnings") : self();
   }
 
   public T addKeepKotlinMetadata() {
diff --git a/src/test/java/com/android/tools/r8/VersionTests.java b/src/test/java/com/android/tools/r8/VersionTests.java
index 9db9512..dad43b9 100644
--- a/src/test/java/com/android/tools/r8/VersionTests.java
+++ b/src/test/java/com/android/tools/r8/VersionTests.java
@@ -23,10 +23,8 @@
     return getTestParameters().withNoneRuntime().build();
   }
 
-  private final TestParameters parameters;
-
   public VersionTests(TestParameters parameters) {
-    this.parameters = parameters;
+    parameters.assertNoneRuntime();
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapTest.java b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapTest.java
index 5d890ab..7dd698b 100644
--- a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapTest.java
+++ b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapTest.java
@@ -70,7 +70,7 @@
 
   public BootstrapTest(TestParameters parameters) {
     // TODO: use parameters to fork the right Java.
-    assert parameters.isNoneRuntime();
+    parameters.assertNoneRuntime();
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java b/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java
index 9527564..68baadf 100644
--- a/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java
@@ -38,7 +38,7 @@
   }
 
   public KeptTargetsIncompleteDiamondTest(TestParameters parameters) {
-    assert parameters.isNoneRuntime();
+    parameters.assertNoneRuntime();
   }
 
   private AppView<AppInfoWithLiveness> computeAppViewWithLiveness(
diff --git a/src/test/java/com/android/tools/r8/compatproguard/KeepRuleConnectivesTestRunner.java b/src/test/java/com/android/tools/r8/compatproguard/KeepRuleConnectivesTestRunner.java
index 818c743..0f82a73 100644
--- a/src/test/java/com/android/tools/r8/compatproguard/KeepRuleConnectivesTestRunner.java
+++ b/src/test/java/com/android/tools/r8/compatproguard/KeepRuleConnectivesTestRunner.java
@@ -30,10 +30,8 @@
     return getTestParameters().withNoneRuntime().build();
   }
 
-  private final TestParameters parameters;
-
   public KeepRuleConnectivesTestRunner(TestParameters parameters) {
-    this.parameters = parameters;
+    parameters.assertNoneRuntime();
   }
 
   private <
diff --git a/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java b/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
index 069a504..4552be6 100644
--- a/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
+++ b/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.debug;
 
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.utils.DescriptorUtils;
@@ -32,9 +34,10 @@
 import java.util.jar.JarInputStream;
 import java.util.stream.Collectors;
 import org.apache.harmony.jpda.tests.framework.jdwp.Value;
+import org.junit.AfterClass;
 import org.junit.Assert;
-import org.junit.BeforeClass;
 import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
@@ -44,17 +47,22 @@
 
   private static final String MAIN_METHOD_NAME = "main";
 
+  private static final TemporaryFolder testTemp = ToolHelper.getTemporaryFolderForTest();
+
   // A list of self-contained jars to process (which do not depend on other jar files).
-  private static final List<Pair<Path, Predicate<Version>>> LIST_OF_JARS = new ConfigListBuilder()
-      .add(DebugTestBase.DEBUGGEE_JAR, ContinuousSteppingTest::allVersions)
-      .add(DebugTestBase.DEBUGGEE_JAVA8_JAR, ContinuousSteppingTest::allVersions)
-      .add(KotlinD8Config.DEBUGGEE_KOTLIN_JAR, ContinuousSteppingTest::allVersions)
-      .addAll(findAllJarsIn(Paths.get(ToolHelper.EXAMPLES_ANDROID_N_BUILD_DIR)),
-          ContinuousSteppingTest::fromAndroidN)
-      // TODO(b/79911828) Investigate timeout issues for Android O examples.
-      //  .addAll(findAllJarsIn(Paths.get(ToolHelper.EXAMPLES_ANDROID_O_BUILD_DIR)),
-      //      ContinuousSteppingTest::fromAndroidO)
-      .build();
+  private static List<Pair<Path, Predicate<Version>>> listOfJars() {
+    return new ConfigListBuilder()
+        .add(DebugTestBase.DEBUGGEE_JAR, ContinuousSteppingTest::allVersions)
+        .add(DebugTestBase.DEBUGGEE_JAVA8_JAR, ContinuousSteppingTest::allVersions)
+        .addAllKotlinDebugJars(testTemp, ContinuousSteppingTest::allVersions)
+        .addAll(
+            findAllJarsIn(Paths.get(ToolHelper.EXAMPLES_ANDROID_N_BUILD_DIR)),
+            ContinuousSteppingTest::fromAndroidN)
+        // TODO(b/79911828) Investigate timeout issues for Android O examples.
+        //  .addAll(findAllJarsIn(Paths.get(ToolHelper.EXAMPLES_ANDROID_O_BUILD_DIR)),
+        //      ContinuousSteppingTest::fromAndroidO)
+        .build();
+  }
 
   private static final Map<Path, DebugTestConfig> compiledJarConfig = new HashMap<>();
 
@@ -77,6 +85,15 @@
       return this;
     }
 
+    public ConfigListBuilder addAllKotlinDebugJars(
+        TemporaryFolder temp, Predicate<Version> predicate) {
+      for (KotlinTestParameters kotlinParameter :
+          TestBase.getKotlinTestParameters().withAllCompilersAndTargetVersions().build()) {
+        add(KotlinD8Config.compileKotlinMemoized.apply(temp, kotlinParameter), predicate);
+      }
+      return this;
+    }
+
     public List<Pair<Path, Predicate<Version>>> build() {
       return builder.build();
     }
@@ -100,27 +117,25 @@
     }
   }
 
-  @BeforeClass
-  public static void setup() {
-    LIST_OF_JARS.forEach(pair -> {
-      if (pair.getSecond().test(ToolHelper.getDexVm().getVersion())) {
-        Path jarPath = pair.getFirst();
-        DebugTestConfig config = new D8DebugTestConfig().compileAndAdd(temp, jarPath);
-        compiledJarConfig.put(jarPath, config);
-      }
-    });
+  @AfterClass
+  public static void tearDown() {
+    testTemp.delete();
   }
 
   @Parameters(name = "{0} from {1}")
   public static Collection<Object[]> getData() throws IOException {
+    testTemp.create();
     List<Object[]> testCases = new ArrayList<>();
-    for (Pair<Path, Predicate<Version>> pair : LIST_OF_JARS) {
+    for (Pair<Path, Predicate<Version>> pair : listOfJars()) {
       if (pair.getSecond().test(ToolHelper.getDexVm().getVersion())) {
         Path jarPath = pair.getFirst();
         List<String> mainClasses = getAllMainClassesFromJar(jarPath);
         for (String className : mainClasses) {
           testCases.add(new Object[]{className, jarPath});
         }
+
+        DebugTestConfig config = new D8DebugTestConfig().compileAndAdd(testTemp, jarPath);
+        compiledJarConfig.put(jarPath, config);
       }
     }
     return testCases;
diff --git a/src/test/java/com/android/tools/r8/debug/D8DebugTestConfig.java b/src/test/java/com/android/tools/r8/debug/D8DebugTestConfig.java
index 1f13006..ef2205a 100644
--- a/src/test/java/com/android/tools/r8/debug/D8DebugTestConfig.java
+++ b/src/test/java/com/android/tools/r8/debug/D8DebugTestConfig.java
@@ -20,16 +20,22 @@
 /** Test configuration with utilities for compiling with D8 and adding results to the classpath. */
 public class D8DebugTestConfig extends DexDebugTestConfig {
 
+  // Use the option with api-level below.
+  @Deprecated()
   public static AndroidApp d8Compile(List<Path> paths, Consumer<InternalOptions> optionsConsumer) {
+    return d8Compile(paths, ToolHelper.getMinApiLevelForDexVm(), optionsConsumer);
+  }
+
+  public static AndroidApp d8Compile(
+      List<Path> paths, AndroidApiLevel apiLevel, Consumer<InternalOptions> optionsConsumer) {
     try {
-      AndroidApiLevel minSdk = ToolHelper.getMinApiLevelForDexVm();
       D8Command.Builder builder = D8Command.builder();
       return ToolHelper.runD8(
           builder
               .addProgramFiles(paths)
-              .setMinApiLevel(minSdk.getLevel())
+              .setMinApiLevel(apiLevel.getLevel())
               .setMode(CompilationMode.DEBUG)
-              .addLibraryFiles(ToolHelper.getAndroidJar(minSdk)),
+              .addLibraryFiles(ToolHelper.getAndroidJar(apiLevel)),
           optionsConsumer);
     } catch (Exception e) {
       throw new RuntimeException(e);
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinD8Config.java b/src/test/java/com/android/tools/r8/debug/KotlinD8Config.java
index d828a5e..a7e31a4 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinD8Config.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinD8Config.java
@@ -4,39 +4,58 @@
 
 package com.android.tools.r8.debug;
 
+import static com.android.tools.r8.TestBase.kotlinc;
+import static com.android.tools.r8.TestBase.memoizeBiFunction;
+
+import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.OutputMode;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestRuntime;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.io.IOException;
 import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.util.Collections;
+import java.util.function.BiFunction;
 import org.junit.rules.TemporaryFolder;
 
-/**
- * Shared test configuration for D8 compiled resources from the "debugTestResourcesKotlin" target.
- */
+/** Shared test configuration for D8 compiled resources from the "kotlinR8TestResources/debug". */
 class KotlinD8Config extends D8DebugTestConfig {
 
-  public static final Path DEBUGGEE_KOTLIN_JAR =
-      Paths.get(ToolHelper.BUILD_DIR, "test", "debug_test_resources_kotlin.jar");
+  public static BiFunction<TemporaryFolder, KotlinTestParameters, Path> compileKotlinMemoized =
+      memoizeBiFunction(KotlinD8Config::compileWithKotlinC);
 
-  private static AndroidApp compiledResources = null;
-
-  private static synchronized AndroidApp getCompiledResources() throws Throwable {
-    if (compiledResources == null) {
-      compiledResources =
-          D8DebugTestConfig.d8Compile(
-              Collections.singletonList(DEBUGGEE_KOTLIN_JAR), null);
-    }
-    return compiledResources;
+  private static Path compileWithKotlinC(TemporaryFolder temp, KotlinTestParameters parameters)
+      throws IOException {
+    return kotlinc(
+            TestRuntime.getCheckedInJdk9(),
+            temp,
+            parameters.getCompiler(),
+            parameters.getTargetVersion())
+        .addSourceFiles(KotlinTestBase.getKotlinFilesInResource("debug"))
+        .includeRuntime()
+        .compile();
   }
 
-  public KotlinD8Config(TemporaryFolder temp) {
-    super();
+  private static BiFunction<KotlinTestParameters, AndroidApiLevel, Path> compiledResourcesMemoized =
+      memoizeBiFunction(KotlinD8Config::getCompiledResources);
+
+  private static Path getCompiledResources(
+      KotlinTestParameters kotlinTestParameters, AndroidApiLevel apiLevel) throws IOException {
+    Path outputPath =
+        TestBase.getStaticTemp().newFolder().toPath().resolve("d8_debug_test_resources_kotlin.jar");
+    Path kotlinJar = compileKotlinMemoized.apply(TestBase.getStaticTemp(), kotlinTestParameters);
+    D8DebugTestConfig.d8Compile(Collections.singletonList(kotlinJar), apiLevel, null)
+        .write(outputPath, OutputMode.DexIndexed);
+    return outputPath;
+  }
+
+  public static KotlinD8Config build(
+      KotlinTestParameters kotlinTestParameters, AndroidApiLevel apiLevel) {
     try {
-      Path out = temp.newFolder().toPath().resolve("d8_debug_test_resources_kotlin.jar");
-      getCompiledResources().write(out, OutputMode.DexIndexed);
-      addPaths(out);
+      KotlinD8Config kotlinD8Config = new KotlinD8Config();
+      kotlinD8Config.addPaths(compiledResourcesMemoized.apply(kotlinTestParameters, apiLevel));
+      return kotlinD8Config;
     } catch (Throwable e) {
       throw new RuntimeException(e);
     }
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java b/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java
index 894e7d5..00333e0 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java
@@ -6,28 +6,41 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.nio.file.Paths;
+import java.util.List;
 import org.apache.harmony.jpda.tests.framework.jdwp.Value;
-import org.junit.BeforeClass;
 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 KotlinInlineTest extends KotlinDebugTestBase {
 
   public static final String DEBUGGEE_CLASS = "KotlinInline";
   public static final String SOURCE_FILE = "KotlinInline.kt";
 
-  private static KotlinD8Config d8Config;
+  private final TestParameters parameters;
+  private final KotlinTestParameters kotlinParameters;
 
-  @BeforeClass
-  public static void setup() {
-    d8Config = new KotlinD8Config(temp);
+  @Parameters(name = "{0}, {1}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withDexRuntimes().withAllApiLevels().build(),
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
+  }
+
+  public KotlinInlineTest(TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    this.parameters = parameters;
+    this.kotlinParameters = kotlinParameters;
   }
 
   protected KotlinD8Config getD8Config() {
-    return d8Config;
+    return KotlinD8Config.build(kotlinParameters, parameters.getApiLevel());
   }
 
   @Test
@@ -242,7 +255,8 @@
   public void testNestedInlining() throws Throwable {
     // Count the number of lines in the source file. This is needed to check that inlined code
     // refers to non-existing line numbers.
-    Path sourceFilePath = Paths.get(ToolHelper.TESTS_DIR, "debugTestResourcesKotlin", SOURCE_FILE);
+    Path sourceFilePath =
+        AbstractR8KotlinTestBase.getKotlinFileInResource("debug", SOURCE_FILE.replace(".kt", ""));
     assert sourceFilePath.toFile().exists();
     final int maxLineNumber = Files.readAllLines(sourceFilePath).size();
     final String inliningMethodName = "testNestedInlining";
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinTest.java b/src/test/java/com/android/tools/r8/debug/KotlinTest.java
index f1482fb..07e9e05 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinTest.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinTest.java
@@ -5,21 +5,35 @@
 
 import static org.junit.Assert.assertEquals;
 
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestParameters;
+import java.util.List;
 import org.apache.harmony.jpda.tests.framework.jdwp.Value;
-import org.junit.BeforeClass;
 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 KotlinTest extends KotlinDebugTestBase {
 
-  private static KotlinD8Config d8Config;
+  private final TestParameters parameters;
+  private final KotlinTestParameters kotlinParameters;
 
-  @BeforeClass
-  public static void setup() {
-    d8Config = new KotlinD8Config(temp);
+  @Parameters(name = "{0}, {1}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withDexRuntimes().withAllApiLevels().build(),
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
+  }
+
+  public KotlinTest(TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    this.parameters = parameters;
+    this.kotlinParameters = kotlinParameters;
   }
 
   protected KotlinD8Config getD8Config() {
-    return d8Config;
+    return KotlinD8Config.build(kotlinParameters, parameters.getApiLevel());
   }
 
   // TODO(shertz) simplify test
diff --git a/src/test/java/com/android/tools/r8/debug/Regress148661132Test.java b/src/test/java/com/android/tools/r8/debug/Regress148661132Test.java
index ae64de7..0022063 100644
--- a/src/test/java/com/android/tools/r8/debug/Regress148661132Test.java
+++ b/src/test/java/com/android/tools/r8/debug/Regress148661132Test.java
@@ -18,15 +18,13 @@
 @RunWith(Parameterized.class)
 public class Regress148661132Test extends TestBase {
 
-  private final TestParameters parameters;
-
   @Parameterized.Parameters(name = "{0}")
   public static TestParametersCollection data() {
     return getTestParameters().withNoneRuntime().build();
   }
 
   public Regress148661132Test(TestParameters parameters) {
-    this.parameters = parameters;
+    parameters.assertNoneRuntime();
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/InconsistentPrefixTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/InconsistentPrefixTest.java
index 70ae6d6..84684da 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/InconsistentPrefixTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/InconsistentPrefixTest.java
@@ -28,7 +28,9 @@
     return getTestParameters().withNoneRuntime().build();
   }
 
-  public InconsistentPrefixTest(TestParameters parameters) {}
+  public InconsistentPrefixTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
 
   @Test(expected = CompilationFailedException.class)
   public void testNoInconsistentPrefixes() throws Exception {
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11D8CompilationTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11D8CompilationTest.java
index 3b9b321..3d063c5 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11D8CompilationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11D8CompilationTest.java
@@ -19,7 +19,9 @@
 @RunWith(Parameterized.class)
 public class Java11D8CompilationTest extends TestBase {
 
-  public Java11D8CompilationTest(TestParameters parameters) {}
+  public Java11D8CompilationTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
 
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestCompilationExceptionTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestCompilationExceptionTest.java
index c6df029..fd874a1 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestCompilationExceptionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestCompilationExceptionTest.java
@@ -96,11 +96,7 @@
           .setMinApi(parameters.getApiLevel())
           .addProgramFiles(matchingClasses)
           .applyIf(parameters.isCfRuntime(), Jdk9TestUtils.addJdk9LibraryFiles(temp))
-          .addOptionsModification(
-              options -> {
-                options.ignoreMissingClasses = ignoreMissingClasses;
-                options.testing.enableExperimentalMissingClassesReporting = true;
-              })
+          .addIgnoreWarnings(ignoreMissingClasses)
           .allowDiagnosticWarningMessages(allowDiagnosticWarningMessages);
     }
   }
diff --git a/src/test/java/com/android/tools/r8/diagnostics/ApiLevelDiagnosticTest.java b/src/test/java/com/android/tools/r8/diagnostics/ApiLevelDiagnosticTest.java
index 40fab17..66f976b 100644
--- a/src/test/java/com/android/tools/r8/diagnostics/ApiLevelDiagnosticTest.java
+++ b/src/test/java/com/android/tools/r8/diagnostics/ApiLevelDiagnosticTest.java
@@ -35,7 +35,9 @@
     return getTestParameters().withNoneRuntime().build();
   }
 
-  public ApiLevelDiagnosticTest(TestParameters parameters) {}
+  public ApiLevelDiagnosticTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
 
   @Test(expected = CompilationFailedException.class)
   public void testInvokeLambdaMetafactory() throws Exception {
diff --git a/src/test/java/com/android/tools/r8/diagnostics/ErrorDuringIrConversionTest.java b/src/test/java/com/android/tools/r8/diagnostics/ErrorDuringIrConversionTest.java
index 89088ac..436dc43 100644
--- a/src/test/java/com/android/tools/r8/diagnostics/ErrorDuringIrConversionTest.java
+++ b/src/test/java/com/android/tools/r8/diagnostics/ErrorDuringIrConversionTest.java
@@ -49,7 +49,9 @@
     return getTestParameters().withNoneRuntime().build();
   }
 
-  public ErrorDuringIrConversionTest(TestParameters parameters) {}
+  public ErrorDuringIrConversionTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
 
   private ThrowableConsumer<D8TestBuilder> addTestClassWithOrigin() {
     return b ->
diff --git a/src/test/java/com/android/tools/r8/graph/GenericSignatureIdentityTest.java b/src/test/java/com/android/tools/r8/graph/GenericSignatureIdentityTest.java
index b94f484..f4adbfb 100644
--- a/src/test/java/com/android/tools/r8/graph/GenericSignatureIdentityTest.java
+++ b/src/test/java/com/android/tools/r8/graph/GenericSignatureIdentityTest.java
@@ -31,15 +31,13 @@
 @RunWith(Parameterized.class)
 public class GenericSignatureIdentityTest extends TestBase {
 
-  private final TestParameters parameters;
-
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
     return getTestParameters().withNoneRuntime().build();
   }
 
   public GenericSignatureIdentityTest(TestParameters parameters) {
-    this.parameters = parameters;
+    parameters.assertNoneRuntime();
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/graph/MissingClassThrowingTest.java b/src/test/java/com/android/tools/r8/graph/MissingClassThrowingTest.java
index 5526415..52c6e15 100644
--- a/src/test/java/com/android/tools/r8/graph/MissingClassThrowingTest.java
+++ b/src/test/java/com/android/tools/r8/graph/MissingClassThrowingTest.java
@@ -11,7 +11,6 @@
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.TestRuntime.CfVm;
-import com.android.tools.r8.utils.InternalOptions.TestingOptions;
 import com.android.tools.r8.utils.codeinspector.AssertUtils;
 import java.io.IOException;
 import org.junit.Test;
@@ -69,22 +68,20 @@
                 .addProgramClasses(Program.class)
                 .addKeepAllClassesRule()
                 .addKeepAllAttributes()
-                .addOptionsModification(TestingOptions::enableExperimentalMissingClassesReporting)
                 .noMinification()
                 .noTreeShaking()
                 .enableInliningAnnotations()
                 .enableNoHorizontalClassMergingAnnotations()
                 .debug()
                 .compileWithExpectedDiagnostics(
-                    diagnostics -> {
-                      diagnostics
-                          .assertOnlyErrors()
-                          .inspectErrors(
-                              diagnostic ->
-                                  diagnostic
-                                      .assertIsMissingDefinitionsDiagnostic()
-                                      .assertIsMissingClass(MissingException.class)
-                                      .assertNumberOfMissingClasses(1));
-                    }));
+                    diagnostics ->
+                        diagnostics
+                            .assertOnlyErrors()
+                            .inspectErrors(
+                                diagnostic ->
+                                    diagnostic
+                                        .assertIsMissingDefinitionsDiagnostic()
+                                        .assertIsMissingClass(MissingException.class)
+                                        .assertNumberOfMissingClasses(1))));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/ClassSignatureTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/ClassSignatureTest.java
index e02f313..239d924 100644
--- a/src/test/java/com/android/tools/r8/graph/genericsignature/ClassSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/ClassSignatureTest.java
@@ -30,15 +30,13 @@
 @RunWith(Parameterized.class)
 public class ClassSignatureTest extends TestBase {
 
-  private final TestParameters parameters;
-
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
     return getTestParameters().withNoneRuntime().build();
   }
 
   public ClassSignatureTest(TestParameters parameters) {
-    this.parameters = parameters;
+    parameters.assertNoneRuntime();
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/FieldSignatureTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/FieldSignatureTest.java
index 81d8e11..c17ea6b 100644
--- a/src/test/java/com/android/tools/r8/graph/genericsignature/FieldSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/FieldSignatureTest.java
@@ -39,7 +39,9 @@
     return getTestParameters().withNoneRuntime().build();
   }
 
-  public FieldSignatureTest(TestParameters parameters) {}
+  public FieldSignatureTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
 
   @Test
   public void testClass() {
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureCorrectnessHelperTests.java b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureCorrectnessHelperTests.java
index db76bf9..65b434b 100644
--- a/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureCorrectnessHelperTests.java
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureCorrectnessHelperTests.java
@@ -28,15 +28,13 @@
 @RunWith(Parameterized.class)
 public class GenericSignatureCorrectnessHelperTests extends TestBase {
 
-  private final TestParameters parameters;
-
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
     return getTestParameters().withNoneRuntime().build();
   }
 
   public GenericSignatureCorrectnessHelperTests(TestParameters parameters) {
-    this.parameters = parameters;
+    parameters.assertNoneRuntime();
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/MethodSignatureTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/MethodSignatureTest.java
index c751918..cbfc822 100644
--- a/src/test/java/com/android/tools/r8/graph/genericsignature/MethodSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/MethodSignatureTest.java
@@ -30,15 +30,13 @@
 @RunWith(Parameterized.class)
 public class MethodSignatureTest extends TestBase {
 
-  private final TestParameters parameters;
-
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
     return getTestParameters().withNoneRuntime().build();
   }
 
   public MethodSignatureTest(TestParameters parameters) {
-    this.parameters = parameters;
+    parameters.assertNoneRuntime();
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1419TreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1419TreeShakeJarVerificationTest.java
index bbddb47..15d8928 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeV1419TreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1419TreeShakeJarVerificationTest.java
@@ -27,8 +27,6 @@
 
   private static final int MAX_SIZE = 27500000;
 
-  private final TestParameters parameters;
-
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
     return getTestParameters().withNoneRuntime().build();
@@ -36,7 +34,7 @@
 
   public YouTubeV1419TreeShakeJarVerificationTest(TestParameters parameters) {
     super(14, 19);
-    this.parameters = parameters;
+    parameters.assertNoneRuntime();
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1444TreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1444TreeShakeJarVerificationTest.java
index 0a304b1..f2afcb4 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeV1444TreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1444TreeShakeJarVerificationTest.java
@@ -28,8 +28,6 @@
   private static final boolean DUMP = false;
   private static final int MAX_SIZE = 27500000;
 
-  private final TestParameters parameters;
-
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
     return getTestParameters().withNoneRuntime().build();
@@ -37,7 +35,7 @@
 
   public YouTubeV1444TreeShakeJarVerificationTest(TestParameters parameters) {
     super(14, 44);
-    this.parameters = parameters;
+    parameters.assertNoneRuntime();
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1508TreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1508TreeShakeJarVerificationTest.java
index 79e7eb1..406ff62 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeV1508TreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1508TreeShakeJarVerificationTest.java
@@ -28,8 +28,6 @@
   private static final boolean DUMP = false;
   private static final int MAX_SIZE = 27500000;
 
-  private final TestParameters parameters;
-
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
     return getTestParameters().withNoneRuntime().build();
@@ -37,7 +35,7 @@
 
   public YouTubeV1508TreeShakeJarVerificationTest(TestParameters parameters) {
     super(15, 8);
-    this.parameters = parameters;
+    parameters.assertNoneRuntime();
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1533TreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1533TreeShakeJarVerificationTest.java
index 09293ac..88c7692 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeV1533TreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1533TreeShakeJarVerificationTest.java
@@ -28,8 +28,6 @@
   private static final boolean DUMP = false;
   private static final int MAX_SIZE = 27500000;
 
-  private final TestParameters parameters;
-
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
     return getTestParameters().withNoneRuntime().build();
@@ -37,7 +35,7 @@
 
   public YouTubeV1533TreeShakeJarVerificationTest(TestParameters parameters) {
     super(15, 33);
-    this.parameters = parameters;
+    parameters.assertNoneRuntime();
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/internal/retrace/RetraceTests.java b/src/test/java/com/android/tools/r8/internal/retrace/RetraceTests.java
index c8b98d7..949b787 100644
--- a/src/test/java/com/android/tools/r8/internal/retrace/RetraceTests.java
+++ b/src/test/java/com/android/tools/r8/internal/retrace/RetraceTests.java
@@ -4,7 +4,7 @@
 
 package com.android.tools.r8.internal.retrace;
 
-import static com.android.tools.r8.retrace.Retrace.DEFAULT_REGULAR_EXPRESSION;
+import static com.android.tools.r8.retrace.internal.StackTraceRegularExpressionParser.DEFAULT_REGULAR_EXPRESSION;
 
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestDiagnosticMessagesImpl;
@@ -40,7 +40,9 @@
   private static String FINSKY_REGEX = "(?:.*Finsky\\s+:\\s+\\[\\d+\\]\\s+%c\\.%m\\(%l\\):.*)";
   private static String SMILEY_EMOJI = "\uD83D\uDE00";
 
-  public RetraceTests(TestParameters parameters) {}
+  public RetraceTests(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
 
   @Test
   public void testCronetStackTrace() {
diff --git a/src/test/java/com/android/tools/r8/ir/BasicBlockIteratorTest.java b/src/test/java/com/android/tools/r8/ir/BasicBlockIteratorTest.java
index 96d5012..20caa5b 100644
--- a/src/test/java/com/android/tools/r8/ir/BasicBlockIteratorTest.java
+++ b/src/test/java/com/android/tools/r8/ir/BasicBlockIteratorTest.java
@@ -7,8 +7,6 @@
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.dex.ApplicationReader;
-import com.android.tools.r8.graph.AppInfo;
-import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.IRCode;
@@ -71,7 +69,6 @@
     InternalOptions options = new InternalOptions();
     DexApplication dexApplication =
         new ApplicationReader(application, options, Timing.empty()).read();
-    AppView<?> appView = AppView.createForD8(AppInfo.createInitialAppInfo(dexApplication));
 
     // Build the code, and split the code into three blocks.
     MethodSubject methodSubject = getMethodSubject(application, signature);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/ConstraintWithTargetTest.java b/src/test/java/com/android/tools/r8/ir/optimize/ConstraintWithTargetTest.java
index 81f2ba5..cba81b1 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/ConstraintWithTargetTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/ConstraintWithTargetTest.java
@@ -33,10 +33,8 @@
     return getTestParameters().withNoneRuntime().build();
   }
 
-  private final TestParameters parameters;
-
   public ConstraintWithTargetTest(TestParameters parameters) {
-    this.parameters = parameters;
+    parameters.assertNoneRuntime();
   }
 
   @BeforeClass
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParserTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParserTest.java
index d7ecc12..336e9a8 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParserTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinSourceDebugExtensionParserTest.java
@@ -27,7 +27,9 @@
     return getTestParameters().withNoneRuntime().build();
   }
 
-  public KotlinSourceDebugExtensionParserTest(TestParameters parameters) {}
+  public KotlinSourceDebugExtensionParserTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
 
   @Test
   public void testParsingEmpty() {
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexCheckCastInstanceOfDependencyTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexCheckCastInstanceOfDependencyTest.java
new file mode 100644
index 0000000..59cfae1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexCheckCastInstanceOfDependencyTest.java
@@ -0,0 +1,104 @@
+// Copyright (c) 2021, 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.maindexlist;
+
+import static com.android.tools.r8.maindexlist.MainDexCheckCastInstanceOfDependencyTest.Provider.getUnknownObject;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.google.common.collect.ImmutableSet;
+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 MainDexCheckCastInstanceOfDependencyTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters()
+        .withDexRuntimes()
+        .withApiLevelsEndingAtExcluding(apiLevelWithNativeMultiDexSupport())
+        .build();
+  }
+
+  public MainDexCheckCastInstanceOfDependencyTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testDependencyTracing() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepAllClassesRule()
+        .addMainDexRules("-keep class " + Main.class.getTypeName() + " { void foo(); }")
+        .setMinApi(parameters.getApiLevel())
+        .collectMainDexClasses()
+        .compile()
+        .inspectMainDexClasses(
+            mainDexClasses -> {
+              assertEquals(
+                  ImmutableSet.of(
+                      DependencyInstanceOf.class.getTypeName(),
+                      DependencyCheckCast.class.getTypeName(),
+                      DependencyConstClass.class.getTypeName(),
+                      Provider.class.getTypeName(),
+                      Main.class.getTypeName()),
+                  mainDexClasses);
+            })
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines(
+            "Hello InstanceOf",
+            "Hello CheckCast",
+            "class com.android.tools.r8.maindexlist.MainDexCheckCastInstanceOfDependencyTest"
+                + "$DependencyConstClass");
+  }
+
+  public static class DependencyInstanceOf {}
+
+  public static class DependencyCheckCast {}
+
+  public static class DependencyConstClass {}
+
+  public static class Provider {
+
+    public static Object getUnknownObject(String[] args) {
+      return args.length > 0 ? new DependencyInstanceOf() : new DependencyCheckCast();
+    }
+  }
+
+  public static class Main {
+
+    public static void testInstanceOf(String[] args) {
+      if (!(getUnknownObject(args) instanceof DependencyInstanceOf)) {
+        System.out.println("Hello InstanceOf");
+      }
+    }
+
+    public static void testCheckCast(String[] args) {
+      DependencyCheckCast dependency = (DependencyCheckCast) getUnknownObject(args);
+      System.out.println("Hello CheckCast");
+    }
+
+    public static void testConstClass() {
+      System.out.println(DependencyConstClass.class);
+    }
+
+    public static void main(String[] args) {
+      testInstanceOf(args);
+      testCheckCast(args);
+      testConstClass();
+    }
+
+    public static void foo() {
+      System.out.println("Hello World");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index da157c2..f37cc64 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -127,6 +127,7 @@
 
   public MainDexListTests(TestParameters parameters) {
     // We ignore the paramters, but only run once instead of running on every vm
+    parameters.assertNoneRuntime();
   }
 
   interface Runner {
diff --git a/src/test/java/com/android/tools/r8/missingclasses/MissingClassesTestBase.java b/src/test/java/com/android/tools/r8/missingclasses/MissingClassesTestBase.java
index f9941c0..8dfba19 100644
--- a/src/test/java/com/android/tools/r8/missingclasses/MissingClassesTestBase.java
+++ b/src/test/java/com/android/tools/r8/missingclasses/MissingClassesTestBase.java
@@ -17,7 +17,6 @@
 import com.android.tools.r8.diagnostic.internal.MissingDefinitionContextUtils;
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.utils.InternalOptions.TestingOptions;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.List;
@@ -93,7 +92,6 @@
       throws CompilationFailedException {
     testForR8(parameters.getBackend())
         .apply(configuration)
-        .addOptionsModification(TestingOptions::enableExperimentalMissingClassesReporting)
         .setMinApi(parameters.getApiLevel())
         .compileWithExpectedDiagnostics(diagnosticsConsumer);
   }
diff --git a/src/test/java/com/android/tools/r8/naming/OverloadUniqueNameTest.java b/src/test/java/com/android/tools/r8/naming/OverloadUniqueNameTest.java
index 6d9f975..166b2e1 100644
--- a/src/test/java/com/android/tools/r8/naming/OverloadUniqueNameTest.java
+++ b/src/test/java/com/android/tools/r8/naming/OverloadUniqueNameTest.java
@@ -108,15 +108,13 @@
     }
   }
 
-  private final TestParameters parameters;
-
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
     return getTestParameters().withNoneRuntime().build();
   }
 
   public OverloadUniqueNameTest(TestParameters parameters) {
-    this.parameters = parameters;
+    parameters.assertNoneRuntime();
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/naming/ProguardMapReaderPackageInfoTest.java b/src/test/java/com/android/tools/r8/naming/ProguardMapReaderPackageInfoTest.java
index c889dbb..9f172e3 100644
--- a/src/test/java/com/android/tools/r8/naming/ProguardMapReaderPackageInfoTest.java
+++ b/src/test/java/com/android/tools/r8/naming/ProguardMapReaderPackageInfoTest.java
@@ -17,7 +17,6 @@
 @RunWith(Parameterized.class)
 public class ProguardMapReaderPackageInfoTest extends TestBase {
 
-  private final TestParameters parameters;
   private final String MAPPING =
       StringUtils.joinLines(
           "android.support.v4.internal.package-info -> android.support.v4.internal.package-info:",
@@ -29,7 +28,7 @@
   }
 
   public ProguardMapReaderPackageInfoTest(TestParameters parameters) {
-    this.parameters = parameters;
+    parameters.assertNoneRuntime();
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/proguard/rules/NegatedClassMemberTest.java b/src/test/java/com/android/tools/r8/proguard/rules/NegatedClassMemberTest.java
index 8e0213a..053b36d 100644
--- a/src/test/java/com/android/tools/r8/proguard/rules/NegatedClassMemberTest.java
+++ b/src/test/java/com/android/tools/r8/proguard/rules/NegatedClassMemberTest.java
@@ -29,7 +29,7 @@
   }
 
   public NegatedClassMemberTest(TestParameters parameters) {
-
+    parameters.assertNoneRuntime();
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/regress/b113347830/B113347830.java b/src/test/java/com/android/tools/r8/regress/b113347830/B113347830.java
index 942ae66..76db740 100644
--- a/src/test/java/com/android/tools/r8/regress/b113347830/B113347830.java
+++ b/src/test/java/com/android/tools/r8/regress/b113347830/B113347830.java
@@ -20,9 +20,8 @@
 @RunWith(Parameterized.class)
 public class B113347830 extends TestBase {
 
-  public static final Class CLASS = B113347830.class;
+  public static final Class<?> CLASS = B113347830.class;
   public static final String NAME = CLASS.getSimpleName();
-  private final TestParameters parameters;
 
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
@@ -30,7 +29,7 @@
   }
 
   public B113347830(TestParameters parameters) {
-    this.parameters = parameters;
+    parameters.assertNoneRuntime();
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/relocator/RelocatorCommandTest.java b/src/test/java/com/android/tools/r8/relocator/RelocatorCommandTest.java
index 56e1f5d..0543b16 100644
--- a/src/test/java/com/android/tools/r8/relocator/RelocatorCommandTest.java
+++ b/src/test/java/com/android/tools/r8/relocator/RelocatorCommandTest.java
@@ -41,7 +41,9 @@
     return getTestParameters().withNoneRuntime().build();
   }
 
-  public RelocatorCommandTest(TestParameters parameters) {}
+  public RelocatorCommandTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
 
   @Test
   public void testCommandBuilder() throws CompilationFailedException, IOException {
diff --git a/src/test/java/com/android/tools/r8/relocator/RelocatorNoneClassFileTest.java b/src/test/java/com/android/tools/r8/relocator/RelocatorNoneClassFileTest.java
index deccd90..b83fd6c 100644
--- a/src/test/java/com/android/tools/r8/relocator/RelocatorNoneClassFileTest.java
+++ b/src/test/java/com/android/tools/r8/relocator/RelocatorNoneClassFileTest.java
@@ -39,7 +39,9 @@
     return getTestParameters().withNoneRuntime().build();
   }
 
-  public RelocatorNoneClassFileTest(TestParameters parameters) {}
+  public RelocatorNoneClassFileTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
 
   @Test
   public void testRewritingFiles()
diff --git a/src/test/java/com/android/tools/r8/relocator/RelocatorServiceLoaderTest.java b/src/test/java/com/android/tools/r8/relocator/RelocatorServiceLoaderTest.java
index 759082f..1cfb51c 100644
--- a/src/test/java/com/android/tools/r8/relocator/RelocatorServiceLoaderTest.java
+++ b/src/test/java/com/android/tools/r8/relocator/RelocatorServiceLoaderTest.java
@@ -45,7 +45,9 @@
     return getTestParameters().withNoneRuntime().build();
   }
 
-  public RelocatorServiceLoaderTest(TestParameters parameters) {}
+  public RelocatorServiceLoaderTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
 
   @Test
   public void testRewritingOfServicesForNotFoundClasses()
diff --git a/src/test/java/com/android/tools/r8/resolution/singletarget/InstantiatedLowerBoundTest.java b/src/test/java/com/android/tools/r8/resolution/singletarget/InstantiatedLowerBoundTest.java
index 9e89194..c636ef9 100644
--- a/src/test/java/com/android/tools/r8/resolution/singletarget/InstantiatedLowerBoundTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/singletarget/InstantiatedLowerBoundTest.java
@@ -40,7 +40,7 @@
   }
 
   public InstantiatedLowerBoundTest(TestParameters parameters) {
-    // Empty to satisfy construction of none-runtime.
+    parameters.assertNoneRuntime();
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/resolution/singletarget/SuccessAndInvalidLookupTest.java b/src/test/java/com/android/tools/r8/resolution/singletarget/SuccessAndInvalidLookupTest.java
index 916c7a0..a4b183c 100644
--- a/src/test/java/com/android/tools/r8/resolution/singletarget/SuccessAndInvalidLookupTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/singletarget/SuccessAndInvalidLookupTest.java
@@ -32,7 +32,7 @@
   }
 
   public SuccessAndInvalidLookupTest(TestParameters parameters) {
-    assert parameters.isNoneRuntime();
+    parameters.assertNoneRuntime();
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/KeptTargetsIncompleteLookupTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/KeptTargetsIncompleteLookupTest.java
index 7df1ec6..f6836fa 100644
--- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/KeptTargetsIncompleteLookupTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/KeptTargetsIncompleteLookupTest.java
@@ -45,7 +45,7 @@
   }
 
   public KeptTargetsIncompleteLookupTest(TestParameters parameters) {
-    // Empty to satisfy construction of none-runtime.
+    parameters.assertNoneRuntime();
   }
 
   private LookupResultSuccess testLookup(Class<?> methodToBeKept) throws Exception {
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
index 94abc05..84bc19c 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
@@ -4,7 +4,7 @@
 
 package com.android.tools.r8.retrace;
 
-import static com.android.tools.r8.retrace.Retrace.DEFAULT_REGULAR_EXPRESSION;
+import static com.android.tools.r8.retrace.internal.StackTraceRegularExpressionParser.DEFAULT_REGULAR_EXPRESSION;
 import static junit.framework.TestCase.assertEquals;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.MatcherAssert.assertThat;
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceVerboseTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceVerboseTests.java
index 2e75b1d..d217689 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceVerboseTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceVerboseTests.java
@@ -4,7 +4,7 @@
 
 package com.android.tools.r8.retrace;
 
-import static com.android.tools.r8.retrace.Retrace.DEFAULT_REGULAR_EXPRESSION;
+import static com.android.tools.r8.retrace.internal.StackTraceRegularExpressionParser.DEFAULT_REGULAR_EXPRESSION;
 import static junit.framework.TestCase.assertEquals;
 import static org.junit.Assume.assumeFalse;
 
diff --git a/src/test/java/com/android/tools/r8/retrace/SourceFileTest.java b/src/test/java/com/android/tools/r8/retrace/SourceFileTest.java
index fb7f280..9bfcffa 100644
--- a/src/test/java/com/android/tools/r8/retrace/SourceFileTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/SourceFileTest.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.retrace;
 
+import static com.android.tools.r8.retrace.internal.StackTraceRegularExpressionParser.DEFAULT_REGULAR_EXPRESSION;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.junit.Assert.assertEquals;
 
@@ -106,8 +107,7 @@
     StackTrace originalStackTrace = runResult.getOriginalStackTrace();
     StackTrace retracedStackTrace =
         originalStackTrace.retrace(
-            runResult.proguardMap(),
-            useRegularExpression ? Retrace.DEFAULT_REGULAR_EXPRESSION : null);
+            runResult.proguardMap(), useRegularExpression ? DEFAULT_REGULAR_EXPRESSION : null);
     runResult.inspectFailure(inspector -> consumer.accept(retracedStackTrace, inspector));
   }
 
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceRegularExpressionTests.java b/src/test/java/com/android/tools/r8/retrace/StackTraceRegularExpressionParserTests.java
similarity index 98%
rename from src/test/java/com/android/tools/r8/retrace/RetraceRegularExpressionTests.java
rename to src/test/java/com/android/tools/r8/retrace/StackTraceRegularExpressionParserTests.java
index 4f79193..333cc9f 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceRegularExpressionTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/StackTraceRegularExpressionParserTests.java
@@ -4,7 +4,7 @@
 
 package com.android.tools.r8.retrace;
 
-import static com.android.tools.r8.retrace.Retrace.DEFAULT_REGULAR_EXPRESSION;
+import static com.android.tools.r8.retrace.internal.StackTraceRegularExpressionParser.DEFAULT_REGULAR_EXPRESSION;
 import static junit.framework.TestCase.assertEquals;
 
 import com.android.tools.r8.TestBase;
@@ -25,14 +25,16 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class RetraceRegularExpressionTests extends TestBase {
+public class StackTraceRegularExpressionParserTests extends TestBase {
 
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
     return getTestParameters().withNoneRuntime().build();
   }
 
-  public RetraceRegularExpressionTests(TestParameters parameters) {}
+  public StackTraceRegularExpressionParserTests(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
 
   @Test
   public void ensureNotMatchingOnLiteral() {
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
index b479f43..f49dc97 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -168,7 +168,9 @@
     return getTestParameters().withNoneRuntime().build();
   }
 
-  public ProguardConfigurationParserTest(TestParameters parameters) {}
+  public ProguardConfigurationParserTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
 
   @Before
   public void reset() {
diff --git a/src/test/java/com/android/tools/r8/shaking/b169045091/NestMemberAccessibilityTest.java b/src/test/java/com/android/tools/r8/shaking/b169045091/NestMemberAccessibilityTest.java
index 3d157d4..d4d90eb 100644
--- a/src/test/java/com/android/tools/r8/shaking/b169045091/NestMemberAccessibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/b169045091/NestMemberAccessibilityTest.java
@@ -42,7 +42,7 @@
 
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return TestBase.getTestParameters().withNoneRuntime().build();
+    return getTestParameters().withNoneRuntime().build();
   }
 
   public NestMemberAccessibilityTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/tracereferences/TraceReferenceStaticFieldIndirectionTest.java b/src/test/java/com/android/tools/r8/tracereferences/TraceReferenceStaticFieldIndirectionTest.java
index b0e6069..b757b62 100644
--- a/src/test/java/com/android/tools/r8/tracereferences/TraceReferenceStaticFieldIndirectionTest.java
+++ b/src/test/java/com/android/tools/r8/tracereferences/TraceReferenceStaticFieldIndirectionTest.java
@@ -33,7 +33,9 @@
     return getTestParameters().withNoneRuntime().build();
   }
 
-  public TraceReferenceStaticFieldIndirectionTest(TestParameters parameters) {}
+  public TraceReferenceStaticFieldIndirectionTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
 
   static class MissingReferencesConsumer implements TraceReferencesConsumer {
 
diff --git a/src/test/java/com/android/tools/r8/tracereferences/TraceReferenceStaticMethodIndirectionTest.java b/src/test/java/com/android/tools/r8/tracereferences/TraceReferenceStaticMethodIndirectionTest.java
index 3c35e76..9b460cb 100644
--- a/src/test/java/com/android/tools/r8/tracereferences/TraceReferenceStaticMethodIndirectionTest.java
+++ b/src/test/java/com/android/tools/r8/tracereferences/TraceReferenceStaticMethodIndirectionTest.java
@@ -34,7 +34,9 @@
     return getTestParameters().withNoneRuntime().build();
   }
 
-  public TraceReferenceStaticMethodIndirectionTest(TestParameters parameters) {}
+  public TraceReferenceStaticMethodIndirectionTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
 
   static class MissingReferencesConsumer implements TraceReferencesConsumer {
 
diff --git a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesArrayTypesTest.java b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesArrayTypesTest.java
index a1b4180..8fe6289 100644
--- a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesArrayTypesTest.java
+++ b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesArrayTypesTest.java
@@ -33,7 +33,9 @@
     return getTestParameters().withNoneRuntime().build();
   }
 
-  public TraceReferencesArrayTypesTest(TestParameters parameters) {}
+  public TraceReferencesArrayTypesTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
 
   static class MissingReferencesConsumer implements TraceReferencesConsumer {
 
diff --git a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java
index 25a8684..5138617 100644
--- a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java
+++ b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java
@@ -48,7 +48,9 @@
     return getTestParameters().withNoneRuntime().build();
   }
 
-  public TraceReferencesCommandTest(TestParameters parameters) {}
+  public TraceReferencesCommandTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
 
   @Test(expected = CompilationFailedException.class)
   public void emptyBuilder() throws Throwable {
diff --git a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesDiagnosticTest.java b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesDiagnosticTest.java
index 418a282..4f456f6 100644
--- a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesDiagnosticTest.java
+++ b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesDiagnosticTest.java
@@ -36,7 +36,9 @@
     return getTestParameters().withNoneRuntime().build();
   }
 
-  public TraceReferencesDiagnosticTest(TestParameters parameters) {}
+  public TraceReferencesDiagnosticTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
 
   @Test
   public void traceReferencesDiagnosticClassesFieldsAndMethods() throws Throwable {
diff --git a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesMissingReferencesInDexTest.java b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesMissingReferencesInDexTest.java
index b5b26b0..61f5dab 100644
--- a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesMissingReferencesInDexTest.java
+++ b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesMissingReferencesInDexTest.java
@@ -31,7 +31,9 @@
     return getTestParameters().withNoneRuntime().build();
   }
 
-  public TraceReferencesMissingReferencesInDexTest(TestParameters parameters) {}
+  public TraceReferencesMissingReferencesInDexTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
 
   static class MissingReferencesConsumer implements TraceReferencesConsumer {
 
diff --git a/src/test/java/com/android/tools/r8/utils/DexVersionUnitTests.java b/src/test/java/com/android/tools/r8/utils/DexVersionUnitTests.java
index 7174d0e..9eea32f 100644
--- a/src/test/java/com/android/tools/r8/utils/DexVersionUnitTests.java
+++ b/src/test/java/com/android/tools/r8/utils/DexVersionUnitTests.java
@@ -16,15 +16,13 @@
 @RunWith(Parameterized.class)
 public class DexVersionUnitTests extends TestBase {
 
-  private final TestParameters parameters;
-
   @Parameterized.Parameters(name = "{0}")
   public static TestParametersCollection data() {
     return getTestParameters().withNoneRuntime().build();
   }
 
   public DexVersionUnitTests(TestParameters parameters) {
-    this.parameters = parameters;
+    parameters.assertNoneRuntime();
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/utils/SegmentTreeTests.java b/src/test/java/com/android/tools/r8/utils/SegmentTreeTests.java
index bb2ea17..9c79177 100644
--- a/src/test/java/com/android/tools/r8/utils/SegmentTreeTests.java
+++ b/src/test/java/com/android/tools/r8/utils/SegmentTreeTests.java
@@ -19,15 +19,13 @@
 @RunWith(Parameterized.class)
 public class SegmentTreeTests extends TestBase {
 
-  private final TestParameters parameters;
-
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
     return getTestParameters().withNoneRuntime().build();
   }
 
   public SegmentTreeTests(TestParameters parameters) {
-    this.parameters = parameters;
+    parameters.assertNoneRuntime();
   }
 
   private SegmentTree<Integer> tree(boolean allowOverrides) {
diff --git a/src/test/java/com/android/tools/r8/utils/SemanticVersionTests.java b/src/test/java/com/android/tools/r8/utils/SemanticVersionTests.java
index 8f0dd61..cd30341 100644
--- a/src/test/java/com/android/tools/r8/utils/SemanticVersionTests.java
+++ b/src/test/java/com/android/tools/r8/utils/SemanticVersionTests.java
@@ -4,14 +4,12 @@
 package com.android.tools.r8.utils;
 
 import static com.android.tools.r8.utils.SemanticVersion.parse;
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.TestRuntime.NoneRuntime;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -25,7 +23,7 @@
   }
 
   public SemanticVersionTests(TestParameters parameters) {
-    assertEquals(NoneRuntime.getInstance(), parameters.getRuntime());
+    parameters.assertNoneRuntime();
   }
 
   @Test
diff --git a/src/test/debugTestResourcesKotlin/KotlinApp.kt b/src/test/kotlinR8TestResources/debug/KotlinApp.kt
similarity index 100%
rename from src/test/debugTestResourcesKotlin/KotlinApp.kt
rename to src/test/kotlinR8TestResources/debug/KotlinApp.kt
diff --git a/src/test/debugTestResourcesKotlin/KotlinInline.kt b/src/test/kotlinR8TestResources/debug/KotlinInline.kt
similarity index 100%
rename from src/test/debugTestResourcesKotlin/KotlinInline.kt
rename to src/test/kotlinR8TestResources/debug/KotlinInline.kt
diff --git a/third_party/openjdk/desugar_jdk_libs.tar.gz.sha1 b/third_party/openjdk/desugar_jdk_libs.tar.gz.sha1
index 1fe619a..5b0c632 100644
--- a/third_party/openjdk/desugar_jdk_libs.tar.gz.sha1
+++ b/third_party/openjdk/desugar_jdk_libs.tar.gz.sha1
@@ -1 +1 @@
-653b4fe97b5dface6a31b9a6d94063a91a7f80be
\ No newline at end of file
+babe03fe1d812b8eddebfc3e9a1ea4add98d4647
\ No newline at end of file