Delete kotlin lambda merger

Fixes: 172800423
Fixes: 174809311
Bug: 179019716
Bug: 179018501
Bug: 173337498
Bug: 141719453
Change-Id: Ia0ef62d9e52a5dcbe2cd1064386a76951fd3c0db
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index da20df4..c5eac58 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -476,7 +476,6 @@
     assert !internal.enableEnumValueOptimization;
     assert !internal.outline.enabled;
     assert !internal.enableValuePropagation;
-    assert !internal.enableLambdaMerging;
     assert !internal.enableTreeShakingOfLibraryMethodOverrides;
 
     internal.desugarState = getDesugarState();
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index 8bf7cc6..b552bb2 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -174,7 +174,6 @@
     assert !internal.enableEnumValueOptimization;
     assert !internal.outline.enabled;
     assert !internal.enableValuePropagation;
-    assert !internal.enableLambdaMerging;
     assert !internal.enableTreeShakingOfLibraryMethodOverrides;
 
     assert internal.desugarState == DesugarState.ON;
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 0fa55a7..8d07b18 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -834,10 +834,6 @@
     internal.desugarState = getDesugarState();
     assert internal.isShrinking() == getEnableTreeShaking();
     assert internal.isMinifying() == getEnableMinification();
-    // In current implementation we only enable lambda merger if the tree
-    // shaking is enabled. This is caused by the fact that we rely on tree
-    // shaking for removing the lambda classes which should be revised later.
-    internal.enableLambdaMerging = getEnableTreeShaking();
     assert !internal.ignoreMissingClasses;
     internal.ignoreMissingClasses =
         proguardConfiguration.isIgnoreWarnings()
@@ -871,14 +867,12 @@
       internal.enableClassStaticizer = false;
       internal.outline.enabled = false;
       internal.enableEnumUnboxing = false;
-      internal.enableLambdaMerging = false;
     }
     if (!internal.isShrinking()) {
       // If R8 is not shrinking, there is no point in running various optimizations since the
       // optimized classes will still remain in the program (the application size could increase).
       internal.enableEnumUnboxing = false;
       internal.horizontalClassMergerOptions().disable();
-      internal.enableLambdaMerging = false;
       internal.enableVerticalClassMerging = false;
     }
 
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 7d574dd..3b0475f 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -9,7 +9,6 @@
 import com.android.tools.r8.graph.DexValue.DexValueString;
 import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
 import com.android.tools.r8.graph.analysis.InitializedClassesInInstanceMethodsAnalysis.InitializedClassesInInstanceMethods;
-import com.android.tools.r8.graph.classmerging.HorizontallyMergedLambdaClasses;
 import com.android.tools.r8.graph.classmerging.MergedClassesCollection;
 import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
 import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
@@ -91,7 +90,6 @@
   private boolean allCodeProcessed = false;
   private Predicate<DexType> classesEscapingIntoLibrary = Predicates.alwaysTrue();
   private InitializedClassesInInstanceMethods initializedClassesInInstanceMethods;
-  private HorizontallyMergedLambdaClasses horizontallyMergedLambdaClasses;
   private HorizontallyMergedClasses horizontallyMergedClasses;
   private VerticallyMergedClasses verticallyMergedClasses;
   private EnumDataMap unboxedEnums = EnumDataMap.empty();
@@ -483,9 +481,6 @@
     if (horizontallyMergedClasses != null) {
       collection.add(horizontallyMergedClasses);
     }
-    if (horizontallyMergedLambdaClasses != null) {
-      collection.add(horizontallyMergedLambdaClasses);
-    }
     if (verticallyMergedClasses != null) {
       collection.add(verticallyMergedClasses);
     }
@@ -493,23 +488,6 @@
   }
 
   /**
-   * Get the result of horizontal lambda class merging. Returns null if horizontal lambda class
-   * merging has not been run.
-   */
-  public HorizontallyMergedLambdaClasses horizontallyMergedLambdaClasses() {
-    return horizontallyMergedLambdaClasses;
-  }
-
-  public void setHorizontallyMergedLambdaClasses(
-      HorizontallyMergedLambdaClasses horizontallyMergedLambdaClasses) {
-    assert this.horizontallyMergedLambdaClasses == null;
-    this.horizontallyMergedLambdaClasses = horizontallyMergedLambdaClasses;
-    testing()
-        .horizontallyMergedLambdaClassesConsumer
-        .accept(dexItemFactory(), horizontallyMergedLambdaClasses);
-  }
-
-  /**
    * Get the result of horizontal class merging. Returns null if horizontal class merging has not
    * been run.
    */
diff --git a/src/main/java/com/android/tools/r8/graph/classmerging/HorizontallyMergedLambdaClasses.java b/src/main/java/com/android/tools/r8/graph/classmerging/HorizontallyMergedLambdaClasses.java
deleted file mode 100644
index 3a9f618..0000000
--- a/src/main/java/com/android/tools/r8/graph/classmerging/HorizontallyMergedLambdaClasses.java
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.graph.classmerging;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroup;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.collections.BidirectionalManyToOneHashMap;
-import com.android.tools.r8.utils.collections.BidirectionalManyToOneMap;
-import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneMap;
-import java.util.Collections;
-import java.util.Map;
-import java.util.Set;
-import java.util.function.BiConsumer;
-
-public class HorizontallyMergedLambdaClasses implements MergedClasses {
-
-  private final BidirectionalManyToOneMap<DexType, DexType> mergedClasses;
-
-  public HorizontallyMergedLambdaClasses(Map<DexType, LambdaGroup> lambdas) {
-    MutableBidirectionalManyToOneMap<DexType, DexType> mergedClasses =
-        new BidirectionalManyToOneHashMap<>();
-    lambdas.forEach((lambda, group) -> mergedClasses.put(lambda, group.getGroupClassType()));
-    this.mergedClasses = mergedClasses;
-  }
-
-  public static HorizontallyMergedLambdaClasses empty() {
-    return new HorizontallyMergedLambdaClasses(Collections.emptyMap());
-  }
-
-  @Override
-  public void forEachMergeGroup(BiConsumer<Set<DexType>, DexType> consumer) {
-    mergedClasses.forEachManyToOneMapping(consumer);
-  }
-
-  @Override
-  public boolean hasBeenMergedIntoDifferentType(DexType type) {
-    return mergedClasses.containsKey(type);
-  }
-
-  @Override
-  public boolean isMergeTarget(DexType type) {
-    return mergedClasses.containsValue(type);
-  }
-
-  @Override
-  public boolean verifyAllSourcesPruned(AppView<AppInfoWithLiveness> appView) {
-    for (DexType source : mergedClasses.keySet()) {
-      assert appView.appInfo().wasPruned(source)
-          : "Expected horizontally merged lambda class `"
-              + source.toSourceString()
-              + "` to be absent";
-    }
-    return true;
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
index dd0486a..ef1b786 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -23,7 +23,6 @@
 import com.android.tools.r8.horizontalclassmerging.policies.NoInnerClasses;
 import com.android.tools.r8.horizontalclassmerging.policies.NoInterfaces;
 import com.android.tools.r8.horizontalclassmerging.policies.NoKeepRules;
-import com.android.tools.r8.horizontalclassmerging.policies.NoKotlinLambdas;
 import com.android.tools.r8.horizontalclassmerging.policies.NoKotlinMetadata;
 import com.android.tools.r8.horizontalclassmerging.policies.NoNativeMethods;
 import com.android.tools.r8.horizontalclassmerging.policies.NoServiceLoaders;
@@ -138,13 +137,12 @@
         new NoEnums(appView),
         new CheckAbstractClasses(appView),
         new IgnoreSynthetics(appView),
-        new NoClassesOrMembersWithAnnotations(),
+        new NoClassesOrMembersWithAnnotations(appView),
         new NoInnerClasses(),
         new NoClassInitializerWithObservableSideEffects(),
         new NoNativeMethods(),
         new NoKeepRules(appView),
         new NoKotlinMetadata(),
-        new NoKotlinLambdas(appView),
         new NoServiceLoaders(appView),
         new NotVerticallyMergedIntoSubtype(appView),
         new NoDirectRuntimeTypeChecks(runtimeTypeCheckInfo),
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassesOrMembersWithAnnotations.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassesOrMembersWithAnnotations.java
index f9d2106..f0b2a6e 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassesOrMembersWithAnnotations.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassesOrMembersWithAnnotations.java
@@ -4,12 +4,28 @@
 
 package com.android.tools.r8.horizontalclassmerging.policies;
 
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.InternalOptions.HorizontalClassMergerOptions;
 
 public class NoClassesOrMembersWithAnnotations extends SingleClassPolicy {
+
+  private final HorizontalClassMergerOptions options;
+
+  public NoClassesOrMembersWithAnnotations(AppView<AppInfoWithLiveness> appView) {
+    this.options = appView.options().horizontalClassMergerOptions();
+  }
+
   @Override
   public boolean canMerge(DexProgramClass program) {
     return !program.hasClassOrMemberAnnotations();
   }
+
+  @Override
+  public boolean shouldSkipPolicy() {
+    // TODO(b/179019716): Add support for merging in presence of annotations.
+    return options.skipNoClassesOrMembersWithAnnotationsPolicyForTesting;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInnerClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInnerClasses.java
index ddb2aef..cc9f61c 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInnerClasses.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInnerClasses.java
@@ -11,6 +11,7 @@
 
   @Override
   public boolean canMerge(DexProgramClass program) {
+    // TODO(b/179018501): allow merging classes with inner/outer classes.
     return program.getInnerClasses().isEmpty();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKotlinLambdas.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKotlinLambdas.java
deleted file mode 100644
index dee7a4a..0000000
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKotlinLambdas.java
+++ /dev/null
@@ -1,33 +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.horizontalclassmerging.policies;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-
-public class NoKotlinLambdas extends SingleClassPolicy {
-  private final AppView<AppInfoWithLiveness> appView;
-
-  public NoKotlinLambdas(AppView<AppInfoWithLiveness> appView) {
-    this.appView = appView;
-  }
-
-  @Override
-  public boolean shouldSkipPolicy() {
-    return appView.options().horizontalClassMergerOptions().isKotlinLambdaMergingEnabled();
-  }
-
-  @Override
-  public boolean canMerge(DexProgramClass program) {
-    if (program.getKotlinInfo().isNoKotlinInformation()
-        || !program.getKotlinInfo().isSyntheticClass()) {
-      return true;
-    }
-
-    return !program.getKotlinInfo().asSyntheticClass().isLambda();
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKotlinMetadata.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKotlinMetadata.java
index 92f2938..1acad7a 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKotlinMetadata.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKotlinMetadata.java
@@ -19,12 +19,8 @@
   }
 
   private boolean verifyNoUnexpectedKotlinInfo(DexProgramClass clazz) {
-    if (clazz.getKotlinInfo().isNoKotlinInformation()) {
-      assert verifyNoUnexpectedKotlinMemberInfo(clazz);
-      return true;
-    }
-    assert clazz.getKotlinInfo().isSyntheticClass()
-        && clazz.getKotlinInfo().asSyntheticClass().isLambda();
+    assert clazz.getKotlinInfo().isNoKotlinInformation();
+    assert verifyNoUnexpectedKotlinMemberInfo(clazz);
     return true;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 6960b96..22ee5c5 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -23,7 +23,6 @@
 import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.classmerging.HorizontallyMergedLambdaClasses;
 import com.android.tools.r8.ir.analysis.TypeChecker;
 import com.android.tools.r8.ir.analysis.VerifyTypesHelper;
 import com.android.tools.r8.ir.analysis.constant.SparseConditionalConstantPropagation;
@@ -82,7 +81,6 @@
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
 import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
-import com.android.tools.r8.ir.optimize.lambda.LambdaMerger;
 import com.android.tools.r8.ir.optimize.staticizer.ClassStaticizer;
 import com.android.tools.r8.ir.optimize.string.StringBuilderOptimizer;
 import com.android.tools.r8.ir.optimize.string.StringOptimizer;
@@ -140,7 +138,6 @@
   private final TwrCloseResourceRewriter twrCloseResourceRewriter;
   private final BackportedMethodRewriter backportedMethodRewriter;
   private final DesugaredLibraryRetargeter desugaredLibraryRetargeter;
-  private final LambdaMerger lambdaMerger;
   private final ClassInliner classInliner;
   private final ClassStaticizer classStaticizer;
   private final InternalOptions options;
@@ -242,7 +239,6 @@
               : null;
       this.d8NestBasedAccessDesugaring =
           options.shouldDesugarNests() ? new D8NestBasedAccessDesugaring(appView) : null;
-      this.lambdaMerger = null;
       this.covariantReturnTypeAnnotationTransformer = null;
       this.dynamicTypeOptimization = null;
       this.classInliner = null;
@@ -305,12 +301,9 @@
           options.enableTreeShakingOfLibraryMethodOverrides
               ? new LibraryMethodOverrideAnalysis(appViewWithLiveness)
               : null;
-      this.lambdaMerger =
-          options.enableLambdaMerging ? new LambdaMerger(appViewWithLiveness) : null;
       this.enumUnboxer = options.enableEnumUnboxing ? new EnumUnboxer(appViewWithLiveness) : null;
       this.lensCodeRewriter = new LensCodeRewriter(appViewWithLiveness, enumUnboxer);
-      this.inliner =
-          new Inliner(appViewWithLiveness, mainDexClasses, lambdaMerger, lensCodeRewriter);
+      this.inliner = new Inliner(appViewWithLiveness, mainDexClasses, lensCodeRewriter);
       this.outliner = new Outliner(appViewWithLiveness);
       this.memberValuePropagation =
           options.enableValuePropagation ? new MemberValuePropagation(appViewWithLiveness) : null;
@@ -346,7 +339,6 @@
       this.fieldAccessAnalysis = null;
       this.libraryMethodOverrideAnalysis = null;
       this.inliner = null;
-      this.lambdaMerger = null;
       this.outliner = null;
       this.memberValuePropagation = null;
       this.lensCodeRewriter = null;
@@ -674,7 +666,6 @@
     DexApplication application = appView.appInfo().app();
 
     computeReachabilitySensitivity(application);
-    collectLambdaMergingCandidates(application);
     collectStaticizerCandidates(application);
     workaroundAbstractMethodOnNonAbstractClassVerificationBug(
         executorService, simpleOptimizationFeedback);
@@ -800,10 +791,6 @@
     synthesizeRetargetClass(builder, executorService);
     synthesizeEnumUnboxingUtilityMethods(executorService);
 
-    printPhase("Lambda merging finalization");
-    // TODO(b/127694949): Adapt to PostOptimization.
-    finalizeLambdaMerging(application, feedback, builder, executorService, initialGraphLensForIR);
-
     printPhase("Desugared library API Conversion finalization");
     generateDesugaredLibraryAPIWrappers(builder, executorService);
 
@@ -962,28 +949,6 @@
         method, code, OptimizationFeedbackIgnore.getInstance(), Timing.empty());
   }
 
-  private void collectLambdaMergingCandidates(DexApplication application) {
-    if (lambdaMerger != null) {
-      lambdaMerger.collectGroupCandidates(application);
-    }
-  }
-
-  private void finalizeLambdaMerging(
-      DexApplication application,
-      OptimizationFeedback feedback,
-      Builder<?> builder,
-      ExecutorService executorService,
-      GraphLens appliedGraphLens)
-      throws ExecutionException {
-    if (lambdaMerger != null) {
-      lambdaMerger.applyLambdaClassMapping(
-          application, this, feedback, builder, executorService, appliedGraphLens);
-    } else {
-      appView.setHorizontallyMergedLambdaClasses(HorizontallyMergedLambdaClasses.empty());
-    }
-    assert appView.horizontallyMergedLambdaClasses() != null;
-  }
-
   private void generateDesugaredLibraryAPIWrappers(
       DexApplication.Builder<?> builder, ExecutorService executorService)
       throws ExecutionException {
@@ -1233,13 +1198,6 @@
             || !appView.appInfo().withLiveness().isNeverReprocessMethod(method.method)
         : "Illegal reprocessing due to -neverreprocess rule: " + context.toSourceString();
 
-    if (lambdaMerger != null) {
-      timing.begin("Merge lambdas");
-      lambdaMerger.rewriteCode(code.context(), code, inliner, methodProcessor);
-      timing.end();
-      assert code.isConsistentSSA();
-    }
-
     if (typeChecker != null && !typeChecker.check(code)) {
       assert appView.enableWholeProgramOptimizations();
       assert options.testing.allowTypeErrors;
@@ -1542,15 +1500,6 @@
 
     previous = printMethod(code, "IR after twr close resource rewriter (SSA)", previous);
 
-    if (lambdaMerger != null) {
-      timing.begin("Analyze lambda merging");
-      lambdaMerger.analyzeCode(code.context(), code);
-      timing.end();
-      assert code.isConsistentSSA();
-    }
-
-    previous = printMethod(code, "IR after lambda merger (SSA)", previous);
-
     // TODO(b/140766440): an ideal solution would be puttting CodeOptimization for this into
     //  the list for primary processing only.
     if (options.outline.enabled && outliner != null && methodProcessor.isPrimaryMethodProcessor()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 7eca624..9e726b9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -57,7 +57,6 @@
 import com.android.tools.r8.ir.optimize.inliner.InliningReasonStrategy;
 import com.android.tools.r8.ir.optimize.inliner.NopWhyAreYouNotInliningReporter;
 import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
-import com.android.tools.r8.ir.optimize.lambda.LambdaMerger;
 import com.android.tools.r8.kotlin.Kotlin;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.MainDexTracingResult;
@@ -84,7 +83,6 @@
 
   protected final AppView<AppInfoWithLiveness> appView;
   private final Set<DexMethod> extraNeverInlineMethods;
-  private final LambdaMerger lambdaMerger;
   private final LensCodeRewriter lensCodeRewriter;
   final MainDexTracingResult mainDexClasses;
 
@@ -100,7 +98,6 @@
   public Inliner(
       AppView<AppInfoWithLiveness> appView,
       MainDexTracingResult mainDexClasses,
-      LambdaMerger lambdaMerger,
       LensCodeRewriter lensCodeRewriter) {
     Kotlin.Intrinsics intrinsics = appView.dexItemFactory().kotlin.intrinsics;
     this.appView = appView;
@@ -108,7 +105,6 @@
         appView.options().kotlinOptimizationOptions().disableKotlinSpecificOptimizations
             ? ImmutableSet.of()
             : ImmutableSet.of(intrinsics.throwNpe, intrinsics.throwParameterIsNullException);
-    this.lambdaMerger = lambdaMerger;
     this.lensCodeRewriter = lensCodeRewriter;
     this.mainDexClasses = mainDexClasses;
     availableApiExceptions =
@@ -596,7 +592,6 @@
         InvokeMethod invoke,
         ProgramMethod context,
         InliningIRProvider inliningIRProvider,
-        LambdaMerger lambdaMerger,
         LensCodeRewriter lensCodeRewriter) {
       DexItemFactory dexItemFactory = appView.dexItemFactory();
       InternalOptions options = appView.options();
@@ -743,9 +738,6 @@
         assert lensCodeRewriter != null;
         lensCodeRewriter.rewrite(code, target);
       }
-      if (lambdaMerger != null) {
-        lambdaMerger.rewriteCodeForInlining(target, code, context, inliningIRProvider);
-      }
       if (options.testing.inlineeIrModifier != null) {
         options.testing.inlineeIrModifier.accept(code);
       }
@@ -1040,7 +1032,7 @@
 
           InlineeWithReason inlinee =
               action.buildInliningIR(
-                  appView, invoke, context, inliningIRProvider, lambdaMerger, lensCodeRewriter);
+                  appView, invoke, context, inliningIRProvider, lensCodeRewriter);
           if (strategy.willExceedBudget(
               code, invoke, inlinee, block, whyAreYouNotInliningReporter)) {
             assert whyAreYouNotInliningReporter.unsetReasonHasBeenReportedFlag();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/CaptureSignature.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/CaptureSignature.java
deleted file mode 100644
index 5a39e71..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/CaptureSignature.java
+++ /dev/null
@@ -1,160 +0,0 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.optimize.lambda;
-
-import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexTypeList;
-import it.unimi.dsi.fastutil.ints.IntArrayList;
-import it.unimi.dsi.fastutil.ints.IntList;
-import it.unimi.dsi.fastutil.ints.IntLists;
-import java.util.Arrays;
-import java.util.BitSet;
-import java.util.Comparator;
-import java.util.List;
-import java.util.function.IntFunction;
-
-// While mapping fields representing lambda captures we rearrange fields to make sure
-// lambdas having different order of the fields still can be merged. We also store
-// all captures of reference types in fields of java.lang.Objects class.
-//
-// This allows us to use same lambda groups class for these two different lambdas:
-//
-//      Lambda Group Class         Lambda$1               Lambda$2
-//          Object $c0        int      foo -> $c1     char foo -> $c2
-//          int    $c1        String[] bar -> $c0     int  bar -> $c1
-//          char   $c2        char     baz -> $c2     List baz -> $c0
-//
-// Capture signature is represented by a string with sorted shorties of field
-// types, such as "CIL" for both Lambda$1 and Lambda$2 in the above example.
-//
-public final class CaptureSignature {
-  private static final IntList EMPTY_LIST = IntLists.EMPTY_LIST;
-  private static final IntList SINGLE_LIST = IntLists.singleton(0);
-
-  private CaptureSignature() {
-  }
-
-  // Returns an array representing mapping of fields of the normalized capture
-  // into fields of original capture such that:
-  //
-  //   mapping = getReverseCaptureMapping(...)
-  //   <original-capture-index> = mapping[<normalized-capture-index>]
-  public static IntList getReverseCaptureMapping(DexType[] types) {
-    if (types.length == 0) {
-      return EMPTY_LIST;
-    }
-
-    if (types.length == 1) {
-      return SINGLE_LIST;
-    }
-
-    IntList result = new IntArrayList(types.length);
-    for (int i = 0; i < types.length; i++) {
-      result.add(i);
-    }
-    // Sort the indices by shorties (sorting is stable).
-    result.sort(Comparator.comparingInt(i -> types[i].toShorty()));
-    assert verifyMapping(result);
-    return result;
-  }
-
-  // Given a capture signature and an index returns the type of the field.
-  public static DexType fieldType(DexItemFactory factory, String capture, int index) {
-    switch (capture.charAt(index)) {
-      case 'L':
-        return factory.objectType;
-      case 'Z':
-        return factory.booleanType;
-      case 'B':
-        return factory.byteType;
-      case 'S':
-        return factory.shortType;
-      case 'C':
-        return factory.charType;
-      case 'I':
-        return factory.intType;
-      case 'F':
-        return factory.floatType;
-      case 'J':
-        return factory.longType;
-      case 'D':
-        return factory.doubleType;
-      default:
-        throw new Unreachable("Invalid capture character: " + capture.charAt(index));
-    }
-  }
-
-  private static String getCaptureSignature(int size, IntFunction<DexType> type) {
-    if (size == 0) {
-      return "";
-    }
-    if (size == 1) {
-      return Character.toString(type.apply(0).toShorty());
-    }
-
-    char[] chars = new char[size];
-    for (int i = 0; i < size; i++) {
-      chars[i] = type.apply(i).toShorty();
-    }
-    Arrays.sort(chars);
-    return new String(chars);
-  }
-
-  // Compute capture signature based on lambda class capture fields.
-  public static String getCaptureSignature(List<DexEncodedField> fields) {
-    return getCaptureSignature(fields.size(), i -> fields.get(i).field.type);
-  }
-
-  // Compute capture signature based on type list.
-  public static String getCaptureSignature(DexTypeList types) {
-    return getCaptureSignature(types.values.length, i -> types.values[i]);
-  }
-
-  // Having a list of fields of lambda captured values, maps one of them into
-  // an index of the appropriate field in normalized capture signature.
-  public static int mapFieldIntoCaptureIndex(
-      String capture, List<DexEncodedField> lambdaFields, DexField fieldToMap) {
-    char fieldKind = fieldToMap.type.toShorty();
-    int numberOfSameCaptureKind = 0;
-    int result = -1;
-
-    for (DexEncodedField encodedField : lambdaFields) {
-      if (encodedField.field == fieldToMap) {
-        result = numberOfSameCaptureKind;
-        break;
-      }
-      if (encodedField.field.type.toShorty() == fieldKind) {
-        numberOfSameCaptureKind++;
-      }
-    }
-
-    assert result >= 0 : "Field was not found in the lambda class.";
-
-    for (int index = 0; index < capture.length(); index++) {
-      if (capture.charAt(index) == fieldKind) {
-        if (result == 0) {
-          return index;
-        }
-        result--;
-      }
-    }
-
-    throw new Unreachable("Were not able to map lambda field into capture index");
-  }
-
-  private static boolean verifyMapping(IntList mapping) {
-    BitSet bits = new BitSet();
-    for (Integer i : mapping) {
-      assert i >= 0 && i < mapping.size();
-      assert !bits.get(i);
-      bits.set(i);
-    }
-    return true;
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/CodeProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/CodeProcessor.java
deleted file mode 100644
index 1da512d..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/CodeProcessor.java
+++ /dev/null
@@ -1,430 +0,0 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.optimize.lambda;
-
-import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.code.Argument;
-import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.CheckCast;
-import com.android.tools.r8.ir.code.ConstClass;
-import com.android.tools.r8.ir.code.ConstMethodHandle;
-import com.android.tools.r8.ir.code.ConstMethodType;
-import com.android.tools.r8.ir.code.DefaultInstructionVisitor;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.InitClass;
-import com.android.tools.r8.ir.code.InstanceGet;
-import com.android.tools.r8.ir.code.InstancePut;
-import com.android.tools.r8.ir.code.InstructionListIterator;
-import com.android.tools.r8.ir.code.Invoke;
-import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.NewArrayEmpty;
-import com.android.tools.r8.ir.code.NewInstance;
-import com.android.tools.r8.ir.code.StaticGet;
-import com.android.tools.r8.ir.code.StaticPut;
-import com.android.tools.r8.ir.optimize.lambda.LambdaMerger.ApplyStrategy;
-import com.android.tools.r8.kotlin.Kotlin;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import java.util.ListIterator;
-import java.util.function.Function;
-
-// Performs processing of the method code (all methods) needed by lambda rewriter.
-//
-// Functionality can be modified by strategy (specific to lambda group) and lambda
-// type visitor.
-//
-// In general it is used to
-//   (a) track the code for illegal lambda type usages inside the code, and
-//   (b) patching valid lambda type references to point to lambda group classes instead.
-//
-// This class is also used inside particular strategies as a context of the instruction
-// being checked or patched, it provides access to code, block and instruction iterators.
-public abstract class CodeProcessor extends DefaultInstructionVisitor<Void> {
-  // Strategy (specific to lambda group) for detecting valid references to the
-  // lambda classes (of this group) and patching them with group class references.
-  public interface Strategy {
-    LambdaGroup group();
-
-    boolean isValidStaticFieldWrite(CodeProcessor context, DexField field);
-
-    boolean isValidStaticFieldRead(CodeProcessor context, DexField field);
-
-    boolean isValidInstanceFieldWrite(CodeProcessor context, DexField field);
-
-    boolean isValidInstanceFieldRead(CodeProcessor context, DexField field);
-
-    boolean isValidInvoke(CodeProcessor context, InvokeMethod invoke);
-
-    boolean isValidNewInstance(CodeProcessor context, NewInstance invoke);
-
-    boolean isValidInitClass(CodeProcessor context, DexType clazz);
-
-    boolean isValidHolder(CodeProcessor context, DexType holder);
-
-    void patch(ApplyStrategy context, NewInstance newInstance);
-
-    void patch(ApplyStrategy context, InvokeMethod invoke);
-
-    void patch(ApplyStrategy context, InstanceGet instanceGet);
-
-    void patch(ApplyStrategy context, StaticGet staticGet);
-
-    void patch(ApplyStrategy context, InitClass initClass);
-
-    void patch(ApplyStrategy context, Argument argument);
-  }
-
-  // No-op strategy.
-  static final Strategy NoOp =
-      new Strategy() {
-        @Override
-        public LambdaGroup group() {
-          return null;
-        }
-
-        @Override
-        public boolean isValidInstanceFieldWrite(CodeProcessor context, DexField field) {
-          return false;
-        }
-
-        @Override
-        public boolean isValidInstanceFieldRead(CodeProcessor context, DexField field) {
-          return false;
-        }
-
-        @Override
-        public boolean isValidStaticFieldWrite(CodeProcessor context, DexField field) {
-          return false;
-        }
-
-        @Override
-        public boolean isValidStaticFieldRead(CodeProcessor context, DexField field) {
-          return false;
-        }
-
-        @Override
-        public boolean isValidInvoke(CodeProcessor context, InvokeMethod invoke) {
-          return false;
-        }
-
-        @Override
-        public boolean isValidNewInstance(CodeProcessor context, NewInstance invoke) {
-          return false;
-        }
-
-        @Override
-        public boolean isValidInitClass(CodeProcessor context, DexType clazz) {
-          return false;
-        }
-
-        @Override
-        public boolean isValidHolder(CodeProcessor context, DexType holder) {
-          return false;
-        }
-
-        @Override
-        public void patch(ApplyStrategy context, NewInstance newInstance) {
-          throw new Unreachable();
-        }
-
-        @Override
-        public void patch(ApplyStrategy context, InvokeMethod invoke) {
-          throw new Unreachable();
-        }
-
-        @Override
-        public void patch(ApplyStrategy context, InstanceGet instanceGet) {
-          throw new Unreachable();
-        }
-
-        @Override
-        public void patch(ApplyStrategy context, StaticGet staticGet) {
-          throw new Unreachable();
-        }
-
-        @Override
-        public void patch(ApplyStrategy context, InitClass initClass) {
-          throw new Unreachable();
-        }
-
-        @Override
-        public void patch(ApplyStrategy context, Argument argument) {
-          throw new Unreachable();
-        }
-      };
-
-  public final AppView<AppInfoWithLiveness> appView;
-  public final DexItemFactory factory;
-  public final Kotlin kotlin;
-
-  // Defines a factory providing a strategy for a lambda type, returns
-  // NoOp strategy if the type is not a lambda.
-  private final Function<DexType, Strategy> strategyProvider;
-
-  // Visitor for lambda type references seen in unexpected places. Either
-  // invalidates the lambda or asserts depending on the processing phase.
-  private final LambdaTypeVisitor lambdaChecker;
-
-  // Specify the context of the current instruction: method/code/blocks/instructions.
-  public final ProgramMethod method;
-  public final IRCode code;
-  public final ListIterator<BasicBlock> blocks;
-  private InstructionListIterator instructions;
-
-  // The inlining context (caller), if any.
-  private final ProgramMethod context;
-
-  CodeProcessor(
-      AppView<AppInfoWithLiveness> appView,
-      Function<DexType, Strategy> strategyProvider,
-      LambdaTypeVisitor lambdaChecker,
-      ProgramMethod method,
-      IRCode code) {
-    this(appView, strategyProvider, lambdaChecker, method, code, null);
-  }
-
-  CodeProcessor(
-      AppView<AppInfoWithLiveness> appView,
-      Function<DexType, Strategy> strategyProvider,
-      LambdaTypeVisitor lambdaChecker,
-      ProgramMethod method,
-      IRCode code,
-      ProgramMethod context) {
-    this.appView = appView;
-    this.strategyProvider = strategyProvider;
-    this.factory = appView.dexItemFactory();
-    this.kotlin = factory.kotlin;
-    this.lambdaChecker = lambdaChecker;
-    this.method = method;
-    this.code = code;
-    this.blocks = code.listIterator();
-    this.context = context;
-  }
-
-  public final InstructionListIterator instructions() {
-    assert instructions != null;
-    return instructions;
-  }
-
-  void processCode() {
-    while (blocks.hasNext()) {
-      BasicBlock block = blocks.next();
-      instructions = block.listIterator(code);
-      while (instructions.hasNext()) {
-        instructions.next().accept(this);
-      }
-    }
-  }
-
-  private boolean shouldRewrite(DexField field) {
-    return shouldRewrite(field.holder);
-  }
-
-  private boolean shouldRewrite(DexMethod method) {
-    return shouldRewrite(method.holder);
-  }
-
-  private boolean shouldRewrite(DexType type) {
-    // Rewrite references to lambda classes if we are outside the class.
-    return type != (context != null ? context : method).getHolderType();
-  }
-
-  @Override
-  public Void handleInvoke(Invoke invoke) {
-    if (invoke.isInvokeNewArray()) {
-      lambdaChecker.accept(invoke.asInvokeNewArray().getReturnType());
-      return null;
-    }
-    if (invoke.isInvokeMultiNewArray()) {
-      lambdaChecker.accept(invoke.asInvokeMultiNewArray().getReturnType());
-      return null;
-    }
-    if (invoke.isInvokeCustom()) {
-      lambdaChecker.accept(invoke.asInvokeCustom().getCallSite());
-      return null;
-    }
-
-    InvokeMethod invokeMethod = invoke.asInvokeMethod();
-    Strategy strategy = strategyProvider.apply(invokeMethod.getInvokedMethod().holder);
-    if (strategy.isValidInvoke(this, invokeMethod)) {
-      // Invalidate signature, there still should not be lambda references.
-      lambdaChecker.accept(invokeMethod.getInvokedMethod().proto);
-      // Only rewrite references to lambda classes if we are outside the class.
-      if (shouldRewrite(invokeMethod.getInvokedMethod())) {
-        process(strategy, invokeMethod);
-      }
-      return null;
-    }
-
-    // For the rest invalidate any references.
-    if (invoke.isInvokePolymorphic()) {
-      lambdaChecker.accept(invoke.asInvokePolymorphic().getProto());
-    }
-    lambdaChecker.accept(invokeMethod.getInvokedMethod(), null);
-    return null;
-  }
-
-  @Override
-  public Void visit(NewInstance newInstance) {
-    Strategy strategy = strategyProvider.apply(newInstance.clazz);
-    if (strategy.isValidNewInstance(this, newInstance)) {
-      // Only rewrite references to lambda classes if we are outside the class.
-      if (shouldRewrite(newInstance.clazz)) {
-        process(strategy, newInstance);
-      }
-    }
-    return null;
-  }
-
-  @Override
-  public Void visit(CheckCast checkCast) {
-    lambdaChecker.accept(checkCast.getType());
-    return null;
-  }
-
-  @Override
-  public Void visit(NewArrayEmpty newArrayEmpty) {
-    lambdaChecker.accept(newArrayEmpty.type);
-    return null;
-  }
-
-  @Override
-  public Void visit(ConstClass constClass) {
-    lambdaChecker.accept(constClass.getValue());
-    return null;
-  }
-
-  @Override
-  public Void visit(ConstMethodType constMethodType) {
-    lambdaChecker.accept(constMethodType.getValue());
-    return null;
-  }
-
-  @Override
-  public Void visit(ConstMethodHandle constMethodHandle) {
-    lambdaChecker.accept(constMethodHandle.getValue());
-    return null;
-  }
-
-  @Override
-  public Void visit(InstanceGet instanceGet) {
-    DexField field = instanceGet.getField();
-    Strategy strategy = strategyProvider.apply(field.holder);
-    if (strategy.isValidInstanceFieldRead(this, field)) {
-      if (shouldRewrite(field)) {
-        // Only rewrite references to lambda classes if we are outside the class.
-        process(strategy, instanceGet);
-      }
-    } else {
-      lambdaChecker.accept(field.type);
-    }
-
-    // We avoid fields with type being lambda class, it is possible for
-    // a lambda to capture another lambda, but we don't support it for now.
-    lambdaChecker.accept(field.type);
-    return null;
-  }
-
-  @Override
-  public Void visit(InstancePut instancePut) {
-    DexField field = instancePut.getField();
-    Strategy strategy = strategyProvider.apply(field.holder);
-    if (strategy.isValidInstanceFieldWrite(this, field)) {
-      if (shouldRewrite(field)) {
-        // Only rewrite references to lambda classes if we are outside the class.
-        process(strategy, instancePut);
-      }
-    } else {
-      lambdaChecker.accept(field.type);
-    }
-
-    // We avoid fields with type being lambda class, it is possible for
-    // a lambda to capture another lambda, but we don't support it for now.
-    lambdaChecker.accept(field.type);
-    return null;
-  }
-
-  @Override
-  public Void visit(StaticGet staticGet) {
-    DexField field = staticGet.getField();
-    Strategy strategy = strategyProvider.apply(field.holder);
-    if (strategy.isValidStaticFieldRead(this, field)) {
-      if (shouldRewrite(field)) {
-        // Only rewrite references to lambda classes if we are outside the class.
-        process(strategy, staticGet);
-      }
-    } else {
-      lambdaChecker.accept(field.type);
-      lambdaChecker.accept(field.holder);
-    }
-    return null;
-  }
-
-  @Override
-  public Void visit(StaticPut staticPut) {
-    DexField field = staticPut.getField();
-    Strategy strategy = strategyProvider.apply(field.holder);
-    if (strategy.isValidStaticFieldWrite(this, field)) {
-      if (shouldRewrite(field)) {
-        // Only rewrite references to lambda classes if we are outside the class.
-        process(strategy, staticPut);
-      }
-    } else {
-      lambdaChecker.accept(field.type);
-      lambdaChecker.accept(field.holder);
-    }
-    return null;
-  }
-
-  @Override
-  public Void visit(InitClass initClass) {
-    DexType clazz = initClass.getClassValue();
-    Strategy strategy = strategyProvider.apply(clazz);
-    if (strategy.isValidInitClass(this, clazz)) {
-      if (shouldRewrite(clazz)) {
-        // Only rewrite references to lambda classes if we are outside the class.
-        process(strategy, initClass);
-      }
-    } else {
-      lambdaChecker.accept(clazz);
-    }
-    return null;
-  }
-
-  @Override
-  public Void visit(Argument instruction) {
-    if (instruction.outValue() != code.getThis()) {
-      return null;
-    }
-    Strategy strategy = strategyProvider.apply(method.getHolderType());
-    if (strategy.isValidHolder(this, method.getHolderType())) {
-      if (shouldRewrite(method.getHolderType())) {
-        process(strategy, instruction);
-      }
-    }
-    return null;
-  }
-
-  abstract void process(Strategy strategy, InvokeMethod invokeMethod);
-
-  abstract void process(Strategy strategy, NewInstance newInstance);
-
-  abstract void process(Strategy strategy, InstancePut instancePut);
-
-  abstract void process(Strategy strategy, InstanceGet instanceGet);
-
-  abstract void process(Strategy strategy, StaticPut staticPut);
-
-  abstract void process(Strategy strategy, StaticGet staticGet);
-
-  abstract void process(Strategy strategy, InitClass initClass);
-
-  abstract void process(Strategy strategy, Argument argument);
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java
deleted file mode 100644
index cc01f83..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java
+++ /dev/null
@@ -1,237 +0,0 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.optimize.lambda;
-
-import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
-import com.android.tools.r8.ir.optimize.lambda.CodeProcessor.Strategy;
-import com.android.tools.r8.kotlin.Kotlin;
-import com.android.tools.r8.shaking.MainDexClasses;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.ThrowingConsumer;
-import com.google.common.collect.Lists;
-import com.google.common.io.BaseEncoding;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.ObjectOutputStream;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.function.Consumer;
-import java.util.function.Predicate;
-
-// Represents a group of lambda classes which potentially can be represented
-// by the same lambda _group_ class. Each lambda class inside the group is
-// assigned an integer id.
-//
-// NOTE: access to lambdas in lambda group is NOT thread-safe.
-public abstract class LambdaGroup {
-  public final LambdaGroupId id;
-
-  // Lambda group class name. Is intended to be stable and uniques.
-  // In current implementation is generated in following way:
-  //       <optional-package>.-$$LambdaGroup$<HASH>
-  // with HASH generated based on names of the lambda class names
-  // of lambdas included in the group.
-  private DexType classType;
-
-  // Maps lambda classes belonging to the group into the index inside the
-  // group. Note usage of linked hash map to keep insertion ordering stable.
-  private final Map<DexType, LambdaInfo> lambdas = new LinkedHashMap<>();
-
-  public static class LambdaInfo {
-    public int id;
-    public final DexProgramClass clazz;
-
-    LambdaInfo(int id, DexProgramClass clazz) {
-      this.id = id;
-      this.clazz = clazz;
-    }
-
-    public DexProgramClass getLambdaClass() {
-      return clazz;
-    }
-  }
-
-  public LambdaGroup(LambdaGroupId id) {
-    this.id = id;
-  }
-
-  public final DexType getGroupClassType() {
-    assert classType != null;
-    return classType;
-  }
-
-  public final int size() {
-    return lambdas.size();
-  }
-
-  public final void forEachLambda(Consumer<LambdaInfo> action) {
-    assert verifyLambdaIds(false);
-    for (LambdaInfo info : lambdas.values()) {
-      action.accept(info);
-    }
-  }
-
-  public final boolean anyLambda(Predicate<LambdaInfo> predicate) {
-    assert verifyLambdaIds(false);
-    for (LambdaInfo info : lambdas.values()) {
-      if (predicate.test(info)) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  final boolean shouldAddToMainDex(AppView<?> appView) {
-    // We add the group class to main index if any of the
-    // lambda classes it replaces is added to main index.
-    MainDexClasses mainDexClasses = appView.appInfo().getMainDexClasses();
-    for (LambdaInfo info : lambdas.values()) {
-      if (mainDexClasses.contains(info.getLambdaClass())) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  public final boolean containsLambda(DexType lambda) {
-    return lambdas.containsKey(lambda);
-  }
-
-  public final int lambdaId(DexType lambda) {
-    assert lambdas.containsKey(lambda);
-    return lambdas.get(lambda).id;
-  }
-
-  protected final List<DexEncodedField> lambdaCaptureFields(DexType lambda) {
-    assert lambdas.containsKey(lambda);
-    return lambdas.get(lambda).clazz.instanceFields();
-  }
-
-  protected final DexEncodedField lambdaSingletonField(DexType lambda) {
-    assert lambdas.containsKey(lambda);
-    List<DexEncodedField> fields = lambdas.get(lambda).clazz.staticFields();
-    assert fields.size() < 2;
-    return fields.size() == 0 ? null : fields.get(0);
-  }
-
-  // Contains less than 2 elements?
-  final boolean isTrivial() {
-    return lambdas.size() < 2;
-  }
-
-  final void add(DexProgramClass lambda) {
-    assert !lambdas.containsKey(lambda.type);
-    lambdas.put(lambda.type, new LambdaInfo(lambdas.size(), lambda));
-  }
-
-  final void remove(DexType lambda) {
-    assert lambdas.containsKey(lambda);
-    lambdas.remove(lambda);
-  }
-
-  final void compact() {
-    assert verifyLambdaIds(false);
-    int lastUsed = -1;
-    int lastSeen = -1;
-    for (Entry<DexType, LambdaInfo> entry : lambdas.entrySet()) {
-      int index = entry.getValue().id;
-      assert lastUsed <= lastSeen && lastSeen < index;
-      lastUsed++;
-      lastSeen = index;
-      if (lastUsed < index) {
-        entry.getValue().id = lastUsed;
-      }
-    }
-    assert verifyLambdaIds(true);
-  }
-
-  public abstract Strategy getCodeStrategy();
-
-  public abstract ThrowingConsumer<DexClass, LambdaStructureError> lambdaClassValidator(
-      Kotlin kotlin, AppInfoWithClassHierarchy appInfo);
-
-  // Package for a lambda group class to be created in.
-  protected abstract String getTypePackage();
-
-  protected abstract String getGroupSuffix();
-
-  final DexProgramClass synthesizeClass(
-      AppView<? extends AppInfoWithClassHierarchy> appView, OptimizationFeedback feedback) {
-    assert classType == null;
-    assert verifyLambdaIds(true);
-    List<LambdaInfo> lambdas = Lists.newArrayList(this.lambdas.values());
-    classType =
-        appView
-            .dexItemFactory()
-            .createType(
-                "L"
-                    + getTypePackage()
-                    + "-$$LambdaGroup$"
-                    + getGroupSuffix()
-                    + createHash(lambdas)
-                    + ";");
-    return getBuilder(appView.dexItemFactory(), appView.options())
-        .synthesizeClass(appView, feedback);
-  }
-
-  protected abstract LambdaGroupClassBuilder<? extends LambdaGroup> getBuilder(
-      DexItemFactory factory, InternalOptions options);
-
-  private String createHash(List<LambdaInfo> lambdas) {
-    try {
-      ByteArrayOutputStream bytes = new ByteArrayOutputStream();
-      ObjectOutputStream out = new ObjectOutputStream(bytes);
-
-      // We will generate SHA-1 hash of the list of lambda classes represented in the group.
-      for (LambdaInfo lambda : lambdas) {
-        DexString descriptor = lambda.clazz.type.descriptor;
-        out.writeInt(descriptor.size); // To avoid same-prefix problem
-        out.write(descriptor.content);
-      }
-      out.close();
-
-      MessageDigest digest = MessageDigest.getInstance("SHA-1");
-      digest.update(bytes.toByteArray());
-      return BaseEncoding.base64Url().omitPadding().encode(digest.digest());
-    } catch (NoSuchAlgorithmException | IOException ex) {
-      throw new Unreachable("Cannot get SHA-1 message digest");
-    }
-  }
-
-  private boolean verifyLambdaIds(boolean strict) {
-    int previous = -1;
-    for (LambdaInfo info : lambdas.values()) {
-      assert strict ? (previous + 1) == info.id : previous < info.id;
-      previous = info.id;
-    }
-    return true;
-  }
-
-  public static class LambdaStructureError extends Exception {
-    final boolean reportable;
-
-    public LambdaStructureError(String cause) {
-      this(cause, true);
-    }
-
-    public LambdaStructureError(String cause, boolean reportable) {
-      super(cause);
-      this.reportable = reportable;
-    }
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java
deleted file mode 100644
index 92f0972..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.optimize.lambda;
-
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.ClassAccessFlags;
-import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexTypeList;
-import com.android.tools.r8.graph.EnclosingMethodAttribute;
-import com.android.tools.r8.graph.GenericSignature.ClassSignature;
-import com.android.tools.r8.graph.InnerClassAttribute;
-import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
-import com.android.tools.r8.origin.SynthesizedOrigin;
-import java.util.Collections;
-import java.util.List;
-
-// Encapsulates lambda group class building logic and separates
-// it from the rest of lambda group functionality.
-public abstract class LambdaGroupClassBuilder<T extends LambdaGroup> {
-  protected final T group;
-  protected final DexItemFactory factory;
-  protected final String origin;
-
-  protected LambdaGroupClassBuilder(T group, DexItemFactory factory, String origin) {
-    this.group = group;
-    this.factory = factory;
-    this.origin = origin;
-  }
-
-  public final DexProgramClass synthesizeClass(
-      AppView<? extends AppInfoWithClassHierarchy> appView, OptimizationFeedback feedback) {
-    DexType groupClassType = group.getGroupClassType();
-    DexType superClassType = getSuperClassType();
-    return new DexProgramClass(
-        groupClassType,
-        null,
-        new SynthesizedOrigin(origin, getClass()),
-        buildAccessFlags(),
-        superClassType,
-        buildInterfaces(),
-        factory.createString(origin),
-        null,
-        Collections.emptyList(),
-        buildEnclosingMethodAttribute(),
-        buildInnerClasses(),
-        buildClassSignature(),
-        DexAnnotationSet.empty(),
-        buildStaticFields(appView, feedback),
-        buildInstanceFields(),
-        buildDirectMethods(),
-        buildVirtualMethods(),
-        factory.getSkipNameValidationForTesting(),
-        // The name of the class is based on the hash of the content.
-        DexProgramClass::checksumFromType);
-  }
-
-  protected abstract DexType getSuperClassType();
-
-  protected abstract ClassAccessFlags buildAccessFlags();
-
-  protected abstract EnclosingMethodAttribute buildEnclosingMethodAttribute();
-
-  protected abstract List<InnerClassAttribute> buildInnerClasses();
-
-  protected abstract ClassSignature buildClassSignature();
-
-  protected abstract DexEncodedMethod[] buildVirtualMethods();
-
-  protected abstract DexEncodedMethod[] buildDirectMethods();
-
-  protected abstract DexEncodedField[] buildInstanceFields();
-
-  protected abstract DexEncodedField[] buildStaticFields(
-      AppView<? extends AppInfoWithClassHierarchy> appView, OptimizationFeedback feedback);
-
-  protected abstract DexTypeList buildInterfaces();
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupId.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupId.java
deleted file mode 100644
index 2c31541..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupId.java
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.optimize.lambda;
-
-// Represents a lambda group identifier uniquely identifying the groups
-// of potentially mergeable lambdas.
-//
-// Implements hashCode/equals in a way that guarantees that if two lambda
-// classes has equal ids they belong to the same lambda group.
-public interface LambdaGroupId {
-  LambdaGroup createGroup();
-
-  @Override
-  int hashCode();
-
-  @Override
-  boolean equals(Object obj);
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
deleted file mode 100644
index 5bccf4d4..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
+++ /dev/null
@@ -1,738 +0,0 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.optimize.lambda;
-
-import com.android.tools.r8.DiagnosticsHandler;
-import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.graph.DexApplication.Builder;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult;
-import com.android.tools.r8.graph.classmerging.HorizontallyMergedLambdaClasses;
-import com.android.tools.r8.ir.analysis.type.DestructivePhiTypeUpdater;
-import com.android.tools.r8.ir.code.Argument;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.InitClass;
-import com.android.tools.r8.ir.code.InstanceGet;
-import com.android.tools.r8.ir.code.InstancePut;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.InvokeVirtual;
-import com.android.tools.r8.ir.code.NewInstance;
-import com.android.tools.r8.ir.code.Phi;
-import com.android.tools.r8.ir.code.StaticGet;
-import com.android.tools.r8.ir.code.StaticPut;
-import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.conversion.IRConverter;
-import com.android.tools.r8.ir.conversion.MethodProcessor;
-import com.android.tools.r8.ir.optimize.Inliner;
-import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
-import com.android.tools.r8.ir.optimize.Inliner.InliningInfo;
-import com.android.tools.r8.ir.optimize.info.FieldOptimizationInfo;
-import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
-import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
-import com.android.tools.r8.ir.optimize.info.OptimizationFeedback.OptimizationInfoFixer;
-import com.android.tools.r8.ir.optimize.inliner.InliningIRProvider;
-import com.android.tools.r8.ir.optimize.lambda.CodeProcessor.Strategy;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroup.LambdaStructureError;
-import com.android.tools.r8.ir.optimize.lambda.kotlin.KotlinLambdaGroupIdFactory;
-import com.android.tools.r8.kotlin.Kotlin;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
-import com.android.tools.r8.shaking.ProguardConfiguration;
-import com.android.tools.r8.utils.SetUtils;
-import com.android.tools.r8.utils.StringDiagnostic;
-import com.android.tools.r8.utils.ThreadUtils;
-import com.android.tools.r8.utils.ThrowingConsumer;
-import com.android.tools.r8.utils.Timing;
-import com.android.tools.r8.utils.collections.LongLivedProgramMethodSetBuilder;
-import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
-import com.google.common.collect.BiMap;
-import com.google.common.collect.HashBiMap;
-import com.google.common.collect.Sets;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.Deque;
-import java.util.IdentityHashMap;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-import java.util.function.Function;
-
-// Merging lambda classes into single lambda group classes. There are three flavors
-// of lambdas we are dealing with:
-//   (a) lambda classes synthesized in desugaring, handles java lambdas
-//   (b) k-style lambda classes synthesized by kotlin compiler
-//   (c) j-style lambda classes synthesized by kotlin compiler
-//
-// Lambda merging is potentially applicable to all three of them, but
-// current implementation deals with both k- and j-style lambdas.
-//
-// In general we merge lambdas in 5 phases:
-//   1. collect all lambdas and compute group candidates. we do it synchronously
-//      and ensure that the order of lambda groups and lambdas inside each group
-//      is stable.
-//   2. analyze usages of lambdas and exclude lambdas with unexpected usage
-//      NOTE: currently we consider *all* usages outside the code invalid
-//      so we only need to patch method code when replacing the lambda class.
-//   3. exclude (invalidate) all lambda classes with usages we don't understand
-//      or support, compact the remaining lambda groups, remove trivial groups
-//      with less that 2 lambdas.
-//   4. replace lambda valid/supported class constructions with references to
-//      lambda group classes.
-//   5. synthesize group lambda classes.
-//
-public final class LambdaMerger {
-
-  private abstract static class Mode {
-
-    void rewriteCode(
-        ProgramMethod method,
-        IRCode code,
-        Inliner inliner,
-        ProgramMethod context,
-        InliningIRProvider provider) {}
-
-    void analyzeCode(ProgramMethod method, IRCode code) {}
-  }
-
-  private class AnalyzeMode extends Mode {
-
-    @Override
-    void analyzeCode(ProgramMethod method, IRCode code) {
-      new AnalysisStrategy(method, code).processCode();
-    }
-  }
-
-  private class ApplyMode extends Mode {
-
-    private final Map<DexProgramClass, LambdaGroup> lambdaGroups;
-    private final LambdaMergerOptimizationInfoFixer optimizationInfoFixer;
-
-    ApplyMode(
-        Map<DexProgramClass, LambdaGroup> lambdaGroups,
-        LambdaMergerOptimizationInfoFixer optimizationInfoFixer) {
-      this.lambdaGroups = lambdaGroups;
-      this.optimizationInfoFixer = optimizationInfoFixer;
-    }
-
-    @Override
-    void rewriteCode(
-        ProgramMethod method,
-        IRCode code,
-        Inliner inliner,
-        ProgramMethod context,
-        InliningIRProvider provider) {
-      LambdaGroup lambdaGroup = lambdaGroups.get(method.getHolder());
-      if (lambdaGroup == null) {
-        // Only rewrite the methods that have not been synthesized for the lambda group classes.
-        new ApplyStrategy(method, code, context, optimizationInfoFixer).processCode();
-        return;
-      }
-
-      if (method.getDefinition().isInitializer()) {
-        // Should not require rewriting.
-        return;
-      }
-
-      assert method.getDefinition().isNonPrivateVirtualMethod();
-      assert context == null;
-
-      Map<InvokeVirtual, InliningInfo> invokesToInline = new IdentityHashMap<>();
-      for (InvokeVirtual invoke : code.<InvokeVirtual>instructions(Instruction::isInvokeVirtual)) {
-        DexMethod invokedMethod = invoke.getInvokedMethod();
-        DexType holder = invokedMethod.holder;
-        if (lambdaGroup.containsLambda(holder)) {
-          // TODO(b/150685763): Check if we can use simpler lookup.
-          ResolutionResult resolution = appView.appInfo().resolveMethodOnClass(invokedMethod);
-          assert resolution.isSingleResolution();
-          ProgramMethod singleTarget =
-              resolution.asSingleResolution().getResolutionPair().asProgramMethod();
-          assert singleTarget != null;
-          invokesToInline.put(invoke, new InliningInfo(singleTarget, singleTarget.getHolderType()));
-        }
-      }
-
-      assert invokesToInline.size() > 1
-          || appView.options().testing.verificationSizeLimitInBytesOverride > -1;
-
-      inliner.performForcedInlining(method, code, invokesToInline, provider, Timing.empty());
-    }
-  }
-
-  // Maps lambda into a group, only contains lambdas we decided to merge.
-  // NOTE: needs synchronization.
-  private final Map<DexType, LambdaGroup> lambdas = new IdentityHashMap<>();
-  // We use linked map to ensure stable ordering of the groups
-  // when they are processed sequentially.
-  // NOTE: needs synchronization.
-  private final Map<LambdaGroupId, LambdaGroup> groups = new LinkedHashMap<>();
-
-  // Since invalidating lambdas may happen concurrently we don't remove invalidated lambdas
-  // from groups (and `lambdas`) right away since the ordering may be important. Instead we
-  // collect invalidated lambdas and remove them from groups after analysis is done.
-  private final Set<DexType> invalidatedLambdas = Sets.newConcurrentHashSet();
-
-  // Methods which need to be patched to reference lambda group classes instead of the
-  // original lambda classes. The number of methods is expected to be small since there
-  // is a 1:1 relation between lambda and method it is defined in (unless such a method
-  // was inlined by either kotlinc or r8).
-  //
-  // Note that we don't track precisely lambda -> method mapping, so it may happen that
-  // we mark a method for further processing, and then invalidate the only lambda referenced
-  // from it. In this case we will reprocess method that does not need patching, but it
-  // should not be happening very frequently and we ignore possible overhead.
-  private final LongLivedProgramMethodSetBuilder<SortedProgramMethodSet> methodsToReprocess =
-      LongLivedProgramMethodSetBuilder.createForSortedSet();
-
-  private final AppView<AppInfoWithLiveness> appView;
-  private final Kotlin kotlin;
-  private final DiagnosticsHandler reporter;
-
-  private Mode mode;
-
-  // Lambda visitor invalidating lambdas it sees.
-  private final LambdaTypeVisitor lambdaInvalidator;
-  // Lambda visitor throwing Unreachable on each lambdas it sees.
-  private final LambdaTypeVisitor lambdaChecker;
-
-  public LambdaMerger(AppView<AppInfoWithLiveness> appView) {
-    DexItemFactory factory = appView.dexItemFactory();
-    this.appView = appView;
-    this.kotlin = factory.kotlin;
-    this.reporter = appView.options().reporter;
-
-    this.lambdaInvalidator =
-        new LambdaTypeVisitor(factory, this::isMergeableLambda, this::invalidateLambda);
-    this.lambdaChecker =
-        new LambdaTypeVisitor(
-            factory,
-            this::isMergeableLambda,
-            type -> {
-              throw new Unreachable("Unexpected lambda " + type.toSourceString());
-            });
-  }
-
-  private void invalidateLambda(DexType lambda) {
-    invalidatedLambdas.add(lambda);
-  }
-
-  private synchronized boolean isMergeableLambda(DexType lambda) {
-    return lambdas.containsKey(lambda);
-  }
-
-  private synchronized LambdaGroup getLambdaGroup(DexType lambda) {
-    return lambdas.get(lambda);
-  }
-
-  private synchronized void queueForProcessing(ProgramMethod method) {
-    methodsToReprocess.add(method);
-  }
-
-  // Collect all group candidates and assign unique lambda ids inside each group.
-  // We do this before methods are being processed to guarantee stable order of
-  // lambdas inside each group.
-  public final void collectGroupCandidates(DexApplication app) {
-    // Collect lambda groups.
-    app.classes().stream()
-        .filter(cls -> !appView.appInfo().isPinned(cls.type))
-        .filter(
-            cls ->
-                appView.testing().kotlinLambdaMergerFactoryForClass.apply(cls) != null
-                    && KotlinLambdaGroupIdFactory.hasValidAnnotations(kotlin, cls)
-                    && !appView.appInfo().getClassToFeatureSplitMap().isInFeature(cls))
-        .sorted(Comparator.comparing(DexClass::getType)) // Ensure stable ordering.
-        .forEachOrdered(
-            lambda -> {
-              try {
-                KotlinLambdaGroupIdFactory lambdaGroupIdFactory =
-                    appView.testing().kotlinLambdaMergerFactoryForClass.apply(lambda);
-                LambdaGroupId id = lambdaGroupIdFactory.validateAndCreate(appView, kotlin, lambda);
-                LambdaGroup group = groups.computeIfAbsent(id, LambdaGroupId::createGroup);
-                group.add(lambda);
-                lambdas.put(lambda.type, group);
-              } catch (LambdaStructureError error) {
-                // Intentionally empty.
-              }
-            });
-
-    // Remove trivial groups.
-    removeTrivialLambdaGroups();
-
-    assert mode == null;
-    mode = new AnalyzeMode();
-  }
-
-  /**
-   * Is called by IRConverter::rewriteCode. Performs different actions depending on the current
-   * mode.
-   *
-   * <ol>
-   *   <li>in ANALYZE mode analyzes invalid usages of lambda classes inside the method code,
-   *       invalidated such lambda classes, collects methods that need to be patched.
-   *   <li>in APPLY mode does nothing.
-   * </ol>
-   */
-  public final void analyzeCode(ProgramMethod method, IRCode code) {
-    if (mode != null) {
-      mode.analyzeCode(method, code);
-    }
-  }
-
-  /**
-   * Is called by IRConverter::rewriteCode. Performs different actions depending on the current
-   * mode.
-   *
-   * <ol>
-   *   <li>in ANALYZE mode does nothing.
-   *   <li>in APPLY mode patches the code to use lambda group classes, also asserts that there are
-   *       no more invalid lambda class references.
-   * </ol>
-   */
-  public final void rewriteCode(
-      ProgramMethod method, IRCode code, Inliner inliner, MethodProcessor methodProcessor) {
-    if (mode != null) {
-      mode.rewriteCode(
-          code.context(),
-          code,
-          inliner,
-          null,
-          new InliningIRProvider(appView, method, code, methodProcessor));
-    }
-  }
-
-  /**
-   * Similar to {@link #rewriteCode(ProgramMethod, IRCode, Inliner, MethodProcessor)}, but for
-   * rewriting code for inlining. The {@param context} is the caller that {@param method} is being
-   * inlined into.
-   */
-  public final void rewriteCodeForInlining(
-      ProgramMethod method, IRCode code, ProgramMethod context, InliningIRProvider provider) {
-    if (mode != null) {
-      mode.rewriteCode(method, code, null, context, provider);
-    }
-  }
-
-  public final void applyLambdaClassMapping(
-      DexApplication app,
-      IRConverter converter,
-      OptimizationFeedback feedback,
-      Builder<?> builder,
-      ExecutorService executorService,
-      GraphLens appliedGraphLens)
-      throws ExecutionException {
-    if (lambdas.isEmpty()) {
-      appView.setHorizontallyMergedLambdaClasses(HorizontallyMergedLambdaClasses.empty());
-      return;
-    }
-
-    // Analyse references from program classes. We assume that this optimization
-    // is only used for full program analysis and there are no classpath classes.
-    ThreadUtils.processItems(app.classes(), this::analyzeClass, executorService);
-
-    // Analyse more complex aspects of lambda classes including method code.
-    analyzeLambdaClassesStructure(executorService);
-
-    // Remove invalidated lambdas, compact groups to ensure
-    // sequential lambda ids, create group lambda classes.
-    BiMap<LambdaGroup, DexProgramClass> lambdaGroupsClasses = finalizeLambdaGroups(feedback);
-
-    // Fixup optimization info to ensure that the optimization info does not refer to any merged
-    // lambdas.
-    LambdaMergerOptimizationInfoFixer optimizationInfoFixer =
-        new LambdaMergerOptimizationInfoFixer(lambdaGroupsClasses);
-    feedback.fixupOptimizationInfos(appView, executorService, optimizationInfoFixer);
-
-    // Switch to APPLY strategy.
-    this.mode = new ApplyMode(lambdaGroupsClasses.inverse(), optimizationInfoFixer);
-
-    FieldAccessInfoCollectionModifier.Builder fieldAccessInfoCollectionModifierBuilder =
-        FieldAccessInfoCollectionModifier.builder();
-
-    // Add synthesized lambda group classes to the builder.
-    for (Entry<LambdaGroup, DexProgramClass> entry : lambdaGroupsClasses.entrySet()) {
-      DexProgramClass synthesizedClass = entry.getValue();
-      appView
-          .appInfo()
-          .addSynthesizedClass(synthesizedClass, entry.getKey().shouldAddToMainDex(appView));
-      builder.addSynthesizedClass(synthesizedClass);
-
-      synthesizedClass.forEachField(
-          field ->
-              fieldAccessInfoCollectionModifierBuilder
-                  .recordFieldReadInUnknownContext(field.getReference())
-                  .recordFieldWriteInUnknownContext(field.getReference()));
-
-      // Eventually, we need to process synthesized methods in the lambda group.
-      // Otherwise, abstract SynthesizedCode will be flown to Enqueuer.
-      // But that process should not see the holder. Otherwise, lambda calls in the main dispatch
-      // method became recursive calls via the lens rewriter. They should remain, then inliner
-      // will inline methods from mergee lambdas to the main dispatch method.
-      // Then, there is a dilemma: other sub optimizations trigger subtype lookup that will throw
-      // NPE if it cannot find the holder for this synthesized lambda group.
-      // One hack here is to mark those methods `processed` so that the lens rewriter is skipped.
-      synthesizedClass.forEachMethod(
-          encodedMethod -> encodedMethod.markProcessed(ConstraintWithTarget.NEVER));
-    }
-
-    // Record field accesses for synthesized fields.
-    fieldAccessInfoCollectionModifierBuilder.build().modify(appView);
-
-    converter.optimizeSynthesizedClasses(lambdaGroupsClasses.values(), executorService);
-
-    // Rewrite lambda class references into lambda group class
-    // references inside methods from the processing queue.
-    rewriteLambdaReferences(converter, executorService, appliedGraphLens);
-    this.mode = null;
-
-    appView.setHorizontallyMergedLambdaClasses(new HorizontallyMergedLambdaClasses(lambdas));
-  }
-
-  private void analyzeLambdaClassesStructure(ExecutorService service) throws ExecutionException {
-    List<Future<?>> futures = new ArrayList<>();
-    for (LambdaGroup group : groups.values()) {
-      ThrowingConsumer<DexClass, LambdaStructureError> validator =
-          group.lambdaClassValidator(kotlin, appView.appInfo());
-      group.forEachLambda(
-          info ->
-              futures.add(
-                  service.submit(
-                      () -> {
-                        try {
-                          validator.accept(info.clazz);
-                        } catch (LambdaStructureError error) {
-                          ProguardConfiguration proguardConfiguration =
-                              appView.options().getProguardConfiguration();
-                          if (error.reportable
-                              && !proguardConfiguration
-                                  .getDontNotePatterns()
-                                  .matches(info.clazz.getType())) {
-                            reporter.info(
-                                new StringDiagnostic(
-                                    "Unexpected Kotlin lambda structure ["
-                                        + info.clazz.type.toSourceString()
-                                        + "]: "
-                                        + error.getMessage()));
-                          }
-                          invalidateLambda(info.clazz.type);
-                        }
-                      })));
-    }
-    ThreadUtils.awaitFutures(futures);
-  }
-
-  private BiMap<LambdaGroup, DexProgramClass> finalizeLambdaGroups(OptimizationFeedback feedback) {
-    for (DexType lambda : invalidatedLambdas) {
-      LambdaGroup group = lambdas.get(lambda);
-      assert group != null;
-      lambdas.remove(lambda);
-      group.remove(lambda);
-    }
-    invalidatedLambdas.clear();
-
-    // Remove new trivial lambdas.
-    removeTrivialLambdaGroups();
-
-    // Compact lambda groups, synthesize lambda group classes.
-    BiMap<LambdaGroup, DexProgramClass> result = HashBiMap.create();
-    for (LambdaGroup group : groups.values()) {
-      assert !group.isTrivial() : "No trivial group is expected here.";
-      group.compact();
-      DexProgramClass lambdaGroupClass = group.synthesizeClass(appView, feedback);
-      result.put(group, lambdaGroupClass);
-    }
-    return result;
-  }
-
-  private void removeTrivialLambdaGroups() {
-    Iterator<Entry<LambdaGroupId, LambdaGroup>> iterator = groups.entrySet().iterator();
-    while (iterator.hasNext()) {
-      Entry<LambdaGroupId, LambdaGroup> group = iterator.next();
-      if (group.getValue().isTrivial()) {
-        iterator.remove();
-        assert group.getValue().size() < 2;
-        group.getValue().forEachLambda(info -> this.lambdas.remove(info.clazz.type));
-      }
-    }
-  }
-
-  private void rewriteLambdaReferences(
-      IRConverter converter, ExecutorService executorService, GraphLens appliedGraphLens)
-      throws ExecutionException {
-    if (methodsToReprocess.isEmpty()) {
-      return;
-    }
-    SortedProgramMethodSet methods = methodsToReprocess.build(appView, appliedGraphLens);
-    converter.processMethodsConcurrently(methods, executorService);
-    assert methods.stream()
-        .map(DexClassAndMethod::getDefinition)
-        .allMatch(DexEncodedMethod::isProcessed);
-  }
-
-  private void analyzeClass(DexProgramClass clazz) {
-    lambdaInvalidator.accept(clazz.superType);
-    lambdaInvalidator.accept(clazz.interfaces);
-    lambdaInvalidator.accept(clazz.annotations());
-
-    for (DexEncodedField field : clazz.staticFields()) {
-      lambdaInvalidator.accept(field.annotations());
-      if (field.field.type != clazz.type) {
-        // Ignore static fields of the same type.
-        lambdaInvalidator.accept(field.field, clazz.type);
-      }
-    }
-    for (DexEncodedField field : clazz.instanceFields()) {
-      lambdaInvalidator.accept(field.annotations());
-      lambdaInvalidator.accept(field.field, clazz.type);
-    }
-
-    for (DexEncodedMethod method : clazz.methods()) {
-      lambdaInvalidator.accept(method.annotations());
-      lambdaInvalidator.accept(method.parameterAnnotationsList);
-      lambdaInvalidator.accept(method.method, clazz.type);
-    }
-  }
-
-  private Strategy strategyProvider(DexType type) {
-    LambdaGroup group = this.getLambdaGroup(type);
-    return group != null ? group.getCodeStrategy() : CodeProcessor.NoOp;
-  }
-
-  private final class AnalysisStrategy extends CodeProcessor {
-    private AnalysisStrategy(ProgramMethod method, IRCode code) {
-      super(
-          LambdaMerger.this.appView,
-          LambdaMerger.this::strategyProvider,
-          lambdaInvalidator,
-          method,
-          code);
-    }
-
-    @Override
-    void process(Strategy strategy, InvokeMethod invokeMethod) {
-      queueForProcessing(method);
-    }
-
-    @Override
-    void process(Strategy strategy, NewInstance newInstance) {
-      queueForProcessing(method);
-    }
-
-    @Override
-    void process(Strategy strategy, InstancePut instancePut) {
-      queueForProcessing(method);
-    }
-
-    @Override
-    void process(Strategy strategy, InstanceGet instanceGet) {
-      queueForProcessing(method);
-    }
-
-    @Override
-    void process(Strategy strategy, StaticPut staticPut) {
-      queueForProcessing(method);
-    }
-
-    @Override
-    void process(Strategy strategy, StaticGet staticGet) {
-      queueForProcessing(method);
-    }
-
-    @Override
-    void process(Strategy strategy, InitClass initClass) {
-      queueForProcessing(method);
-    }
-
-    @Override
-    void process(Strategy strategy, Argument argument) {
-      throw new Unreachable();
-    }
-  }
-
-  public final class ApplyStrategy extends CodeProcessor {
-
-    private final LambdaMergerOptimizationInfoFixer optimizationInfoFixer;
-
-    private final Set<Value> typeAffectedValues = Sets.newIdentityHashSet();
-
-    private ApplyStrategy(
-        ProgramMethod method,
-        IRCode code,
-        ProgramMethod context,
-        LambdaMergerOptimizationInfoFixer optimizationInfoFixer) {
-      super(
-          LambdaMerger.this.appView,
-          LambdaMerger.this::strategyProvider,
-          lambdaChecker,
-          method,
-          code,
-          context);
-      this.optimizationInfoFixer = optimizationInfoFixer;
-    }
-
-    public void recordTypeHasChanged(Value value) {
-      for (Value affectedValue : value.affectedValues()) {
-        if (typeMayHaveChanged(affectedValue)) {
-          typeAffectedValues.add(affectedValue);
-        }
-      }
-    }
-
-    @Override
-    void processCode() {
-      super.processCode();
-
-      if (typeAffectedValues.isEmpty()) {
-        return;
-      }
-
-      // Find all the transitively type affected values.
-      Set<Value> transitivelyTypeAffectedValues = SetUtils.newIdentityHashSet(typeAffectedValues);
-      Deque<Value> worklist = new ArrayDeque<>(typeAffectedValues);
-      while (!worklist.isEmpty()) {
-        Value value = worklist.pop();
-        assert typeMayHaveChanged(value);
-        assert transitivelyTypeAffectedValues.contains(value);
-
-        for (Value affectedValue : value.affectedValues()) {
-          if (typeMayHaveChanged(affectedValue)
-              && transitivelyTypeAffectedValues.add(affectedValue)) {
-            worklist.add(affectedValue);
-          }
-        }
-      }
-
-      // Update the types of these values if they refer to obsolete types. This is needed to be
-      // able to propagate the type information correctly, since lambda merging is neither a
-      // narrowing nor a widening.
-      for (Value value : transitivelyTypeAffectedValues) {
-        value.setType(value.getType().fixupClassTypeReferences(optimizationInfoFixer, appView));
-      }
-
-      // Filter out the type affected phis and destructively update the type of the phis. This is
-      // needed because narrowing does not work in presence of cyclic phis.
-      Set<Phi> typeAffectedPhis = Sets.newIdentityHashSet();
-      for (Value typeAffectedValue : transitivelyTypeAffectedValues) {
-        if (typeAffectedValue.isPhi()) {
-          typeAffectedPhis.add(typeAffectedValue.asPhi());
-        }
-      }
-      if (!typeAffectedPhis.isEmpty()) {
-        new DestructivePhiTypeUpdater(appView, optimizationInfoFixer)
-            .recomputeAndPropagateTypes(code, typeAffectedPhis);
-      }
-      assert code.verifyTypes(appView);
-    }
-
-    private boolean typeMayHaveChanged(Value value) {
-      return value.isPhi() || !value.definition.hasInvariantOutType();
-    }
-
-    @Override
-    void process(Strategy strategy, InvokeMethod invokeMethod) {
-      strategy.patch(this, invokeMethod);
-    }
-
-    @Override
-    void process(Strategy strategy, NewInstance newInstance) {
-      strategy.patch(this, newInstance);
-    }
-
-    @Override
-    void process(Strategy strategy, InstancePut instancePut) {
-      // Instance put should only appear in lambda class instance constructor,
-      // we should never get here since we never rewrite them.
-      throw new Unreachable();
-    }
-
-    @Override
-    void process(Strategy strategy, InstanceGet instanceGet) {
-      strategy.patch(this, instanceGet);
-    }
-
-    @Override
-    void process(Strategy strategy, StaticPut staticPut) {
-      // Static put should only appear in lambda class static initializer,
-      // we should never get here since we never rewrite them.
-      throw new Unreachable();
-    }
-
-    @Override
-    void process(Strategy strategy, StaticGet staticGet) {
-      strategy.patch(this, staticGet);
-    }
-
-    @Override
-    void process(Strategy strategy, InitClass initClass) {
-      strategy.patch(this, initClass);
-    }
-
-    @Override
-    void process(Strategy strategy, Argument argument) {
-      strategy.patch(this, argument);
-    }
-  }
-
-  private final class LambdaMergerOptimizationInfoFixer
-      implements Function<DexType, DexType>, OptimizationInfoFixer {
-
-    private final Map<LambdaGroup, DexProgramClass> lambdaGroupsClasses;
-
-    LambdaMergerOptimizationInfoFixer(Map<LambdaGroup, DexProgramClass> lambdaGroupsClasses) {
-      this.lambdaGroupsClasses = lambdaGroupsClasses;
-    }
-
-    @Override
-    public DexType apply(DexType type) {
-      LambdaGroup group = lambdas.get(type);
-      if (group != null) {
-        DexProgramClass clazz = lambdaGroupsClasses.get(group);
-        if (clazz != null) {
-          return clazz.type;
-        }
-      }
-      return type;
-    }
-
-    @Override
-    public void fixup(DexEncodedField field) {
-      FieldOptimizationInfo optimizationInfo = field.getOptimizationInfo();
-      if (optimizationInfo.isMutableFieldOptimizationInfo()) {
-        optimizationInfo.asMutableFieldOptimizationInfo().fixupClassTypeReferences(this, appView);
-      } else {
-        assert optimizationInfo.isDefaultFieldOptimizationInfo();
-      }
-    }
-
-    @Override
-    public void fixup(DexEncodedMethod method) {
-      MethodOptimizationInfo optimizationInfo = method.getOptimizationInfo();
-      if (optimizationInfo.isUpdatableMethodOptimizationInfo()) {
-        optimizationInfo
-            .asUpdatableMethodOptimizationInfo()
-            .fixupClassTypeReferences(this, appView);
-      } else {
-        assert optimizationInfo.isDefaultMethodOptimizationInfo();
-      }
-    }
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaTypeVisitor.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaTypeVisitor.java
deleted file mode 100644
index 9dce489..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaTypeVisitor.java
+++ /dev/null
@@ -1,146 +0,0 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.optimize.lambda;
-
-import com.android.tools.r8.graph.DexAnnotation;
-import com.android.tools.r8.graph.DexAnnotationElement;
-import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexCallSite;
-import com.android.tools.r8.graph.DexEncodedAnnotation;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexMethodHandle;
-import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexTypeList;
-import com.android.tools.r8.graph.DexValue;
-import com.android.tools.r8.graph.ParameterAnnotationsList;
-import java.util.function.Consumer;
-import java.util.function.Predicate;
-
-// Encapsulates the logic of visiting all lambda classes.
-final class LambdaTypeVisitor {
-  private final DexItemFactory factory;
-  private final Predicate<DexType> isLambdaType;
-  private final Consumer<DexType> onLambdaType;
-
-  LambdaTypeVisitor(DexItemFactory factory,
-      Predicate<DexType> isLambdaType, Consumer<DexType> onLambdaType) {
-    this.factory = factory;
-    this.isLambdaType = isLambdaType;
-    this.onLambdaType = onLambdaType;
-  }
-
-  void accept(DexCallSite callSite) {
-    accept(callSite.methodProto);
-    accept(callSite.bootstrapMethod);
-    for (DexValue value : callSite.bootstrapArgs) {
-      accept(value);
-    }
-  }
-
-  private void accept(DexValue value) {
-    switch (value.getValueKind()) {
-      case ARRAY:
-        for (DexValue elementValue : value.asDexValueArray().getValues()) {
-          accept(elementValue);
-        }
-        break;
-      case FIELD:
-        accept(value.asDexValueField().value, null);
-        break;
-      case METHOD:
-        accept(value.asDexValueMethod().value, null);
-        break;
-      case METHOD_HANDLE:
-        accept(value.asDexValueMethodHandle().value);
-        break;
-      case METHOD_TYPE:
-        accept(value.asDexValueMethodType().value);
-        break;
-      case TYPE:
-        accept(value.asDexValueType().value);
-        break;
-      default:
-        // Intentionally empty.
-    }
-  }
-
-  void accept(DexMethodHandle handle) {
-    if (handle.isFieldHandle()) {
-      accept(handle.asField(), null);
-    } else {
-      assert handle.isMethodHandle();
-      accept(handle.asMethod(), null);
-    }
-  }
-
-  void accept(DexField field, DexType holderToIgnore) {
-    accept(field.type);
-    if (holderToIgnore != field.holder) {
-      accept(field.holder);
-    }
-  }
-
-  void accept(DexMethod method, DexType holderToIgnore) {
-    if (holderToIgnore != method.holder) {
-      accept(method.holder);
-    }
-    accept(method.proto);
-  }
-
-  void accept(DexProto proto) {
-    accept(proto.returnType);
-    accept(proto.parameters);
-  }
-
-  void accept(DexTypeList types) {
-    for (DexType type : types.values) {
-      accept(type);
-    }
-  }
-
-  void accept(DexAnnotationSet annotationSet) {
-    for (DexAnnotation annotation : annotationSet.annotations) {
-      accept(annotation);
-    }
-  }
-
-  void accept(ParameterAnnotationsList parameterAnnotationsList) {
-    parameterAnnotationsList.forEachAnnotation(this::accept);
-  }
-
-  private void accept(DexAnnotation annotation) {
-    accept(annotation.annotation);
-  }
-
-  private void accept(DexEncodedAnnotation annotation) {
-    accept(annotation.type);
-    for (DexAnnotationElement element : annotation.elements) {
-      accept(element);
-    }
-  }
-
-  private void accept(DexAnnotationElement element) {
-    accept(element.value);
-  }
-
-  void accept(DexType type) {
-    if (type == null) {
-      return;
-    }
-    if (type.isPrimitiveType() || type.isVoidType() || type.isPrimitiveArrayType()) {
-      return;
-    }
-    if (type.isArrayType()) {
-      accept(type.toArrayElementType(factory));
-      return;
-    }
-    if (isLambdaType.test(type)) {
-      onLambdaType.accept(type);
-    }
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/ClassInitializerSourceCode.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/ClassInitializerSourceCode.java
deleted file mode 100644
index 0eec9af..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/ClassInitializerSourceCode.java
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.optimize.lambda.kotlin;
-
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexTypeList;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
-import com.android.tools.r8.ir.code.Invoke.Type;
-import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.code.ValueType;
-import com.android.tools.r8.ir.conversion.IRBuilder;
-import com.android.tools.r8.ir.synthetic.SyntheticSourceCode;
-import com.google.common.collect.Lists;
-import java.util.List;
-
-final class ClassInitializerSourceCode extends SyntheticSourceCode {
-  private final DexItemFactory factory;
-  private final KotlinLambdaGroup group;
-
-  ClassInitializerSourceCode(
-      DexMethod method, DexItemFactory factory, KotlinLambdaGroup group, Position callerPosition) {
-    super(null, method, callerPosition);
-    assert method.proto.returnType == factory.voidType;
-    assert method.proto.parameters == DexTypeList.empty();
-    this.factory = factory;
-    this.group = group;
-  }
-
-  @Override
-  protected void prepareInstructions() {
-    DexType groupClassType = group.getGroupClassType();
-    DexMethod lambdaConstructorMethod = factory.createMethod(groupClassType,
-        factory.createProto(factory.voidType, factory.intType), factory.constructorMethodName);
-
-    int instance = nextRegister(ValueType.OBJECT);
-    int lambdaId = nextRegister(ValueType.INT);
-    List<ValueType> argTypes = Lists.newArrayList(ValueType.OBJECT, ValueType.INT);
-    List<Integer> argRegisters = Lists.newArrayList(instance, lambdaId);
-
-    group.forEachLambda(
-        info -> {
-          DexType lambda = info.clazz.type;
-          if (group.isSingletonLambda(lambda)) {
-            int id = group.lambdaId(lambda);
-            add(builder -> builder.addNewInstance(instance, groupClassType));
-            add(builder -> builder.addConst(TypeElement.getInt(), lambdaId, id));
-            add(
-                builder ->
-                    builder.addInvoke(
-                        Type.DIRECT,
-                        lambdaConstructorMethod,
-                        lambdaConstructorMethod.proto,
-                        argTypes,
-                        argRegisters,
-                        false /* isInterface*/));
-            add(
-                builder ->
-                    builder.addStaticPut(instance, group.getSingletonInstanceField(factory, id)));
-          }
-        });
-
-    assert this.nextInstructionIndex() > 0 : "no single field initialized";
-    add(IRBuilder::addReturn);
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java
deleted file mode 100644
index 117ee3b..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java
+++ /dev/null
@@ -1,238 +0,0 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.optimize.lambda.kotlin;
-
-import com.android.tools.r8.code.ReturnVoid;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.EnclosingMethodAttribute;
-import com.android.tools.r8.graph.InnerClassAttribute;
-import com.android.tools.r8.ir.code.Invoke.Type;
-import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.code.ValueType;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroup;
-import com.android.tools.r8.ir.synthetic.SyntheticSourceCode;
-import com.android.tools.r8.kotlin.Kotlin;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.ThrowingConsumer;
-import com.google.common.collect.Lists;
-import java.util.List;
-import java.util.function.IntFunction;
-
-// Represents a j-style lambda group created to combine several lambda classes
-// generated by kotlin compiler for kotlin lambda expressions passed to java receivers,
-// like:
-//
-//      --- Java --------------------------------------------------------------
-//      public static void acceptString(Supplier<String> s) {
-//        // ...
-//      }
-//      -----------------------------------------------------------------------
-//      acceptString({ "A" })
-//      -----------------------------------------------------------------------
-//
-// Regular stateless j-style lambda class structure looks like below:
-// NOTE: stateless j-style lambdas do not always have INSTANCE field.
-//
-// -----------------------------------------------------------------------------------------------
-// signature <T:Ljava/lang/Object;>Ljava/lang/Object;
-//                    Ljava/util/function/Supplier<Ljava/lang/String;>;
-// final class lambdas/LambdasKt$foo$4 implements java/util/function/Supplier {
-//
-//     public synthetic bridge get()Ljava/lang/Object;
-//
-//     public final get()Ljava/lang/String;
-//       @Lorg/jetbrains/annotations/NotNull;() // invisible
-//
-//     <init>()V
-//
-//     public final static Llambdas/LambdasKt$foo$4; INSTANCE
-//
-//     static <clinit>()V
-//
-//     OUTERCLASS lambdas/LambdasKt foo (Ljava/lang/String;I)Lkotlin/jvm/functions/Function0;
-//     final static INNERCLASS lambdas/LambdasKt$foo$4 null null
-// }
-// -----------------------------------------------------------------------------------------------
-//
-// Regular stateful j-style lambda class structure looks like below:
-//
-// -----------------------------------------------------------------------------------------------
-// signature <T:Ljava/lang/Object;>
-//                Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/String;>;
-// declaration: lambdas/LambdasKt$foo$5<T> implements java.util.function.Supplier<java.lang.String>
-// final class lambdas/LambdasKt$foo$5 implements java/util/function/Supplier  {
-//
-//     public synthetic bridge get()Ljava/lang/Object;
-//
-//     public final get()Ljava/lang/String;
-//       @Lorg/jetbrains/annotations/NotNull;() // invisible
-//
-//     <init>(Ljava/lang/String;I)V
-//
-//     final synthetic Ljava/lang/String; $m
-//     final synthetic I $v
-//
-//     OUTERCLASS lambdas/LambdasKt foo (Ljava/lang/String;I)Lkotlin/jvm/functions/Function0;
-//     final static INNERCLASS lambdas/LambdasKt$foo$5 null null
-// }
-// -----------------------------------------------------------------------------------------------
-//
-// Key j-style lambda class details:
-//   - extends java.lang.Object
-//   - implements *any* functional interface (Kotlin does not seem to support scenarios when
-//     lambda can implement multiple interfaces).
-//   - lambda class is created as an anonymous inner class
-//   - lambda class carries generic signature and kotlin metadata attribute
-//   - class instance fields represent captured values and have an instance constructor
-//     with matching parameters initializing them (see the second class above)
-//   - stateless lambda *may* be implemented as a singleton with a static field storing the
-//     only instance and initialized in static class constructor (see the first class above)
-//   - main lambda method usually matches an exact lambda signature and may have
-//     generic signature attribute and nullability parameter annotations
-//   - optional bridge method created to satisfy interface implementation and
-//     forwarding call to lambda main method
-//
-final class JStyleLambdaGroup extends KotlinLambdaGroup {
-  private JStyleLambdaGroup(GroupId id) {
-    super(id);
-  }
-
-  @Override
-  protected ClassBuilder getBuilder(DexItemFactory factory, InternalOptions options) {
-    return new ClassBuilder(factory, options, "java-style lambda group");
-  }
-
-  @Override
-  public ThrowingConsumer<DexClass, LambdaStructureError> lambdaClassValidator(
-      Kotlin kotlin, AppInfoWithClassHierarchy appInfo) {
-    return new ClassValidator(kotlin, appInfo);
-  }
-
-  @Override
-  protected String getGroupSuffix() {
-    return "js$";
-  }
-
-  // Specialized group id.
-  final static class GroupId extends KotlinLambdaGroupId {
-    GroupId(
-        AppView<AppInfoWithLiveness> appView,
-        String capture,
-        DexType iface,
-        String pkg,
-        String signature,
-        DexEncodedMethod mainMethod,
-        InnerClassAttribute inner,
-        EnclosingMethodAttribute enclosing) {
-      super(appView, capture, iface, pkg, signature, mainMethod, inner, enclosing);
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-      return obj instanceof GroupId && computeEquals((KotlinLambdaGroupId) obj);
-    }
-
-    @Override
-    String getLambdaKindDescriptor() {
-      return "Kotlin j-style lambda group";
-    }
-
-    @Override
-    public LambdaGroup createGroup() {
-      return new JStyleLambdaGroup(this);
-    }
-  }
-
-  // Specialized class validator.
-  private class ClassValidator extends KotlinLambdaClassValidator {
-    ClassValidator(Kotlin kotlin, AppInfoWithClassHierarchy appInfo) {
-      super(kotlin, JStyleLambdaGroup.this, appInfo);
-    }
-
-    @Override
-    int getInstanceInitializerMaxSize(List<DexEncodedField> captures) {
-      return captures.size() + 2;
-    }
-
-    @Override
-    int validateInstanceInitializerEpilogue(
-        com.android.tools.r8.code.Instruction[] instructions, int index)
-        throws LambdaStructureError {
-      if (!(instructions[index] instanceof com.android.tools.r8.code.InvokeDirect
-              || instructions[index] instanceof com.android.tools.r8.code.InvokeDirectRange)
-          || instructions[index].getMethod() != kotlin.factory.objectMembers.constructor) {
-        throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
-      }
-      index++;
-      if (!(instructions[index] instanceof ReturnVoid)) {
-        throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
-      }
-      return index + 1;
-    }
-  }
-
-  // Specialized class builder.
-  private final class ClassBuilder extends KotlinLambdaGroupClassBuilder<JStyleLambdaGroup> {
-    ClassBuilder(DexItemFactory factory, InternalOptions options, String origin) {
-      super(JStyleLambdaGroup.this, factory, options, origin);
-    }
-
-    @Override
-    protected DexType getSuperClassType() {
-      return factory.objectType;
-    }
-
-    @Override
-    SyntheticSourceCode createInstanceInitializerSourceCode(
-        DexType groupClassType, DexMethod initializerMethod, Position callerPosition) {
-      return new InstanceInitializerSourceCode(
-          factory,
-          groupClassType,
-          group.getLambdaIdField(factory),
-          id -> group.getCaptureField(factory, id),
-          initializerMethod,
-          callerPosition);
-    }
-  }
-
-  // Specialized instance initializer code.
-  private static final class InstanceInitializerSourceCode
-      extends KotlinInstanceInitializerSourceCode {
-    private final DexMethod objectInitializer;
-
-    InstanceInitializerSourceCode(
-        DexItemFactory factory,
-        DexType lambdaGroupType,
-        DexField idField,
-        IntFunction<DexField> fieldGenerator,
-        DexMethod method,
-        Position callerPosition) {
-      super(lambdaGroupType, idField, fieldGenerator, method, callerPosition);
-      this.objectInitializer = factory.objectMembers.constructor;
-    }
-
-    @Override
-    void prepareSuperConstructorCall(int receiverRegister) {
-      add(
-          builder ->
-              builder.addInvoke(
-                  Type.DIRECT,
-                  objectInitializer,
-                  objectInitializer.proto,
-                  Lists.newArrayList(ValueType.OBJECT),
-                  Lists.newArrayList(receiverRegister),
-                  false /* isInterface */));
-    }
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroupIdFactory.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroupIdFactory.java
deleted file mode 100644
index df72677..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroupIdFactory.java
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.optimize.lambda.kotlin;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.ClassAccessFlags;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.InnerClassAttribute;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroup.LambdaStructureError;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroupId;
-import com.android.tools.r8.kotlin.Kotlin;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-
-public final class JStyleLambdaGroupIdFactory extends KotlinLambdaGroupIdFactory {
-  private static final JStyleLambdaGroupIdFactory INSTANCE = new JStyleLambdaGroupIdFactory();
-
-  private JStyleLambdaGroupIdFactory() {}
-
-  public static JStyleLambdaGroupIdFactory getInstance() {
-    return INSTANCE;
-  }
-
-  @Override
-  public LambdaGroupId validateAndCreate(
-      AppView<AppInfoWithLiveness> appView, Kotlin kotlin, DexClass lambda)
-      throws LambdaStructureError {
-    boolean accessRelaxed =
-        appView.options().getProguardConfiguration().isAccessModificationAllowed();
-
-    // Ignore ACC_SUPER.
-    ClassAccessFlags copy = lambda.accessFlags.copy();
-    copy.unsetSuper();
-    checkAccessFlags("class access flags", copy, PUBLIC_LAMBDA_CLASS_FLAGS, LAMBDA_CLASS_FLAGS);
-
-    // Class and interface.
-    validateSuperclass(kotlin, lambda);
-    DexType iface = validateInterfaces(kotlin, lambda);
-
-    validateStaticFields(kotlin, lambda);
-    String captureSignature = validateInstanceFields(lambda, accessRelaxed);
-    validateDirectMethods(lambda);
-    DexEncodedMethod mainMethod = validateVirtualMethods(lambda);
-    String genericSignature = validateAnnotations(appView, kotlin, lambda);
-    InnerClassAttribute innerClass = validateInnerClasses(lambda);
-
-    return new JStyleLambdaGroup.GroupId(
-        appView,
-        captureSignature,
-        iface,
-        accessRelaxed ? "" : lambda.type.getPackageDescriptor(),
-        genericSignature,
-        mainMethod,
-        innerClass,
-        lambda.getEnclosingMethodAttribute());
-  }
-
-  @Override
-  void validateSuperclass(Kotlin kotlin, DexClass lambda) throws LambdaStructureError {
-    if (lambda.superType != kotlin.factory.objectType) {
-      throw new LambdaStructureError("implements " + lambda.superType.toSourceString() +
-          " instead of java.lang.Object");
-    }
-  }
-
-  @Override
-  DexType validateInterfaces(Kotlin kotlin, DexClass lambda) throws LambdaStructureError {
-    if (lambda.interfaces.size() == 0) {
-      throw new LambdaStructureError("does not implement any interfaces");
-    }
-    if (lambda.interfaces.size() > 1) {
-      throw new LambdaStructureError(
-          "implements more than one interface: " + lambda.interfaces.size());
-    }
-
-    // We don't validate that the interface is actually a functional interface,
-    // since it may be desugared, or optimized in any other way which should not
-    // affect lambda class merging.
-    return lambda.interfaces.values[0];
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
deleted file mode 100644
index cfae611..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
+++ /dev/null
@@ -1,256 +0,0 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.optimize.lambda.kotlin;
-
-import com.android.tools.r8.code.Const16;
-import com.android.tools.r8.code.Const4;
-import com.android.tools.r8.code.ReturnVoid;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.EnclosingMethodAttribute;
-import com.android.tools.r8.graph.InnerClassAttribute;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
-import com.android.tools.r8.ir.code.Invoke.Type;
-import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.code.ValueType;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroup;
-import com.android.tools.r8.ir.synthetic.SyntheticSourceCode;
-import com.android.tools.r8.kotlin.Kotlin;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.ThrowingConsumer;
-import com.google.common.collect.Lists;
-import java.util.List;
-import java.util.function.IntFunction;
-
-// Represents a k-style lambda group created to combine several lambda classes
-// generated by kotlin compiler for regular kotlin lambda expressions, like:
-//
-//      -----------------------------------------------------------------------
-//      fun foo(m: String, v: Int): () -> String {
-//        val lambda: (String, Int) -> String = { s, i -> s.substring(i) }
-//        return { "$m: $v" }
-//      }
-//      -----------------------------------------------------------------------
-//
-// Regular stateless k-style lambda class structure looks like below:
-// NOTE: stateless k-style lambdas do not always have INSTANCE field.
-//
-// -----------------------------------------------------------------------------------------------
-// // signature Lkotlin/jvm/internal/Lambda;Lkotlin/jvm/functions/Function2<
-//                          Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;>;
-// final class lambdas/LambdasKt$foo$lambda1$1
-//                  extends kotlin/jvm/internal/Lambda
-//                  implements kotlin/jvm/functions/Function2  {
-//
-//     public synthetic bridge invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-//
-//     public final invoke(Ljava/lang/String;I)Ljava/lang/String;
-//       @Lorg/jetbrains/annotations/NotNull;() // invisible
-//         @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0
-//
-//     <init>()V
-//
-//     public final static Llambdas/LambdasKt$foo$lambda1$1; INSTANCE
-//
-//     static <clinit>()V
-//
-//     OUTERCLASS lambdas/LambdasKt foo (Ljava/lang/String;I)Lkotlin/jvm/functions/Function0;
-//     final static INNERCLASS lambdas/LambdasKt$foo$lambda1$1 null null
-// }
-// -----------------------------------------------------------------------------------------------
-//
-// Regular stateful k-style lambda class structure looks like below:
-//
-// -----------------------------------------------------------------------------------------------
-// // signature Lkotlin/jvm/internal/Lambda;Lkotlin/jvm/functions/Function0<Ljava/lang/String;>;
-// final class lambdas/LambdasKt$foo$1
-//                  extends kotlin/jvm/internal/Lambda
-//                  implements kotlin/jvm/functions/Function0  {
-//
-//     public synthetic bridge invoke()Ljava/lang/Object;
-//
-//     public final invoke()Ljava/lang/String;
-//       @Lorg/jetbrains/annotations/NotNull;() // invisible
-//
-//     <init>(Ljava/lang/String;I)V
-//
-//     final synthetic Ljava/lang/String; $m
-//     final synthetic I $v
-//
-//     OUTERCLASS lambdas/LambdasKt foo (Ljava/lang/String;I)Lkotlin/jvm/functions/Function0;
-//     final static INNERCLASS lambdas/LambdasKt$foo$1 null null
-// }
-// -----------------------------------------------------------------------------------------------
-//
-// Key k-style lambda class details:
-//   - extends kotlin.jvm.internal.Lambda
-//   - implements one of kotlin.jvm.functions.Function0..Function22, or FunctionN
-//     see: https://github.com/JetBrains/kotlin/blob/master/libraries/
-//                  stdlib/jvm/runtime/kotlin/jvm/functions/Functions.kt
-//     and: https://github.com/JetBrains/kotlin/blob/master/spec-docs/function-types.md
-//   - lambda class is created as an anonymous inner class
-//   - lambda class carries generic signature and kotlin metadata attribute
-//   - class instance fields represent captured values and have an instance constructor
-//     with matching parameters initializing them (see the second class above)
-//   - stateless lambda *may* be  implemented as a singleton with a static field storing the
-//     only instance and initialized in static class constructor (see the first class above)
-//   - main lambda method usually matches an exact lambda signature and may have
-//     generic signature attribute and nullability parameter annotations
-//   - optional bridge method created to satisfy interface implementation and
-//     forwarding call to lambda main method
-//
-final class KStyleLambdaGroup extends KotlinLambdaGroup {
-  private KStyleLambdaGroup(GroupId id) {
-    super(id);
-  }
-
-  @Override
-  protected ClassBuilder getBuilder(DexItemFactory factory, InternalOptions options) {
-    return new ClassBuilder(factory, options, "kotlin-style lambda group");
-  }
-
-  @Override
-  public ThrowingConsumer<DexClass, LambdaStructureError> lambdaClassValidator(
-      Kotlin kotlin, AppInfoWithClassHierarchy appInfo) {
-    return new ClassValidator(kotlin, appInfo);
-  }
-
-  @Override
-  protected String getGroupSuffix() {
-    return "ks$";
-  }
-
-  // Specialized group id.
-  final static class GroupId extends KotlinLambdaGroupId {
-    GroupId(
-        AppView<AppInfoWithLiveness> appView,
-        String capture,
-        DexType iface,
-        String pkg,
-        String signature,
-        DexEncodedMethod mainMethod,
-        InnerClassAttribute inner,
-        EnclosingMethodAttribute enclosing) {
-      super(appView, capture, iface, pkg, signature, mainMethod, inner, enclosing);
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-      return obj instanceof GroupId && computeEquals((KotlinLambdaGroupId) obj);
-    }
-
-    @Override
-    String getLambdaKindDescriptor() {
-      return "Kotlin k-style lambda group";
-    }
-
-    @Override
-    public LambdaGroup createGroup() {
-      return new KStyleLambdaGroup(this);
-    }
-  }
-
-  // Specialized class validator.
-  private final class ClassValidator extends KotlinLambdaClassValidator {
-    ClassValidator(Kotlin kotlin, AppInfoWithClassHierarchy appInfo) {
-      super(kotlin, KStyleLambdaGroup.this, appInfo);
-    }
-
-    @Override
-    int getInstanceInitializerMaxSize(List<DexEncodedField> captures) {
-      return captures.size() + 3;
-    }
-
-    @Override
-    int validateInstanceInitializerEpilogue(
-        com.android.tools.r8.code.Instruction[] instructions, int index)
-        throws LambdaStructureError {
-      if (!(instructions[index] instanceof Const4) &&
-          !(instructions[index] instanceof Const16)) {
-        throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
-      }
-      index++;
-      if (!(instructions[index] instanceof com.android.tools.r8.code.InvokeDirect
-              || instructions[index] instanceof com.android.tools.r8.code.InvokeDirectRange)
-          || instructions[index].getMethod() != kotlin.functional.lambdaInitializerMethod) {
-        throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
-      }
-      index++;
-      if (!(instructions[index] instanceof ReturnVoid)) {
-        throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
-      }
-      return index + 1;
-    }
-  }
-
-  // Specialized class builder.
-  private final class ClassBuilder extends KotlinLambdaGroupClassBuilder<KStyleLambdaGroup> {
-    ClassBuilder(DexItemFactory factory, InternalOptions options, String origin) {
-      super(KStyleLambdaGroup.this, factory, options, origin);
-    }
-
-    @Override
-    protected DexType getSuperClassType() {
-      return factory.kotlin.functional.lambdaType;
-    }
-
-    @Override
-    SyntheticSourceCode createInstanceInitializerSourceCode(
-        DexType groupClassType, DexMethod initializerMethod, Position callerPosition) {
-      return new InstanceInitializerSourceCode(
-          factory,
-          groupClassType,
-          group.getLambdaIdField(factory),
-          id -> group.getCaptureField(factory, id),
-          initializerMethod,
-          factory.kotlin.functional.getArity(id.iface),
-          callerPosition);
-    }
-  }
-
-  // Specialized instance initializer code.
-  private final static class InstanceInitializerSourceCode
-      extends KotlinInstanceInitializerSourceCode {
-    private final int arity;
-    private final DexMethod lambdaInitializer;
-
-    InstanceInitializerSourceCode(
-        DexItemFactory factory,
-        DexType lambdaGroupType,
-        DexField idField,
-        IntFunction<DexField> fieldGenerator,
-        DexMethod method,
-        int arity,
-        Position callerPosition) {
-      super(lambdaGroupType, idField, fieldGenerator, method, callerPosition);
-      this.arity = arity;
-      this.lambdaInitializer = factory.createMethod(factory.kotlin.functional.lambdaType,
-          factory.createProto(factory.voidType, factory.intType), factory.constructorMethodName);
-    }
-
-    @Override
-    void prepareSuperConstructorCall(int receiverRegister) {
-      int arityRegister = nextRegister(ValueType.INT);
-      add(builder -> builder.addConst(TypeElement.getInt(), arityRegister, arity));
-      add(
-          builder ->
-              builder.addInvoke(
-                  Type.DIRECT,
-                  lambdaInitializer,
-                  lambdaInitializer.proto,
-                  Lists.newArrayList(ValueType.OBJECT, ValueType.INT),
-                  Lists.newArrayList(receiverRegister, arityRegister),
-                  false /* isInterface */));
-    }
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroupIdFactory.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroupIdFactory.java
deleted file mode 100644
index cc212bf..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroupIdFactory.java
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.optimize.lambda.kotlin;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.ClassAccessFlags;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.InnerClassAttribute;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroup.LambdaStructureError;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroupId;
-import com.android.tools.r8.kotlin.Kotlin;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-
-public final class KStyleLambdaGroupIdFactory extends KotlinLambdaGroupIdFactory {
-  private static final KStyleLambdaGroupIdFactory INSTANCE = new KStyleLambdaGroupIdFactory();
-
-  private KStyleLambdaGroupIdFactory() {}
-
-  public static KStyleLambdaGroupIdFactory getInstance() {
-    return INSTANCE;
-  }
-
-  @Override
-  public LambdaGroupId validateAndCreate(
-      AppView<AppInfoWithLiveness> appView, Kotlin kotlin, DexClass lambda)
-      throws LambdaStructureError {
-    boolean accessRelaxed =
-        appView.options().getProguardConfiguration().isAccessModificationAllowed();
-
-    // Ignore ACC_SUPER.
-    ClassAccessFlags copy = lambda.accessFlags.copy();
-    copy.unsetSuper();
-    checkAccessFlags("class access flags", copy, PUBLIC_LAMBDA_CLASS_FLAGS, LAMBDA_CLASS_FLAGS);
-
-    // Class and interface.
-    validateSuperclass(kotlin, lambda);
-    DexType iface = validateInterfaces(kotlin, lambda);
-
-    validateStaticFields(kotlin, lambda);
-    String captureSignature = validateInstanceFields(lambda, accessRelaxed);
-    validateDirectMethods(lambda);
-    DexEncodedMethod mainMethod = validateVirtualMethods(lambda);
-    String genericSignature = validateAnnotations(appView, kotlin, lambda);
-    InnerClassAttribute innerClass = validateInnerClasses(lambda);
-
-    return new KStyleLambdaGroup.GroupId(
-        appView,
-        captureSignature,
-        iface,
-        accessRelaxed ? "" : lambda.type.getPackageDescriptor(),
-        genericSignature,
-        mainMethod,
-        innerClass,
-        lambda.getEnclosingMethodAttribute());
-  }
-
-  @Override
-  void validateSuperclass(Kotlin kotlin, DexClass lambda) throws LambdaStructureError {
-    if (lambda.superType != kotlin.functional.lambdaType) {
-      throw new LambdaStructureError("implements " + lambda.superType.toSourceString() +
-          " instead of kotlin.jvm.internal.Lambda");
-    }
-  }
-
-  @Override
-  DexType validateInterfaces(Kotlin kotlin, DexClass lambda) throws LambdaStructureError {
-    if (lambda.interfaces.size() == 0) {
-      throw new LambdaStructureError("does not implement any interfaces");
-    }
-    if (lambda.interfaces.size() > 1) {
-      throw new LambdaStructureError(
-          "implements more than one interface: " + lambda.interfaces.size());
-    }
-    DexType iface = lambda.interfaces.values[0];
-    if (!kotlin.functional.isFunctionInterface(iface)) {
-      throw new LambdaStructureError("implements " + iface.toSourceString() +
-          " instead of kotlin functional interface.");
-    }
-    return iface;
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinInstanceInitializerSourceCode.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinInstanceInitializerSourceCode.java
deleted file mode 100644
index 687b081..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinInstanceInitializerSourceCode.java
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.optimize.lambda.kotlin;
-
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.conversion.IRBuilder;
-import com.android.tools.r8.ir.synthetic.SyntheticSourceCode;
-import java.util.function.IntFunction;
-
-abstract class KotlinInstanceInitializerSourceCode extends SyntheticSourceCode {
-  private final DexField idField;
-  private final IntFunction<DexField> fieldGenerator;
-
-  KotlinInstanceInitializerSourceCode(
-      DexType lambdaGroupType,
-      DexField idField,
-      IntFunction<DexField> fieldGenerator,
-      DexMethod method,
-      Position callerPosition) {
-    super(lambdaGroupType, method, callerPosition);
-    this.idField = idField;
-    this.fieldGenerator = fieldGenerator;
-  }
-
-  @Override
-  protected void prepareInstructions() {
-    int receiverRegister = getReceiverRegister();
-
-    // Initialize lambda id field.
-    add(builder -> builder.addInstancePut(getParamRegister(0), receiverRegister, idField));
-
-    // Initialize capture values.
-    DexType[] values = proto.parameters.values;
-    for (int i = 1; i < values.length; i++) {
-      int index = i;
-      add(builder -> builder.addInstancePut(
-          getParamRegister(index), receiverRegister, fieldGenerator.apply(index - 1)));
-    }
-
-    // Call superclass constructor.
-    prepareSuperConstructorCall(receiverRegister);
-
-    add(IRBuilder::addReturn);
-  }
-
-  abstract void prepareSuperConstructorCall(int receiverRegister);
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaClassValidator.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaClassValidator.java
deleted file mode 100644
index a956f84..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaClassValidator.java
+++ /dev/null
@@ -1,267 +0,0 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.optimize.lambda.kotlin;
-
-import com.android.tools.r8.code.Format22c;
-import com.android.tools.r8.code.Instruction;
-import com.android.tools.r8.code.Iput;
-import com.android.tools.r8.code.IputBoolean;
-import com.android.tools.r8.code.IputByte;
-import com.android.tools.r8.code.IputChar;
-import com.android.tools.r8.code.IputObject;
-import com.android.tools.r8.code.IputShort;
-import com.android.tools.r8.code.IputWide;
-import com.android.tools.r8.code.ReturnVoid;
-import com.android.tools.r8.code.SputObject;
-import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
-import com.android.tools.r8.graph.Code;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.optimize.Inliner.Reason;
-import com.android.tools.r8.ir.optimize.inliner.NopWhyAreYouNotInliningReporter;
-import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
-import com.android.tools.r8.ir.optimize.lambda.CaptureSignature;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroup.LambdaStructureError;
-import com.android.tools.r8.kotlin.Kotlin;
-import com.android.tools.r8.utils.ThrowingConsumer;
-import java.util.List;
-
-// Encapsulates the logic of deep-checking of the lambda class assumptions.
-//
-// For k- and j-style lambdas we only check the code of class and instance
-// initializers to ensure that their code performs no unexpected actions:
-//
-//  (a) Class initializer is only present for stateless lambdas and does
-//      nothing expect instantiating the instance and storing it in
-//      static instance field.
-//
-//  (b) Instance initializers stores all captured values in proper capture
-//      fields and calls the super constructor passing arity to it.
-abstract class KotlinLambdaClassValidator
-    implements ThrowingConsumer<DexClass, LambdaStructureError> {
-
-  static final String LAMBDA_INIT_CODE_VERIFICATION_FAILED =
-      "instance initializer code verification failed";
-  private static final String LAMBDA_CLINIT_CODE_VERIFICATION_FAILED =
-      "static initializer code verification failed";
-
-  final Kotlin kotlin;
-  private final KotlinLambdaGroup group;
-  private final AppInfoWithClassHierarchy appInfo;
-
-  KotlinLambdaClassValidator(
-      Kotlin kotlin, KotlinLambdaGroup group, AppInfoWithClassHierarchy appInfo) {
-    this.kotlin = kotlin;
-    this.group = group;
-    this.appInfo = appInfo;
-  }
-
-  // Report a structure error.
-  final LambdaStructureError structureError(String s) {
-    return new LambdaStructureError(s, false);
-  }
-
-  @Override
-  public void accept(DexClass lambda) throws LambdaStructureError {
-    if (!CaptureSignature.getCaptureSignature(lambda.instanceFields()).equals(group.id().capture)) {
-      throw structureError("capture signature was modified");
-    }
-
-    DexEncodedMethod classInitializer = null;
-    DexEncodedMethod instanceInitializer = null;
-    for (DexEncodedMethod method : lambda.directMethods()) {
-      // We only check bodies of class and instance initializers since we don't expect to
-      // see any static or private methods and all virtual methods will be translated into
-      // same methods dispatching on lambda id to proper code.
-      if (method.isClassInitializer()) {
-        Code code = method.getCode();
-        if (!(group.isStateless() && group.isSingletonLambda(lambda.type))) {
-          throw structureError("static initializer on non-singleton lambda");
-        }
-        if (classInitializer != null || code == null || !code.isDexCode()) {
-          throw structureError(LAMBDA_CLINIT_CODE_VERIFICATION_FAILED);
-        }
-        validateStatelessLambdaClassInitializer(lambda, code.asDexCode());
-        classInitializer = method;
-
-      } else if (method.isInstanceInitializer()) {
-        Code code = method.getCode();
-        if (instanceInitializer != null || code == null || !code.isDexCode()) {
-          throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
-        }
-        validateInstanceInitializer(lambda, code.asDexCode());
-        instanceInitializer = method;
-      }
-    }
-
-    if (group.isStateless() && group.isSingletonLambda(lambda.type) && (classInitializer == null)) {
-      throw structureError("missing static initializer on singleton lambda");
-    }
-
-    // This check is actually not required for lambda class merging, we only have to do
-    // this because group class method composition piggybacks on inlining which has
-    // assertions checking when we can inline force-inlined methods. So we double-check
-    // these assertion never triggers.
-    //
-    // NOTE: the real type name for lambda group class is not generated yet, but we
-    //       can safely use a fake one here.
-    DexType fakeLambdaGroupType = kotlin.factory.createType(
-        "L" + group.getTypePackage() + "-$$LambdaGroup$XXXX;");
-    WhyAreYouNotInliningReporter whyAreYouNotInliningReporter =
-        NopWhyAreYouNotInliningReporter.getInstance();
-    for (DexEncodedMethod method : lambda.virtualMethods()) {
-      if (!method.isInliningCandidate(
-          fakeLambdaGroupType, Reason.SIMPLE, appInfo, whyAreYouNotInliningReporter)) {
-        throw structureError("method " + method.method.toSourceString() +
-            " is not inline-able into lambda group class");
-      }
-    }
-  }
-
-  abstract int getInstanceInitializerMaxSize(List<DexEncodedField> captures);
-
-  abstract int validateInstanceInitializerEpilogue(
-      com.android.tools.r8.code.Instruction[] instructions, int index) throws LambdaStructureError;
-
-  private void validateInstanceInitializer(DexClass lambda, Code code)
-      throws LambdaStructureError {
-    List<DexEncodedField> captures = lambda.instanceFields();
-    com.android.tools.r8.code.Instruction[] instructions = code.asDexCode().instructions;
-    int index = 0;
-
-    if (instructions.length > getInstanceInitializerMaxSize(captures)) {
-      throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
-    }
-
-    // Capture field assignments: go through captured fields in assumed order
-    // and ensure they are assigned values from appropriate parameters.
-    index = validateInstanceInitializerParameterMapping(captures, instructions, index);
-
-    // Check the constructor epilogue: a call to superclass constructor.
-    index = validateInstanceInitializerEpilogue(instructions, index);
-    assert index == instructions.length;
-  }
-
-  private int validateInstanceInitializerParameterMapping(
-      List<DexEncodedField> captures, Instruction[] instructions, int index)
-      throws LambdaStructureError {
-    int dead = 0;
-    int wideFieldsSeen = 0;
-    for (DexEncodedField field : captures) {
-      if (field.getOptimizationInfo().isDead()) {
-        dead++;
-        continue;
-      }
-      switch (field.field.type.toShorty()) {
-        case 'Z':
-          if (!(instructions[index] instanceof IputBoolean)
-              || (instructions[index].getField() != field.field)
-              || (((Format22c) instructions[index]).A != (index + 1 + dead + wideFieldsSeen))) {
-            throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
-          }
-          break;
-
-        case 'B':
-          if (!(instructions[index] instanceof IputByte)
-              || (instructions[index].getField() != field.field)
-              || (((Format22c) instructions[index]).A != (index + 1 + dead + wideFieldsSeen))) {
-            throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
-          }
-          break;
-
-        case 'S':
-          if (!(instructions[index] instanceof IputShort)
-              || (instructions[index].getField() != field.field)
-              || (((Format22c) instructions[index]).A != (index + 1 + dead + wideFieldsSeen))) {
-            throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
-          }
-          break;
-
-        case 'C':
-          if (!(instructions[index] instanceof IputChar)
-              || (instructions[index].getField() != field.field)
-              || (((Format22c) instructions[index]).A != (index + 1 + dead + wideFieldsSeen))) {
-            throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
-          }
-          break;
-
-        case 'I':
-        case 'F':
-          if (!(instructions[index] instanceof Iput)
-              || (instructions[index].getField() != field.field)
-              || (((Format22c) instructions[index]).A != (index + 1 + dead + wideFieldsSeen))) {
-            throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
-          }
-          break;
-
-        case 'J':
-        case 'D':
-          if (!(instructions[index] instanceof IputWide)
-              || (instructions[index].getField() != field.field)
-              || (((Format22c) instructions[index]).A != (index + 1 + dead + wideFieldsSeen))) {
-            throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
-          }
-          wideFieldsSeen++;
-          break;
-
-        case 'L':
-          if (!(instructions[index] instanceof IputObject)
-              || (instructions[index].getField() != field.field)
-              || (((Format22c) instructions[index]).A != (index + 1 + dead + wideFieldsSeen))) {
-            throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
-          }
-          break;
-
-        default:
-          throw new Unreachable();
-      }
-      index++;
-    }
-    return index;
-  }
-
-  private void validateStatelessLambdaClassInitializer(DexClass lambda, Code code)
-      throws LambdaStructureError {
-    assert group.isStateless() && group.isSingletonLambda(lambda.type);
-    com.android.tools.r8.code.Instruction[] instructions = code.asDexCode().instructions;
-    if (instructions.length != 4) {
-      throw structureError(LAMBDA_CLINIT_CODE_VERIFICATION_FAILED);
-    }
-    if (!(instructions[0] instanceof com.android.tools.r8.code.NewInstance)
-        || ((com.android.tools.r8.code.NewInstance) instructions[0]).getType() != lambda.type) {
-      throw structureError(LAMBDA_CLINIT_CODE_VERIFICATION_FAILED);
-    }
-    if (!(instructions[1] instanceof com.android.tools.r8.code.InvokeDirect
-            || instructions[1] instanceof com.android.tools.r8.code.InvokeDirectRange)
-        || !isLambdaInitializerMethod(lambda, instructions[1].getMethod())) {
-      throw structureError(LAMBDA_CLINIT_CODE_VERIFICATION_FAILED);
-    }
-    if (!(instructions[2] instanceof SputObject)
-        || !isLambdaSingletonField(lambda, instructions[2].getField())) {
-      throw structureError(LAMBDA_CLINIT_CODE_VERIFICATION_FAILED);
-    }
-    if (!(instructions[3] instanceof ReturnVoid)) {
-      throw structureError(LAMBDA_CLINIT_CODE_VERIFICATION_FAILED);
-    }
-  }
-
-  private boolean isLambdaSingletonField(DexClass lambda, DexField field) {
-    return field.type == lambda.type
-        && field.holder == lambda.type
-        && field.name == kotlin.functional.kotlinStyleLambdaInstanceName;
-  }
-
-  private boolean isLambdaInitializerMethod(DexClass holder, DexMethod method) {
-    return method.holder == holder.type
-        && method.name == kotlin.factory.constructorMethodName
-        && method.proto.parameters.isEmpty()
-        && method.proto.returnType == kotlin.factory.voidType;
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaConstants.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaConstants.java
deleted file mode 100644
index 9ac5ca7..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaConstants.java
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.optimize.lambda.kotlin;
-
-import com.android.tools.r8.dex.Constants;
-import com.android.tools.r8.graph.ClassAccessFlags;
-import com.android.tools.r8.graph.FieldAccessFlags;
-import com.android.tools.r8.graph.MethodAccessFlags;
-
-interface KotlinLambdaConstants {
-  // Default lambda class flags.
-  ClassAccessFlags LAMBDA_CLASS_FLAGS =
-      ClassAccessFlags.fromDexAccessFlags(Constants.ACC_FINAL);
-  // Access-relaxed lambda class flags.
-  ClassAccessFlags PUBLIC_LAMBDA_CLASS_FLAGS =
-      ClassAccessFlags.fromDexAccessFlags(Constants.ACC_PUBLIC + Constants.ACC_FINAL);
-
-  // Default lambda class initializer flags.
-  MethodAccessFlags CLASS_INITIALIZER_FLAGS =
-      MethodAccessFlags.fromSharedAccessFlags(Constants.ACC_STATIC, true);
-  // Default lambda class constructor flags.
-  MethodAccessFlags CONSTRUCTOR_FLAGS =
-      MethodAccessFlags.fromSharedAccessFlags(0, true);
-  // Access-relaxed lambda class constructor flags.
-  MethodAccessFlags CONSTRUCTOR_FLAGS_RELAXED =
-      MethodAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC, true);
-
-  // Default main lambda method flags.
-  MethodAccessFlags MAIN_METHOD_FLAGS =
-      MethodAccessFlags.fromSharedAccessFlags(
-          Constants.ACC_PUBLIC + Constants.ACC_FINAL, false);
-  // Default bridge lambda method flags.
-  MethodAccessFlags BRIDGE_METHOD_FLAGS =
-      MethodAccessFlags.fromSharedAccessFlags(
-          Constants.ACC_PUBLIC + Constants.ACC_SYNTHETIC + Constants.ACC_BRIDGE, false);
-  // Bridge lambda method flags after inliner.
-  MethodAccessFlags BRIDGE_METHOD_FLAGS_FIXED =
-      MethodAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC, false);
-
-  // Default singleton instance folding field flags.
-  FieldAccessFlags SINGLETON_FIELD_FLAGS =
-      FieldAccessFlags.fromSharedAccessFlags(
-          Constants.ACC_PUBLIC + Constants.ACC_STATIC + Constants.ACC_FINAL);
-  // Default instance (lambda capture) field flags.
-  FieldAccessFlags CAPTURE_FIELD_FLAGS =
-      FieldAccessFlags.fromSharedAccessFlags(
-          Constants.ACC_FINAL + Constants.ACC_SYNTHETIC);
-  // access-relaxed instance (lambda capture) field flags.
-  FieldAccessFlags CAPTURE_FIELD_FLAGS_RELAXED =
-      FieldAccessFlags.fromSharedAccessFlags(
-          Constants.ACC_PUBLIC + Constants.ACC_FINAL + Constants.ACC_SYNTHETIC);
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroup.java
deleted file mode 100644
index fc3f2bd..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroup.java
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.optimize.lambda.kotlin;
-
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.optimize.lambda.CaptureSignature;
-import com.android.tools.r8.ir.optimize.lambda.CodeProcessor.Strategy;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroup;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroupId;
-
-// Represents a lambda group created to combine several lambda classes generated
-// by kotlin compiler for either regular kotlin lambda expressions (k-style lambdas)
-// or lambda expressions created to implement java SAM interface.
-abstract class KotlinLambdaGroup extends LambdaGroup {
-  private final Strategy strategy = new KotlinLambdaGroupCodeStrategy(this);
-
-  KotlinLambdaGroup(LambdaGroupId id) {
-    super(id);
-  }
-
-  final KotlinLambdaGroupId id() {
-    return (KotlinLambdaGroupId) id;
-  }
-
-  final boolean isStateless() {
-    return id().capture.isEmpty();
-  }
-
-  final boolean hasAnySingletons() {
-    assert isStateless();
-    return anyLambda(info -> this.isSingletonLambda(info.clazz.type));
-  }
-
-  final boolean isSingletonLambda(DexType lambda) {
-    assert isStateless();
-    return lambdaSingletonField(lambda) != null;
-  }
-
-  // Field referencing singleton instance for a lambda with specified id.
-  final DexField getSingletonInstanceField(DexItemFactory factory, int id) {
-    return factory.createField(this.getGroupClassType(),
-        this.getGroupClassType(), factory.createString("INSTANCE$" + id));
-  }
-
-  @Override
-  protected String getTypePackage() {
-    String pkg = id().pkg;
-    return pkg.isEmpty() ? "" : (pkg + "/");
-  }
-
-  final DexProto createConstructorProto(DexItemFactory factory) {
-    String capture = id().capture;
-    DexType[] newParameters = new DexType[capture.length() + 1];
-    newParameters[0] = factory.intType; // Lambda id.
-    for (int i = 0; i < capture.length(); i++) {
-      newParameters[i + 1] = CaptureSignature.fieldType(factory, capture, i);
-    }
-    return factory.createProto(factory.voidType, newParameters);
-  }
-
-  final DexField getLambdaIdField(DexItemFactory factory) {
-    return factory.createField(this.getGroupClassType(), factory.intType, "$id$");
-  }
-
-  final int mapFieldIntoCaptureIndex(DexType lambda, DexField field) {
-    return CaptureSignature.mapFieldIntoCaptureIndex(
-        id().capture, lambdaCaptureFields(lambda), field);
-  }
-
-  final DexField getCaptureField(DexItemFactory factory, int index) {
-    assert index >= 0 && index < id().capture.length();
-    return factory.createField(this.getGroupClassType(),
-        CaptureSignature.fieldType(factory, id().capture, index), "$capture$" + index);
-  }
-
-  @Override
-  public Strategy getCodeStrategy() {
-    return strategy;
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
deleted file mode 100644
index 977bdcf..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
+++ /dev/null
@@ -1,354 +0,0 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.optimize.lambda.kotlin;
-
-import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
-
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.ClassAccessFlags;
-import com.android.tools.r8.graph.Code;
-import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexTypeList;
-import com.android.tools.r8.graph.DexValue.DexValueNull;
-import com.android.tools.r8.graph.EnclosingMethodAttribute;
-import com.android.tools.r8.graph.GenericSignature;
-import com.android.tools.r8.graph.GenericSignature.ClassSignature;
-import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
-import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
-import com.android.tools.r8.graph.InnerClassAttribute;
-import com.android.tools.r8.graph.MethodAccessFlags;
-import com.android.tools.r8.graph.ParameterAnnotationsList;
-import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
-import com.android.tools.r8.ir.code.IntSwitch;
-import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroupClassBuilder;
-import com.android.tools.r8.ir.synthetic.SynthesizedCode;
-import com.android.tools.r8.ir.synthetic.SyntheticSourceCode;
-import com.android.tools.r8.origin.SynthesizedOrigin;
-import com.android.tools.r8.utils.Box;
-import com.android.tools.r8.utils.IntBox;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.TriConsumer;
-import com.google.common.collect.Lists;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-
-// Builds components of kotlin lambda group class.
-abstract class KotlinLambdaGroupClassBuilder<T extends KotlinLambdaGroup>
-    extends LambdaGroupClassBuilder<T> implements KotlinLambdaConstants {
-
-  final KotlinLambdaGroupId id;
-  final InternalOptions options;
-
-  KotlinLambdaGroupClassBuilder(
-      T group, DexItemFactory factory, InternalOptions options, String origin) {
-    super(group, factory, origin);
-    this.id = group.id();
-    this.options = options;
-  }
-
-  abstract SyntheticSourceCode createInstanceInitializerSourceCode(
-      DexType groupClassType, DexMethod initializerMethod, Position callerPosition);
-
-  // Always generate public final classes.
-  @Override
-  protected ClassAccessFlags buildAccessFlags() {
-    return PUBLIC_LAMBDA_CLASS_FLAGS;
-  }
-
-  // Take the attribute from the group, if exists.
-  @Override
-  protected EnclosingMethodAttribute buildEnclosingMethodAttribute() {
-    return id.enclosing;
-  }
-
-  // Take the attribute from the group, if exists.
-  @Override
-  protected List<InnerClassAttribute> buildInnerClasses() {
-    return !id.hasInnerClassAttribute()
-        ? Collections.emptyList()
-        : Lists.newArrayList(
-            new InnerClassAttribute(id.innerClassAccess, group.getGroupClassType(), null, null));
-  }
-
-  @Override
-  protected ClassSignature buildClassSignature() {
-    // Kotlin-style lambdas supported by the merged may only contain optional signature and
-    // kotlin metadata annotations. We remove the latter, but keep the signature if present.
-    return GenericSignature.parseClassSignature(
-        origin, id.signature, new SynthesizedOrigin(origin, getClass()), factory, options.reporter);
-  }
-
-  @Override
-  protected DexEncodedMethod[] buildVirtualMethods() {
-    // All virtual method are dispatched on $id$ field.
-    //
-    // For each of the virtual method name/signatures seen in the group
-    // we generate a correspondent method in lambda group class with same
-    // name/signatures dispatching the call to appropriate code taken
-    // from the lambda class.
-
-    Map<DexString, Map<DexProto, List<DexEncodedMethod>>> methods = collectVirtualMethods();
-    List<DexEncodedMethod> result = new ArrayList<>();
-
-    for (Entry<DexString, Map<DexProto, List<DexEncodedMethod>>> upper : methods.entrySet()) {
-      DexString methodName = upper.getKey();
-      for (Entry<DexProto, List<DexEncodedMethod>> inner : upper.getValue().entrySet()) {
-        // Methods for unique name/signature pair.
-        DexProto methodProto = inner.getKey();
-        List<DexEncodedMethod> implMethods = inner.getValue();
-
-        boolean isMainMethod =
-            id.mainMethodName == methodName && id.mainMethodProto == methodProto;
-
-        // Merging lambdas can introduce methods with too many instructions for the verifier on
-        // ART to give up statically verifying the method. We therefore split up the implementation
-        // methods and chain them with fallthrough:
-        // function <method>() {
-        //   switch(field.id) {
-        //     case 1:
-        //     case 2:
-        //     ...
-        //     case n:
-        //     default: <method$1>()
-        // }
-        //
-        // function <method$1>() {
-        //     case n + 1:
-        //     case n + 2:
-        //     ...
-        //     case n + m:
-        //     default: throw null
-        // }
-        IntBox counter = new IntBox(0);
-        Box<DexMethod> currentMethodBox =
-            new Box<>(factory.createMethod(group.getGroupClassType(), methodProto, methodName));
-        splitIntoGroupsBasedOnInstructionSize(
-            implMethods,
-            (implMethodsToAdd, methodsSoFar, methodsRemaining) -> {
-              assert currentMethodBox.isSet();
-              // For bridge methods we still use same PUBLIC FINAL as for the main method,
-              // since inlining removes BRIDGE & SYNTHETIC attributes from the bridge methods
-              // anyways and our new method is a product of inlining.
-              MethodAccessFlags accessFlags = MAIN_METHOD_FLAGS.copy();
-              DexMethod method = currentMethodBox.get();
-              DexMethod fallthrough =
-                  methodsRemaining
-                      ? factory.createMethod(
-                          group.getGroupClassType(),
-                          methodProto,
-                          methodName.toString() + "$" + counter.getAndIncrement())
-                      : null;
-              result.add(
-                  new DexEncodedMethod(
-                      method,
-                      accessFlags,
-                      MethodTypeSignature.noSignature(),
-                      isMainMethod ? id.mainMethodAnnotations : DexAnnotationSet.empty(),
-                      isMainMethod
-                          ? id.mainMethodParamAnnotations
-                          : ParameterAnnotationsList.empty(),
-                      new SynthesizedCode(
-                          callerPosition ->
-                              new KotlinLambdaVirtualMethodSourceCode(
-                                  factory,
-                                  group.getGroupClassType(),
-                                  method,
-                                  group.getLambdaIdField(factory),
-                                  implMethodsToAdd,
-                                  fallthrough,
-                                  methodsSoFar,
-                                  callerPosition)),
-                      true));
-              currentMethodBox.set(fallthrough);
-            });
-        assert !currentMethodBox.isSet();
-      }
-    }
-    return result.toArray(DexEncodedMethod.EMPTY_ARRAY);
-  }
-
-  private void splitIntoGroupsBasedOnInstructionSize(
-      List<DexEncodedMethod> implMethods,
-      TriConsumer<List<DexEncodedMethod>, Integer, Boolean> consumer) {
-    List<DexEncodedMethod> methods = new ArrayList<>();
-    // Upper bound in DEX for reading the field for switching on the group id.
-    final int fieldLoadInstructionSize = 10;
-    int verificationSizeLimitInBytes = options.verificationSizeLimitInBytes();
-    int currentInstructionsSize = fieldLoadInstructionSize;
-    int implMethodsCommitted = 0;
-    for (DexEncodedMethod implMethod : implMethods) {
-      int packedSwitchPayloadSize =
-          (int)
-              (IntSwitch.basePackedSize(options.getInternalOutputMode())
-                  + IntSwitch.packedPayloadSize(options.getInternalOutputMode(), methods.size()));
-      Code code = implMethod.getCode();
-      // We only do lambda merging for DEX. If we started doing lambda merging for CF, we would
-      // have to compute a size.
-      assert code.isDexCode();
-      int codeSize = code.asDexCode().codeSizeInBytes();
-      int estimatedMethodSize = currentInstructionsSize + codeSize + packedSwitchPayloadSize;
-      if (methods.size() > 0 && estimatedMethodSize > verificationSizeLimitInBytes) {
-        consumer.accept(methods, implMethodsCommitted, true);
-        currentInstructionsSize = fieldLoadInstructionSize;
-        implMethodsCommitted += methods.size();
-        methods = new ArrayList<>();
-      }
-      methods.add(implMethod);
-      currentInstructionsSize += codeSize;
-    }
-    consumer.accept(methods, implMethodsCommitted, false);
-  }
-
-  // Build a map of virtual methods with unique name/proto pointing to a list of methods
-  // from lambda classes implementing appropriate logic. The indices in the list correspond
-  // to lambda ids. Note that some of the slots in the lists may be empty, indicating the
-  // fact that corresponding lambda does not have a virtual method with this signature.
-  private Map<DexString, Map<DexProto, List<DexEncodedMethod>>> collectVirtualMethods() {
-    Map<DexString, Map<DexProto, List<DexEncodedMethod>>> methods = new LinkedHashMap<>();
-    int size = group.size();
-    group.forEachLambda(info -> {
-      for (DexEncodedMethod method : info.clazz.virtualMethods()) {
-        List<DexEncodedMethod> list = methods
-            .computeIfAbsent(method.method.name,
-                k -> new LinkedHashMap<>())
-            .computeIfAbsent(method.method.proto,
-                k -> Lists.newArrayList(Collections.nCopies(size, null)));
-        assert list.get(info.id) == null;
-        list.set(info.id, method);
-      }
-    });
-    return methods;
-  }
-
-  @Override
-  protected DexEncodedMethod[] buildDirectMethods() {
-    // We only build an instance initializer and optional class
-    // initializer for stateless lambdas.
-
-    boolean needsSingletonInstances = group.isStateless() && group.hasAnySingletons();
-    DexType groupClassType = group.getGroupClassType();
-
-    DexEncodedMethod[] result = new DexEncodedMethod[needsSingletonInstances ? 2 : 1];
-    // Instance initializer mapping parameters into capture fields.
-    DexProto initializerProto = group.createConstructorProto(factory);
-    DexMethod initializerMethod =
-        factory.createMethod(groupClassType, initializerProto, factory.constructorMethodName);
-    result[0] =
-        new DexEncodedMethod(
-            initializerMethod,
-            CONSTRUCTOR_FLAGS_RELAXED, // always create access-relaxed constructor.
-            MethodTypeSignature.noSignature(),
-            DexAnnotationSet.empty(),
-            ParameterAnnotationsList.empty(),
-            new SynthesizedCode(
-                callerPosition ->
-                    createInstanceInitializerSourceCode(
-                        groupClassType, initializerMethod, callerPosition)),
-            true);
-
-    // Static class initializer for stateless lambdas.
-    if (needsSingletonInstances) {
-      DexMethod method =
-          factory.createMethod(
-              groupClassType,
-              factory.createProto(factory.voidType),
-              factory.classConstructorMethodName);
-      result[1] =
-          new DexEncodedMethod(
-              method,
-              CLASS_INITIALIZER_FLAGS,
-              MethodTypeSignature.noSignature(),
-              DexAnnotationSet.empty(),
-              ParameterAnnotationsList.empty(),
-              new SynthesizedCode(
-                  callerPosition ->
-                      new ClassInitializerSourceCode(method, factory, group, callerPosition)),
-              true);
-    }
-
-    return result;
-  }
-
-  @Override
-  protected DexEncodedField[] buildInstanceFields() {
-    // Lambda id field plus other fields defined by the capture signature.
-    String capture = id.capture;
-    int size = capture.length();
-    DexEncodedField[] result = new DexEncodedField[1 + size];
-
-    result[0] =
-        new DexEncodedField(
-            group.getLambdaIdField(factory),
-            CAPTURE_FIELD_FLAGS_RELAXED,
-            FieldTypeSignature.noSignature(),
-            DexAnnotationSet.empty(),
-            null);
-
-    for (int id = 0; id < size; id++) {
-      result[id + 1] =
-          new DexEncodedField(
-              group.getCaptureField(factory, id),
-              CAPTURE_FIELD_FLAGS_RELAXED,
-              FieldTypeSignature.noSignature(),
-              DexAnnotationSet.empty(),
-              null);
-    }
-
-    return result;
-  }
-
-  @Override
-  protected DexEncodedField[] buildStaticFields(
-      AppView<? extends AppInfoWithClassHierarchy> appView, OptimizationFeedback feedback) {
-    if (!group.isStateless()) {
-      return DexEncodedField.EMPTY_ARRAY;
-    }
-    // One field for each singleton lambda in the group.
-    List<DexEncodedField> result = new ArrayList<>(group.size());
-    group.forEachLambda(
-        info -> {
-          if (group.isSingletonLambda(info.clazz.type)) {
-            DexField field = group.getSingletonInstanceField(factory, info.id);
-            DexEncodedField encodedField =
-                new DexEncodedField(
-                    field,
-                    SINGLETON_FIELD_FLAGS,
-                    FieldTypeSignature.noSignature(),
-                    DexAnnotationSet.empty(),
-                    DexValueNull.NULL);
-            result.add(encodedField);
-
-            // Record that the field is definitely not null. It is guaranteed to be assigned in the
-            // class initializer of the enclosing class before it is read.
-            ClassTypeElement exactType =
-                ClassTypeElement.create(field.type, definitelyNotNull(), appView);
-            feedback.markFieldHasDynamicLowerBoundType(encodedField, exactType);
-            feedback.markFieldHasDynamicUpperBoundType(encodedField, exactType);
-          }
-        });
-    assert result.isEmpty() == !group.hasAnySingletons();
-    return result.toArray(DexEncodedField.EMPTY_ARRAY);
-  }
-
-  @Override
-  protected DexTypeList buildInterfaces() {
-    return new DexTypeList(new DexType[]{id.iface});
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java
deleted file mode 100644
index 2c88700..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java
+++ /dev/null
@@ -1,310 +0,0 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.optimize.lambda.kotlin;
-
-import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
-import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
-
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.analysis.type.TypeElement;
-import com.android.tools.r8.ir.code.Argument;
-import com.android.tools.r8.ir.code.CheckCast;
-import com.android.tools.r8.ir.code.ConstNumber;
-import com.android.tools.r8.ir.code.InitClass;
-import com.android.tools.r8.ir.code.InstanceGet;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.InvokeDirect;
-import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.InvokeVirtual;
-import com.android.tools.r8.ir.code.NewInstance;
-import com.android.tools.r8.ir.code.StaticGet;
-import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.optimize.lambda.CaptureSignature;
-import com.android.tools.r8.ir.optimize.lambda.CodeProcessor;
-import com.android.tools.r8.ir.optimize.lambda.CodeProcessor.Strategy;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroup;
-import com.android.tools.r8.ir.optimize.lambda.LambdaMerger.ApplyStrategy;
-import java.util.ArrayList;
-import java.util.List;
-
-// Defines the code processing strategy for kotlin lambdas.
-final class KotlinLambdaGroupCodeStrategy implements Strategy {
-  private final KotlinLambdaGroup group;
-
-  KotlinLambdaGroupCodeStrategy(KotlinLambdaGroup group) {
-    this.group = group;
-  }
-
-  @Override
-  public LambdaGroup group() {
-    return group;
-  }
-
-  @Override
-  public boolean isValidStaticFieldWrite(CodeProcessor context, DexField field) {
-    DexType lambda = field.holder;
-    assert group.containsLambda(lambda);
-    // Only support writes to singleton static field named 'INSTANCE' from lambda
-    // static class initializer.
-    return field.name == context.kotlin.functional.kotlinStyleLambdaInstanceName
-        && lambda == field.type
-        && context.method.getDefinition().isClassInitializer()
-        && context.method.getHolderType() == lambda;
-  }
-
-  @Override
-  public boolean isValidStaticFieldRead(CodeProcessor context, DexField field) {
-    DexType lambda = field.holder;
-    assert group.containsLambda(lambda);
-    // Support all reads of singleton static field named 'INSTANCE'.
-    return field.name == context.kotlin.functional.kotlinStyleLambdaInstanceName
-        && lambda == field.type;
-  }
-
-  @Override
-  public boolean isValidInstanceFieldWrite(CodeProcessor context, DexField field) {
-    DexType lambda = field.holder;
-    DexMethod method = context.method.getReference();
-    assert group.containsLambda(lambda);
-    // Support writes to capture instance fields inside lambda constructor only.
-    return method.holder == lambda && context.method.getDefinition().isInstanceInitializer();
-  }
-
-  @Override
-  public boolean isValidInstanceFieldRead(CodeProcessor context, DexField field) {
-    assert group.containsLambda(field.holder);
-    // Support all reads from capture instance fields.
-    return true;
-  }
-
-  @Override
-  public boolean isValidNewInstance(CodeProcessor context, NewInstance invoke) {
-    // Only valid for stateful lambdas.
-    return !(group.isStateless() && group.isSingletonLambda(invoke.clazz));
-  }
-
-  @Override
-  public boolean isValidInvoke(CodeProcessor context, InvokeMethod invoke) {
-    return isValidInitializerCall(context, invoke) || isValidVirtualCall(invoke);
-  }
-
-  private boolean isValidInitializerCall(CodeProcessor context, InvokeMethod invoke) {
-    DexMethod method = invoke.getInvokedMethod();
-    DexType lambda = method.holder;
-    assert group.containsLambda(lambda);
-    // Allow calls to a constructor from other classes if the lambda is singleton,
-    // otherwise allow such a call only from the same class static initializer.
-    boolean isSingletonLambda = group.isStateless() && group.isSingletonLambda(lambda);
-    return (isSingletonLambda == (context.method.getHolderType() == lambda))
-        && invoke.isInvokeDirect()
-        && context.factory.isConstructor(method)
-        && CaptureSignature.getCaptureSignature(method.proto.parameters).equals(group.id().capture);
-  }
-
-  private boolean isValidVirtualCall(InvokeMethod invoke) {
-    assert group.containsLambda(invoke.getInvokedMethod().holder);
-    // Allow all virtual calls.
-    return invoke.isInvokeVirtual();
-  }
-
-  @Override
-  public boolean isValidInitClass(CodeProcessor context, DexType clazz) {
-    assert group.containsLambda(clazz);
-    // Support all init class instructions.
-    return true;
-  }
-
-  @Override
-  public boolean isValidHolder(CodeProcessor context, DexType holder) {
-    assert group.containsLambda(holder);
-    return true;
-  }
-
-  @Override
-  public void patch(ApplyStrategy context, NewInstance newInstance) {
-    DexType oldType = newInstance.clazz;
-    DexType newType = group.getGroupClassType();
-
-    NewInstance patchedNewInstance =
-        new NewInstance(
-            newType,
-            context.code.createValue(
-                TypeElement.fromDexType(newType, definitelyNotNull(), context.appView)));
-    context.instructions().replaceCurrentInstruction(patchedNewInstance);
-
-    assert newType != oldType;
-    context.recordTypeHasChanged(patchedNewInstance.outValue());
-  }
-
-  @Override
-  public void patch(ApplyStrategy context, InvokeMethod invoke) {
-    assert group.containsLambda(invoke.getInvokedMethod().holder);
-    if (isValidInitializerCall(context, invoke)) {
-      patchInitializer(context, invoke.asInvokeDirect());
-    } else {
-      // Regular calls to virtual methods only need target method be replaced.
-      assert isValidVirtualCall(invoke);
-      DexMethod oldMethod = invoke.getInvokedMethod();
-      DexMethod newMethod = mapVirtualMethod(context.factory, oldMethod);
-
-      InvokeVirtual patchedInvokeVirtual =
-          new InvokeVirtual(
-              newMethod,
-              createValueForType(context, newMethod.proto.returnType),
-              invoke.arguments());
-      context.instructions().replaceCurrentInstruction(patchedInvokeVirtual);
-
-      // Otherwise, we need to record that the type of the out-value has changed.
-      assert newMethod.proto.returnType == oldMethod.proto.returnType;
-    }
-  }
-
-  @Override
-  public void patch(ApplyStrategy context, InstanceGet instanceGet) {
-    DexField oldField = instanceGet.getField();
-    DexField newField = mapCaptureField(context.factory, oldField.holder, oldField);
-
-    DexType oldFieldType = oldField.type;
-    DexType newFieldType = newField.type;
-
-    // We need to insert remapped values and in case the capture field
-    // of type Object optionally cast to expected field.
-    InstanceGet newInstanceGet =
-        new InstanceGet(createValueForType(context, newFieldType), instanceGet.object(), newField);
-    context.instructions().replaceCurrentInstruction(newInstanceGet);
-
-    if (oldFieldType.isPrimitiveType() || oldFieldType == context.factory.objectType) {
-      return;
-    }
-
-    // Since all captured values of non-primitive types are stored in fields of type
-    // java.lang.Object, we need to cast them to appropriate type to satisfy the verifier.
-    TypeElement castTypeLattice =
-        TypeElement.fromDexType(oldFieldType, maybeNull(), context.appView);
-    Value newValue = context.code.createValue(castTypeLattice, newInstanceGet.getLocalInfo());
-    newInstanceGet.outValue().replaceUsers(newValue);
-    CheckCast cast = new CheckCast(newValue, newInstanceGet.outValue(), oldFieldType);
-    cast.setPosition(newInstanceGet.getPosition());
-    context.instructions().add(cast);
-
-    // If the current block has catch handlers split the check cast into its own block.
-    // Since new cast is never supposed to fail, we leave catch handlers empty.
-    if (cast.getBlock().hasCatchHandlers()) {
-      context.instructions().previous();
-      context.instructions().split(context.code, 1, context.blocks);
-    }
-  }
-
-  @Override
-  public void patch(ApplyStrategy context, StaticGet staticGet) {
-    DexField oldField = staticGet.getField();
-    DexField newField = mapSingletonInstanceField(context.factory, oldField);
-
-    StaticGet patchedStaticGet =
-        new StaticGet(
-            context.code.createValue(
-                TypeElement.fromDexType(newField.type, maybeNull(), context.appView)),
-            newField);
-    context.instructions().replaceCurrentInstruction(patchedStaticGet);
-
-    assert newField.type != oldField.type;
-    context.recordTypeHasChanged(patchedStaticGet.outValue());
-  }
-
-  @Override
-  public void patch(ApplyStrategy context, InitClass initClass) {
-    InitClass pachedInitClass =
-        new InitClass(context.code.createValue(TypeElement.getInt()), group.getGroupClassType());
-    context.instructions().replaceCurrentInstruction(pachedInitClass);
-  }
-
-  @Override
-  public void patch(ApplyStrategy context, Argument argument) {
-    // An argument can be a direct operand to a phi that we potentially could not remove.
-    assert argument.getIndex() == 0;
-    // The argument value will be replaced by the invoke value.
-    argument
-        .outValue()
-        .setType(TypeElement.fromDexType(group.getGroupClassType(), maybeNull(), context.appView));
-    context.recordTypeHasChanged(argument.outValue());
-  }
-
-  private void patchInitializer(CodeProcessor context, InvokeDirect invoke) {
-    // Patching includes:
-    //  - change of methods
-    //  - adding lambda id as the first argument
-    //  - reshuffling other arguments (representing captured values)
-    //    according to capture signature of the group.
-
-    DexMethod method = invoke.getInvokedMethod();
-    DexType lambda = method.holder;
-
-    // Create constant with lambda id.
-    Value lambdaIdValue = context.code.createValue(TypeElement.getInt());
-    ConstNumber lambdaId = new ConstNumber(lambdaIdValue, group.lambdaId(lambda));
-    lambdaId.setPosition(invoke.getPosition());
-    context.instructions().previous();
-    context.instructions().add(lambdaId);
-
-    // Create a new InvokeDirect instruction.
-    Instruction next = context.instructions().next();
-    assert next == invoke;
-
-    DexMethod newTarget = mapInitializerMethod(context.factory, method);
-    List<Value> newArguments = mapInitializerArgs(lambdaIdValue, invoke.arguments(), method.proto);
-    context.instructions().replaceCurrentInstruction(
-        new InvokeDirect(newTarget, null /* no return value */, newArguments)
-    );
-  }
-
-  private Value createValueForType(CodeProcessor context, DexType returnType) {
-    return returnType == context.factory.voidType
-        ? null
-        : context.code.createValue(
-            TypeElement.fromDexType(returnType, maybeNull(), context.appView));
-  }
-
-  private List<Value> mapInitializerArgs(
-      Value lambdaIdValue, List<Value> oldArguments, DexProto proto) {
-    assert oldArguments.size() == proto.parameters.size() + 1;
-    List<Value> newArguments = new ArrayList<>();
-    newArguments.add(oldArguments.get(0)); // receiver
-    newArguments.add(lambdaIdValue); // lambda-id
-    List<Integer> reverseMapping =
-        CaptureSignature.getReverseCaptureMapping(proto.parameters.values);
-    for (int index : reverseMapping) {
-      // <original-capture-index> = mapping[<normalized-capture-index>]
-      newArguments.add(oldArguments.get(index + 1 /* after receiver */));
-    }
-    return newArguments;
-  }
-
-  // Map lambda class initializer into lambda group class initializer.
-  private DexMethod mapInitializerMethod(DexItemFactory factory, DexMethod method) {
-    assert factory.isConstructor(method);
-    assert CaptureSignature.getCaptureSignature(method.proto.parameters).equals(group.id().capture);
-    return factory.createMethod(group.getGroupClassType(),
-        group.createConstructorProto(factory), method.name);
-  }
-
-  // Map lambda class virtual method into lambda group class method.
-  private DexMethod mapVirtualMethod(DexItemFactory factory, DexMethod method) {
-    return factory.createMethod(group.getGroupClassType(), method.proto, method.name);
-  }
-
-  // Map lambda class capture field into lambda group class capture field.
-  private DexField mapCaptureField(DexItemFactory factory, DexType lambda, DexField field) {
-    return group.getCaptureField(factory, group.mapFieldIntoCaptureIndex(lambda, field));
-  }
-
-  // Map lambda class initializer into lambda group class initializer.
-  private DexField mapSingletonInstanceField(DexItemFactory factory, DexField field) {
-    return group.getSingletonInstanceField(factory, group.lambdaId(field.holder));
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupId.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupId.java
deleted file mode 100644
index 7f24d79..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupId.java
+++ /dev/null
@@ -1,149 +0,0 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.optimize.lambda.kotlin;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.EnclosingMethodAttribute;
-import com.android.tools.r8.graph.InnerClassAttribute;
-import com.android.tools.r8.graph.ParameterAnnotationsList;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroup;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroupId;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-
-abstract class KotlinLambdaGroupId implements LambdaGroupId {
-  private static final int MISSING_INNER_CLASS_ATTRIBUTE = -1;
-
-  private final int hash;
-
-  // Capture signature.
-  final String capture;
-  // Kotlin functional interface.
-  final DexType iface;
-  // Package (in case access relaxation is enabled root package is used).
-  final String pkg;
-  // Generic signature of the lambda class.
-  final String signature;
-
-  // Characteristics of the main lambda method. Kotlin generates one main method with
-  // the signature matching the lambda signature, and a bridge method (if needed)
-  // forwarding the call via interface to the main method. Main method may have
-  // generic signature and parameter annotations defining nullability.
-  //
-  // TODO: address cases when main method is removed after inlining.
-  // (the main method if created is supposed to be inlined, since it is always
-  //  only called from the bridge, and removed. In this case the method with
-  //  signature and annotations is removed allowing more lambda to be merged.)
-  final DexString mainMethodName;
-  final DexProto mainMethodProto;
-  final DexAnnotationSet mainMethodAnnotations;
-  final ParameterAnnotationsList mainMethodParamAnnotations;
-
-  final EnclosingMethodAttribute enclosing;
-
-  // Note that lambda classes are always created as _anonymous_ inner classes. We only
-  // need to store the fact that the class had this attribute and if it had store the
-  // access from InnerClassAttribute.
-  final int innerClassAccess;
-
-  KotlinLambdaGroupId(
-      AppView<AppInfoWithLiveness> appView,
-      String capture,
-      DexType iface,
-      String pkg,
-      String signature,
-      DexEncodedMethod mainMethod,
-      InnerClassAttribute inner,
-      EnclosingMethodAttribute enclosing) {
-    assert capture != null && iface != null && pkg != null && mainMethod != null;
-    assert inner == null || (inner.isAnonymous() && inner.getOuter() == null);
-    this.capture = capture;
-    this.iface = iface;
-    this.pkg = pkg;
-    this.signature = signature;
-    this.mainMethodName = mainMethod.method.name;
-    this.mainMethodProto = mainMethod.method.proto;
-    this.mainMethodAnnotations = mainMethod.liveAnnotations(appView);
-    this.mainMethodParamAnnotations = mainMethod.liveParameterAnnotations(appView);
-    this.innerClassAccess = inner != null ? inner.getAccess() : MISSING_INNER_CLASS_ATTRIBUTE;
-    this.enclosing = enclosing;
-    this.hash = computeHashCode();
-  }
-
-  final boolean hasInnerClassAttribute() {
-    return innerClassAccess != MISSING_INNER_CLASS_ATTRIBUTE;
-  }
-
-  @Override
-  public final int hashCode() {
-    return hash;
-  }
-
-  private int computeHashCode() {
-    int hash = capture.hashCode() * 7;
-    hash += iface.hashCode() * 17;
-    hash += pkg.hashCode() * 37;
-    hash += signature != null ? signature.hashCode() * 47 : 0;
-    hash += mainMethodName.hashCode() * 71;
-    hash += mainMethodProto.hashCode() * 89;
-    hash += mainMethodAnnotations != null ? mainMethodAnnotations.hashCode() * 101 : 0;
-    hash += mainMethodParamAnnotations != null ? mainMethodParamAnnotations.hashCode() * 113 : 0;
-    hash += innerClassAccess * 131;
-    hash += enclosing != null ? enclosing.hashCode() * 211 : 0;
-    return hash;
-  }
-
-  @Override
-  public abstract boolean equals(Object obj);
-
-  boolean computeEquals(KotlinLambdaGroupId other) {
-    return capture.equals(other.capture) &&
-        iface == other.iface &&
-        pkg.equals(other.pkg) &&
-        mainMethodName == other.mainMethodName &&
-        mainMethodProto == other.mainMethodProto &&
-        (mainMethodAnnotations == null ? other.mainMethodAnnotations == null
-            : mainMethodAnnotations.equals(other.mainMethodAnnotations)) &&
-        (mainMethodParamAnnotations == null ? other.mainMethodParamAnnotations == null
-            : mainMethodParamAnnotations.equals(other.mainMethodParamAnnotations)) &&
-        (signature == null ? other.signature == null : signature.equals(other.signature)) &&
-        innerClassAccess == other.innerClassAccess &&
-        (enclosing == null ? other.enclosing == null : enclosing.equals(other.enclosing));
-  }
-
-  @Override
-  public String toString() {
-    StringBuilder builder = new StringBuilder(getLambdaKindDescriptor())
-        .append("\n  capture: ").append(capture)
-        .append("\n  interface: ").append(iface.descriptor)
-        .append("\n  package: ").append(pkg)
-        .append("\n  signature: ").append(signature)
-        .append("\n  main method name: ").append(mainMethodName.toString())
-        .append("\n  main method: ").append(mainMethodProto.toSourceString())
-        .append("\n  main annotations: ").append(mainMethodAnnotations)
-        .append("\n  main param annotations: ").append(mainMethodParamAnnotations)
-        .append("\n  inner: ")
-        .append(innerClassAccess == MISSING_INNER_CLASS_ATTRIBUTE ? "none" : innerClassAccess);
-    if (enclosing != null) {
-      if (enclosing.getEnclosingClass() != null) {
-        builder.append("\n  enclosingClass: ")
-            .append(enclosing.getEnclosingClass().descriptor);
-      } else {
-        builder.append("\n  enclosingMethod: ")
-            .append(enclosing.getEnclosingMethod().toSourceString());
-      }
-    }
-    return builder.toString();
-  }
-
-  abstract String getLambdaKindDescriptor();
-
-  @Override
-  public abstract LambdaGroup createGroup();
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
deleted file mode 100644
index 472d861..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
+++ /dev/null
@@ -1,244 +0,0 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.optimize.lambda.kotlin;
-
-import com.android.tools.r8.graph.AccessFlags;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexAnnotation;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.InnerClassAttribute;
-import com.android.tools.r8.ir.optimize.lambda.CaptureSignature;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroup.LambdaStructureError;
-import com.android.tools.r8.ir.optimize.lambda.LambdaGroupId;
-import com.android.tools.r8.kotlin.Kotlin;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import java.util.List;
-
-public abstract class KotlinLambdaGroupIdFactory implements KotlinLambdaConstants {
-  KotlinLambdaGroupIdFactory() {
-  }
-
-  public static KotlinLambdaGroupIdFactory getFactoryForClass(DexProgramClass clazz) {
-    if (clazz.getKotlinInfo().isSyntheticClass()
-        && clazz.getKotlinInfo().asSyntheticClass().isLambda()) {
-      if (clazz.getKotlinInfo().asSyntheticClass().isKotlinStyleLambda()) {
-        return KStyleLambdaGroupIdFactory.getInstance();
-      }
-      assert clazz.getKotlinInfo().asSyntheticClass().isJavaStyleLambda();
-      return JStyleLambdaGroupIdFactory.getInstance();
-    }
-    return null;
-  }
-
-  // Creates a lambda group id for a Java or Kotlin style lambda. Never returns null, but may throw
-  // a LambdaStructureError if the lambda does not pass pre-requirements (mostly by not meeting
-  // high-level structure expectations).
-  //
-  // At this point we only perform high-level checks before qualifying the lambda as a candidate
-  // for merging and assigning lambda group id. We can NOT perform checks on method bodies since
-  // they may not be converted yet, we'll do that in KStyleLambdaClassValidator.
-  public abstract LambdaGroupId validateAndCreate(
-      AppView<AppInfoWithLiveness> appView, Kotlin kotlin, DexClass lambda)
-      throws LambdaStructureError;
-
-  abstract void validateSuperclass(Kotlin kotlin, DexClass lambda) throws LambdaStructureError;
-
-  abstract DexType validateInterfaces(Kotlin kotlin, DexClass lambda) throws LambdaStructureError;
-
-  DexEncodedMethod validateVirtualMethods(DexClass lambda) throws LambdaStructureError {
-    DexEncodedMethod mainMethod = null;
-
-    for (DexEncodedMethod method : lambda.virtualMethods()) {
-      if (method.accessFlags.materialize() == MAIN_METHOD_FLAGS.materialize()) {
-        if (mainMethod != null) {
-          throw new LambdaStructureError("more than one main method found");
-        }
-        mainMethod = method;
-      } else {
-        checkAccessFlags("unexpected virtual method access flags",
-            method.accessFlags, BRIDGE_METHOD_FLAGS, BRIDGE_METHOD_FLAGS_FIXED);
-        checkDirectMethodAnnotations(method);
-      }
-    }
-
-    if (mainMethod == null) {
-      // Missing main method may be a result of tree shaking.
-      throw new LambdaStructureError("no main method found", false);
-    }
-    return mainMethod;
-  }
-
-  InnerClassAttribute validateInnerClasses(DexClass lambda) throws LambdaStructureError {
-    List<InnerClassAttribute> innerClasses = lambda.getInnerClasses();
-    if (innerClasses != null) {
-      for (InnerClassAttribute inner : innerClasses) {
-        if (inner.getInner() == lambda.type) {
-          if (!inner.isAnonymous()) {
-            throw new LambdaStructureError("is not anonymous");
-          }
-          return inner;
-        }
-      }
-    }
-    return null;
-  }
-
-  public static boolean hasValidAnnotations(Kotlin kotlin, DexClass lambda) {
-    for (DexAnnotation annotation : lambda.annotations().annotations) {
-      if (annotation.annotation.type == kotlin.factory.kotlinMetadataType) {
-        continue;
-      }
-      return false;
-    }
-    return true;
-  }
-
-  String validateAnnotations(AppView<AppInfoWithLiveness> appView, Kotlin kotlin, DexClass lambda)
-      throws LambdaStructureError {
-    for (DexAnnotation annotation : lambda.liveAnnotations(appView).annotations) {
-      if (annotation.annotation.type == appView.dexItemFactory().kotlinMetadataType) {
-        // Ignore kotlin metadata on lambda classes. Metadata on synthetic
-        // classes exists but is not used in the current Kotlin version (1.2.21)
-        // and newly generated lambda _group_ class is not exactly a kotlin class.
-        continue;
-      }
-
-      assert !hasValidAnnotations(kotlin, lambda);
-      throw new LambdaStructureError(
-          "unexpected annotation: " + annotation.annotation.type.toSourceString());
-    }
-    assert hasValidAnnotations(kotlin, lambda);
-    return lambda.getClassSignature().toString();
-  }
-
-  void validateStaticFields(Kotlin kotlin, DexClass lambda) throws LambdaStructureError {
-    List<DexEncodedField> staticFields = lambda.staticFields();
-    if (staticFields.size() == 1) {
-      DexEncodedField field = staticFields.get(0);
-      if (field.field.name != kotlin.functional.kotlinStyleLambdaInstanceName ||
-          field.field.type != lambda.type || !field.accessFlags.isPublic() ||
-          !field.accessFlags.isFinal() || !field.accessFlags.isStatic()) {
-        throw new LambdaStructureError("unexpected static field " + field.toSourceString());
-      }
-      // No state if the lambda is a singleton.
-      if (lambda.instanceFields().size() > 0) {
-        throw new LambdaStructureError("has instance fields along with INSTANCE");
-      }
-      checkAccessFlags("static field access flags", field.accessFlags, SINGLETON_FIELD_FLAGS);
-      checkFieldAnnotations(field);
-
-    } else if (staticFields.size() > 1) {
-      throw new LambdaStructureError(
-          "only one static field max expected, found " + staticFields.size());
-    }
-  }
-
-  String validateInstanceFields(DexClass lambda, boolean accessRelaxed)
-      throws LambdaStructureError {
-    List<DexEncodedField> instanceFields = lambda.instanceFields();
-    for (DexEncodedField field : instanceFields) {
-      checkAccessFlags("capture field access flags", field.accessFlags,
-          accessRelaxed ? CAPTURE_FIELD_FLAGS_RELAXED : CAPTURE_FIELD_FLAGS);
-      checkFieldAnnotations(field);
-    }
-    return CaptureSignature.getCaptureSignature(instanceFields);
-  }
-
-  void validateDirectMethods(DexClass lambda) throws LambdaStructureError {
-    for (DexEncodedMethod method : lambda.directMethods()) {
-      if (method.isClassInitializer()) {
-        // We expect to see class initializer only if there is a singleton field.
-        if (lambda.staticFields().size() != 1) {
-          throw new LambdaStructureError("has static initializer, but no singleton field");
-        }
-        checkAccessFlags(
-            "unexpected static initializer access flags",
-            method.accessFlags.getOriginalAccessFlags(),
-            CLASS_INITIALIZER_FLAGS);
-        checkDirectMethodAnnotations(method);
-      } else if (method.isStatic()) {
-        throw new LambdaStructureError(
-            "unexpected static method: " + method.method.toSourceString());
-      } else if (method.isInstanceInitializer()) {
-        // Lambda class is expected to have one constructor
-        // with parameters matching capture signature.
-        DexType[] parameters = method.method.proto.parameters.values;
-        List<DexEncodedField> instanceFields = lambda.instanceFields();
-        if (parameters.length != instanceFields.size()) {
-          throw new LambdaStructureError("constructor parameters don't match captured values.");
-        }
-        for (int i = 0; i < parameters.length; i++) {
-          // Kotlin compiler sometimes reshuffles the parameters so that their order
-          // in the constructor don't match order of capture fields. We could add
-          // support for it, but it happens quite rarely so don't bother for now.
-          if (parameters[i] != instanceFields.get(i).field.type) {
-            throw new LambdaStructureError(
-                "constructor parameters don't match captured values.", false);
-          }
-        }
-        checkAccessFlags("unexpected constructor access flags",
-            method.accessFlags, CONSTRUCTOR_FLAGS, CONSTRUCTOR_FLAGS_RELAXED);
-        checkDirectMethodAnnotations(method);
-
-      } else if (method.isPrivateMethod()) {
-        // TODO(b/135975229)
-        throw new LambdaStructureError("private method: " + method.method.toSourceString());
-      } else {
-        assert false;
-        throw new LambdaStructureError(
-            "unexpected method encountered: " + method.method.toSourceString());
-      }
-    }
-  }
-
-  private static void checkDirectMethodAnnotations(DexEncodedMethod method)
-      throws LambdaStructureError {
-    if (!method.annotations().isEmpty()) {
-      throw new LambdaStructureError(
-          "unexpected method annotations ["
-              + method.annotations().toSmaliString()
-              + "] on "
-              + method.method.toSourceString());
-    }
-    if (!method.parameterAnnotationsList.isEmpty()) {
-      throw new LambdaStructureError(
-          "unexpected method parameters annotations ["
-              + method.parameterAnnotationsList.toSmaliString()
-              + "] on "
-              + method.method.toSourceString());
-    }
-  }
-
-  private static void checkFieldAnnotations(DexEncodedField field) throws LambdaStructureError {
-    if (field.hasAnnotation()) {
-      throw new LambdaStructureError(
-          "unexpected field annotations ["
-              + field.annotations().toSmaliString()
-              + "] on "
-              + field.field.toSourceString());
-    }
-  }
-
-  @SafeVarargs
-  static <T extends AccessFlags> void checkAccessFlags(
-      String message, T actual, T... expected) throws LambdaStructureError {
-    checkAccessFlags(message, actual.materialize(), expected);
-  }
-
-  @SafeVarargs
-  private static <T extends AccessFlags> void checkAccessFlags(
-      String message, int actual, T... expected) throws LambdaStructureError {
-    for (T flag : expected) {
-      if (actual == flag.materialize()) {
-        return;
-      }
-    }
-    throw new LambdaStructureError(message);
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaVirtualMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaVirtualMethodSourceCode.java
deleted file mode 100644
index 657a796..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaVirtualMethodSourceCode.java
+++ /dev/null
@@ -1,122 +0,0 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.optimize.lambda.kotlin;
-
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.code.Invoke;
-import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.code.ValueType;
-import com.android.tools.r8.ir.conversion.IRBuilder;
-import com.android.tools.r8.ir.synthetic.SyntheticSourceCode;
-import java.util.ArrayList;
-import java.util.List;
-
-final class KotlinLambdaVirtualMethodSourceCode extends SyntheticSourceCode {
-  private final DexItemFactory factory;
-  private final DexField idField;
-  private final List<DexEncodedMethod> implMethods;
-  private final DexMethod fallThroughMethod;
-  private final int keyStart;
-
-  KotlinLambdaVirtualMethodSourceCode(
-      DexItemFactory factory,
-      DexType groupClass,
-      DexMethod method,
-      DexField idField,
-      List<DexEncodedMethod> implMethods,
-      DexMethod fallThroughMethod,
-      int keyStart,
-      Position callerPosition) {
-    super(groupClass, method, callerPosition);
-    this.factory = factory;
-    this.idField = idField;
-    this.implMethods = implMethods;
-    this.fallThroughMethod = fallThroughMethod;
-    this.keyStart = keyStart;
-  }
-
-  @Override
-  protected void prepareInstructions() {
-    int implMethodCount = implMethods.size();
-    // We generate a single switch on lambda $id value read from appropriate
-    // field, and for each lambda id generate a call to appropriate method of
-    // the lambda class. Since this methods are marked as 'force inline',
-    // they are inlined by the inliner.
-
-    // Return value register if needed.
-    DexType returnType = proto.returnType;
-    boolean returnsValue = returnType != factory.voidType;
-    ValueType retValueType = returnsValue ? ValueType.fromDexType(returnType) : null;
-    int retRegister = returnsValue ? nextRegister(retValueType) : -1;
-
-    // Lambda id register to switch on.
-    int idRegister = nextRegister(ValueType.INT);
-    add(builder -> builder.addInstanceGet(idRegister, getReceiverRegister(), idField));
-
-    // Switch on id.
-    // Note that 'keys' and 'offsets' are just captured here and filled
-    // in with values when appropriate basic blocks are created.
-    int[] keys = new int[implMethodCount];
-    int[] offsets = new int[implMethodCount];
-    int[] fallthrough = new int[1]; // Array as a container for late initialization.
-    int switchIndex = lastInstructionIndex();
-    add(builder -> builder.addSwitch(idRegister, keys, fallthrough[0], offsets),
-        builder -> endsSwitch(builder, switchIndex, fallthrough[0], offsets));
-
-    List<Value> arguments = new ArrayList<>(proto.parameters.values.length + 1);
-
-    fallthrough[0] = nextInstructionIndex();
-    if (fallThroughMethod == null) {
-      // Fallthrough treated as unreachable.
-      int nullRegister = nextRegister(ValueType.OBJECT);
-      add(builder -> builder.addNullConst(nullRegister));
-      add(builder -> builder.addThrow(nullRegister), endsBlock);
-    } else {
-      addMethodCall(fallThroughMethod, arguments, returnsValue, retRegister);
-    }
-
-    // Blocks for each lambda id.
-    for (int i = 0; i < implMethodCount; i++) {
-      keys[i] = keyStart + i;
-      DexEncodedMethod impl = implMethods.get(i);
-      if (impl == null) {
-        // Virtual method is missing in lambda class.
-        offsets[i] = fallthrough[0];
-        continue;
-      }
-      offsets[i] = nextInstructionIndex();
-      addMethodCall(impl.method, arguments, returnsValue, retRegister);
-    }
-  }
-
-  private void addMethodCall(
-      DexMethod method, List<Value> arguments, boolean returnsValue, int retRegister) {
-    // Emit fake call on `this` receiver.
-    add(
-        builder -> {
-          if (arguments.isEmpty()) {
-            arguments.add(builder.getReceiverValue());
-            List<Value> argumentValues = builder.getArgumentValues();
-            if (argumentValues != null) {
-              arguments.addAll(builder.getArgumentValues());
-            }
-          }
-          builder.addInvoke(
-              Invoke.Type.VIRTUAL, method, method.proto, arguments, false /* isInterface */);
-        });
-    // Handle return value if needed.
-    if (returnsValue) {
-      add(builder -> builder.addMoveResult(retRegister));
-      add(builder -> builder.addReturn(retRegister), endsBlock);
-    } else {
-      add(IRBuilder::addReturn, endsBlock);
-    }
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
index 6b011a6..6fc09f5 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
@@ -7,6 +7,7 @@
 import static com.android.tools.r8.kotlin.KotlinMetadataUtils.NO_KOTLIN_INFO;
 import static com.android.tools.r8.kotlin.KotlinSyntheticClassInfo.getFlavour;
 
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexAnnotationElement;
 import com.android.tools.r8.graph.DexClass;
@@ -35,11 +36,10 @@
       DexClass clazz,
       DexItemFactory factory,
       Reporter reporter,
-      boolean onlyProcessLambda,
       Consumer<DexEncodedMethod> keepByteCode) {
     DexAnnotation meta = clazz.annotations().getFirstMatching(factory.kotlinMetadataType);
     if (meta != null) {
-      return getKotlinInfo(kotlin, clazz, factory, reporter, onlyProcessLambda, keepByteCode, meta);
+      return getKotlinInfo(kotlin, clazz, factory, reporter, keepByteCode, meta);
     }
     return NO_KOTLIN_INFO;
   }
@@ -49,14 +49,10 @@
       DexClass clazz,
       DexItemFactory factory,
       Reporter reporter,
-      boolean onlyProcessLambda,
       Consumer<DexEncodedMethod> keepByteCode,
       DexAnnotation annotation) {
     try {
       KotlinClassMetadata kMetadata = toKotlinClassMetadata(kotlin, annotation.annotation);
-      if (onlyProcessLambda && !isSyntheticClassifiedLambda(kotlin, clazz, kMetadata)) {
-        return NO_KOTLIN_INFO;
-      }
       return createKotlinInfo(kotlin, clazz, kMetadata, factory, reporter, keepByteCode);
     } catch (ClassCastException | InconsistentKotlinMetadataException | MetadataError e) {
       reporter.info(
@@ -77,12 +73,18 @@
     }
   }
 
-  private static boolean isSyntheticClassifiedLambda(
-      Kotlin kotlin, DexClass clazz, KotlinClassMetadata kMetadata) {
-    if (kMetadata instanceof SyntheticClass) {
-      SyntheticClass syntheticClass = (SyntheticClass) kMetadata;
-      return syntheticClass.isLambda()
-          && getFlavour(syntheticClass, clazz, kotlin) != Flavour.Unclassified;
+  public static boolean isLambda(AppView<?> appView, DexClass clazz) {
+    DexItemFactory dexItemFactory = appView.dexItemFactory();
+    Kotlin kotlin = dexItemFactory.kotlin;
+    DexAnnotation metadataAnnotation =
+        clazz.annotations().getFirstMatching(dexItemFactory.kotlinMetadataType);
+    if (metadataAnnotation != null) {
+      KotlinClassMetadata kMetadata = toKotlinClassMetadata(kotlin, metadataAnnotation.annotation);
+      if (kMetadata instanceof SyntheticClass) {
+        SyntheticClass syntheticClass = (SyntheticClass) kMetadata;
+        return syntheticClass.isLambda()
+            && getFlavour(syntheticClass, clazz, kotlin) != Flavour.Unclassified;
+      }
     }
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
index f9d2d18..4eef6cb 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
@@ -18,6 +18,8 @@
 import com.android.tools.r8.graph.EnclosingMethodAttribute;
 import com.android.tools.r8.graph.ProgramDefinition;
 import com.android.tools.r8.graph.analysis.EnqueuerAnalysis;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
 import com.android.tools.r8.shaking.Enqueuer;
 import com.android.tools.r8.shaking.Enqueuer.EnqueuerDefinitionSupplier;
 import com.google.common.collect.Sets;
@@ -25,6 +27,8 @@
 
 public class KotlinMetadataEnqueuerExtension extends EnqueuerAnalysis {
 
+  private static final OptimizationFeedback feedback = OptimizationFeedbackSimple.getInstance();
+
   private final AppView<?> appView;
   private final EnqueuerDefinitionSupplier enqueuerDefinitionSupplier;
   private final Set<DexType> prunedTypes;
@@ -57,23 +61,27 @@
       Set<DexProgramClass> localOrAnonymousClasses = Sets.newIdentityHashSet();
       enqueuer.forAllLiveClasses(
           clazz -> {
-            boolean onlyProcessLambdas = !keepMetadata || !enqueuer.isPinned(clazz.type);
             assert clazz.getKotlinInfo().isNoKotlinInformation();
-            clazz.setKotlinInfo(
-                KotlinClassMetadataReader.getKotlinInfo(
-                    appView.dexItemFactory().kotlin,
-                    clazz,
-                    appView.dexItemFactory(),
-                    appView.options().reporter,
-                    onlyProcessLambdas,
-                    method -> keepByteCodeFunctions.add(method.method)));
-            if (onlyProcessLambdas) {
+            if (!keepMetadata || !enqueuer.isPinned(clazz.getType())) {
+              if (KotlinClassMetadataReader.isLambda(appView, clazz)
+                  && clazz.hasClassInitializer()) {
+                feedback.classInitializerMayBePostponed(clazz.getClassInitializer());
+              }
+              clazz.setKotlinInfo(NO_KOTLIN_INFO);
               clazz.removeAnnotations(
                   annotation -> annotation.getAnnotationType() == kotlinMetadataType);
-            }
-            if (clazz.getEnclosingMethodAttribute() != null
-                && clazz.getEnclosingMethodAttribute().getEnclosingMethod() != null) {
-              localOrAnonymousClasses.add(clazz);
+            } else {
+              clazz.setKotlinInfo(
+                  KotlinClassMetadataReader.getKotlinInfo(
+                      appView.dexItemFactory().kotlin,
+                      clazz,
+                      appView.dexItemFactory(),
+                      appView.options().reporter,
+                      method -> keepByteCodeFunctions.add(method.getReference())));
+              if (clazz.getEnclosingMethodAttribute() != null
+                  && clazz.getEnclosingMethodAttribute().getEnclosingMethod() != null) {
+                localOrAnonymousClasses.add(clazz);
+              }
             }
           });
       appView.setCfByteCodePassThrough(keepByteCodeFunctions);
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
index 1001ca9..44d6ede 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataRewriter.java
@@ -139,7 +139,7 @@
           }
           final KotlinClassLevelInfo kotlinInfo =
               KotlinClassMetadataReader.getKotlinInfo(
-                  kotlin, clazz, factory, reporter, false, ConsumerUtils.emptyConsumer(), metadata);
+                  kotlin, clazz, factory, reporter, ConsumerUtils.emptyConsumer(), metadata);
           if (kotlinInfo == NO_KOTLIN_INFO) {
             return;
           }
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 0d4a768..240d3b9 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -40,7 +40,6 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.classmerging.HorizontallyMergedLambdaClasses;
 import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
 import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
 import com.android.tools.r8.inspector.internal.InspectorImpl;
@@ -50,7 +49,6 @@
 import com.android.tools.r8.ir.desugar.nest.Nest;
 import com.android.tools.r8.ir.optimize.Inliner;
 import com.android.tools.r8.ir.optimize.enums.EnumDataMap;
-import com.android.tools.r8.ir.optimize.lambda.kotlin.KotlinLambdaGroupIdFactory;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.position.Position;
 import com.android.tools.r8.references.Reference;
@@ -201,7 +199,6 @@
     enableClassInlining = false;
     enableClassStaticizer = false;
     enableDevirtualization = false;
-    enableLambdaMerging = false;
     horizontalClassMergerOptions.disable();
     enableVerticalClassMerging = false;
     enableEnumUnboxing = false;
@@ -502,8 +499,6 @@
 
   // Flag to turn on/offLoad/store optimization in the Cf back-end.
   public boolean enableLoadStoreOptimization = true;
-  // Flag to turn on/off lambda class merging in R8.
-  public boolean enableLambdaMerging = false;
   // Flag to turn on/off desugaring in D8/R8.
   public DesugarState desugarState = DesugarState.ON;
   // Flag to turn on/off reduction of nest to improve class merging optimizations.
@@ -1139,20 +1134,17 @@
     public boolean enableConstructorMerging = true;
     // TODO(b/174809311): Update or remove the option and its tests after new lambdas synthetics.
     public boolean enableJavaLambdaMerging = false;
-    public boolean enableKotlinLambdaMerging = true;
 
     public int syntheticArgumentCount = 3;
     public int maxGroupSize = 30;
 
+    // TODO(b/179019716): Add support for merging in presence of annotations.
+    public boolean skipNoClassesOrMembersWithAnnotationsPolicyForTesting = false;
+
     public void disable() {
       enable = false;
     }
 
-    @Deprecated
-    public void disableKotlinLambdaMerging() {
-      enableKotlinLambdaMerging = false;
-    }
-
     public void enable() {
       enable = true;
     }
@@ -1165,10 +1157,6 @@
       enableJavaLambdaMerging = true;
     }
 
-    public void enableKotlinLambdaMergingIf(boolean enableKotlinLambdaMerging) {
-      this.enableKotlinLambdaMerging = enableKotlinLambdaMerging;
-    }
-
     public int getMaxGroupSize() {
       return maxGroupSize;
     }
@@ -1192,10 +1180,6 @@
     public boolean isJavaLambdaMergingEnabled() {
       return enableJavaLambdaMerging;
     }
-
-    public boolean isKotlinLambdaMergingEnabled() {
-      return enableKotlinLambdaMerging;
-    }
   }
 
   public static class ProtoShrinkingOptions {
@@ -1243,9 +1227,6 @@
 
     public BiConsumer<AppInfoWithLiveness, Enqueuer.Mode> enqueuerInspector = null;
 
-    public Function<DexProgramClass, KotlinLambdaGroupIdFactory> kotlinLambdaMergerFactoryForClass =
-        KotlinLambdaGroupIdFactory::getFactoryForClass;
-
     public BiConsumer<ProgramMethod, MethodProcessingId> methodProcessingIdConsumer = null;
 
     public Function<AppView<AppInfoWithLiveness>, RepackagingConfiguration>
@@ -1257,9 +1238,6 @@
     public BiConsumer<DexItemFactory, HorizontallyMergedClasses> horizontallyMergedClassesConsumer =
         ConsumerUtils.emptyBiConsumer();
 
-    public BiConsumer<DexItemFactory, HorizontallyMergedLambdaClasses>
-        horizontallyMergedLambdaClassesConsumer = ConsumerUtils.emptyBiConsumer();
-
     public BiConsumer<DexItemFactory, EnumDataMap> unboxedEnumsConsumer =
         ConsumerUtils.emptyBiConsumer();
 
diff --git a/src/test/java/com/android/tools/r8/KotlinTestBase.java b/src/test/java/com/android/tools/r8/KotlinTestBase.java
index f8dc222..38202a6 100644
--- a/src/test/java/com/android/tools/r8/KotlinTestBase.java
+++ b/src/test/java/com/android/tools/r8/KotlinTestBase.java
@@ -41,10 +41,12 @@
 
   protected final KotlinCompiler kotlinc;
   protected final KotlinTargetVersion targetVersion;
+  protected final KotlinTestParameters kotlinParameters;
 
-  protected KotlinTestBase(KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    this.targetVersion = targetVersion;
-    this.kotlinc = kotlinc;
+  protected KotlinTestBase(KotlinTestParameters kotlinParameters) {
+    this.targetVersion = kotlinParameters.getTargetVersion();
+    this.kotlinc = kotlinParameters.getCompiler();
+    this.kotlinParameters = kotlinParameters;
   }
 
   protected static List<Path> getKotlinFilesInTestPackage(Package pkg) throws IOException {
@@ -125,6 +127,11 @@
       return this;
     }
 
+    public Path getForConfiguration(KotlinTestParameters kotlinParameters) {
+      return getForConfiguration(
+          kotlinParameters.getCompiler(), kotlinParameters.getTargetVersion());
+    }
+
     public Path getForConfiguration(KotlinCompiler compiler, KotlinTargetVersion targetVersion) {
       Map<KotlinTargetVersion, Path> kotlinTargetVersionPathMap = compiledPaths.get(compiler);
       if (kotlinTargetVersionPathMap == null) {
diff --git a/src/test/java/com/android/tools/r8/KotlinTestParameters.java b/src/test/java/com/android/tools/r8/KotlinTestParameters.java
new file mode 100644
index 0000000..69a1cff
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/KotlinTestParameters.java
@@ -0,0 +1,95 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import static org.junit.Assert.assertNotNull;
+
+import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import java.util.ArrayList;
+import java.util.List;
+
+public class KotlinTestParameters {
+
+  private final int index;
+  private final KotlinCompiler kotlinc;
+  private final KotlinTargetVersion targetVersion;
+
+  private KotlinTestParameters(
+      KotlinCompiler kotlinc, KotlinTargetVersion targetVersion, int index) {
+    this.index = index;
+    this.kotlinc = kotlinc;
+    this.targetVersion = targetVersion;
+  }
+
+  public KotlinCompiler getCompiler() {
+    return kotlinc;
+  }
+
+  public KotlinTargetVersion getTargetVersion() {
+    return targetVersion;
+  }
+
+  public boolean isFirst() {
+    return index == 0;
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  @Override
+  public String toString() {
+    return kotlinc + "[target=" + targetVersion + "]";
+  }
+
+  public static class Builder {
+
+    private KotlinCompiler[] compilers;
+    private KotlinTargetVersion[] targetVersions;
+
+    private Builder() {}
+
+    public Builder withAllCompilers() {
+      compilers = ToolHelper.getKotlinCompilers();
+      return this;
+    }
+
+    public Builder withAllCompilersAndTargetVersions() {
+      return withAllCompilers().withAllTargetVersions();
+    }
+
+    public Builder withCompiler(KotlinCompiler compiler) {
+      compilers = new KotlinCompiler[] {compiler};
+      return this;
+    }
+
+    public Builder withAllTargetVersions() {
+      targetVersions = KotlinTargetVersion.values();
+      return this;
+    }
+
+    public Builder withTargetVersion(KotlinTargetVersion targetVersion) {
+      targetVersions = new KotlinTargetVersion[] {targetVersion};
+      return this;
+    }
+
+    public KotlinTestParametersCollection build() {
+      validate();
+      List<KotlinTestParameters> testParameters = new ArrayList<>();
+      int index = 0;
+      for (KotlinCompiler kotlinc : compilers) {
+        for (KotlinTargetVersion targetVersion : targetVersions) {
+          testParameters.add(new KotlinTestParameters(kotlinc, targetVersion, index++));
+        }
+      }
+      return new KotlinTestParametersCollection(testParameters);
+    }
+
+    private void validate() {
+      assertNotNull(compilers);
+      assertNotNull(targetVersions);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/KotlinTestParametersCollection.java b/src/test/java/com/android/tools/r8/KotlinTestParametersCollection.java
new file mode 100644
index 0000000..6851116
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/KotlinTestParametersCollection.java
@@ -0,0 +1,22 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+public class KotlinTestParametersCollection implements Iterable<KotlinTestParameters> {
+
+  private final Collection<KotlinTestParameters> parameters;
+
+  public KotlinTestParametersCollection(Collection<KotlinTestParameters> parameters) {
+    assert parameters != null;
+    this.parameters = parameters;
+  }
+
+  @Override
+  public Iterator<KotlinTestParameters> iterator() {
+    return parameters.iterator();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index b2945dc..fe0aa6c 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -360,8 +360,6 @@
 
   // Actually running Proguard should only be during development.
   private static final boolean RUN_PROGUARD = System.getProperty("run_proguard") != null;
-  // Actually running r8.jar in a forked process.
-  private static final boolean RUN_R8_JAR = System.getProperty("run_r8_jar") != null;
 
   @Rule public ExpectedException thrown = ExpectedException.none();
 
@@ -395,6 +393,10 @@
     return TestParametersBuilder.builder();
   }
 
+  public static KotlinTestParameters.Builder getKotlinTestParameters() {
+    return KotlinTestParameters.builder();
+  }
+
   protected static <S, T, E extends Throwable> Function<S, T> memoizeFunction(
       ThrowingFunction<S, T, E> fn) {
     return CacheBuilder.newBuilder()
@@ -446,13 +448,6 @@
   }
 
   /**
-   * Check if tests should run R8 in a forked process when applicable.
-   */
-  protected boolean isRunR8Jar() {
-    return RUN_R8_JAR;
-  }
-
-  /**
    * Write lines of text to a temporary file.
    *
    * The file will include a line separator after the last line.
@@ -1662,6 +1657,10 @@
     return dexItemFactory.createType(descriptor(clazz));
   }
 
+  public static DexType toDexType(ClassReference classReference, DexItemFactory dexItemFactory) {
+    return dexItemFactory.createType(classReference.getDescriptor());
+  }
+
   public static String binaryName(Class<?> clazz) {
     return DescriptorUtils.getBinaryNameFromJavaType(typeName(clazz));
   }
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index b7ca275..27d643a 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -20,7 +20,6 @@
 import com.android.tools.r8.utils.ThrowingOutputStream;
 import com.android.tools.r8.utils.codeinspector.EnumUnboxingInspector;
 import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
-import com.android.tools.r8.utils.codeinspector.HorizontallyMergedLambdaClassesInspector;
 import com.android.tools.r8.utils.codeinspector.VerticallyMergedClassesInspector;
 import com.google.common.base.Suppliers;
 import java.io.ByteArrayOutputStream;
@@ -124,35 +123,24 @@
   }
 
   public T addHorizontallyMergedClassesInspector(
-      Consumer<HorizontallyMergedClassesInspector> inspector) {
+      ThrowableConsumer<HorizontallyMergedClassesInspector> inspector) {
     return addOptionsModification(
         options ->
             options.testing.horizontallyMergedClassesConsumer =
                 ((dexItemFactory, horizontallyMergedClasses) ->
-                    inspector.accept(
+                    inspector.acceptWithRuntimeException(
                         new HorizontallyMergedClassesInspector(
                             dexItemFactory, horizontallyMergedClasses))));
   }
 
   public T addHorizontallyMergedClassesInspectorIf(
-      boolean condition, Consumer<HorizontallyMergedClassesInspector> inspector) {
+      boolean condition, ThrowableConsumer<HorizontallyMergedClassesInspector> inspector) {
     if (condition) {
       return addHorizontallyMergedClassesInspector(inspector);
     }
     return self();
   }
 
-  public T addHorizontallyMergedLambdaClassesInspector(
-      Consumer<HorizontallyMergedLambdaClassesInspector> inspector) {
-    return addOptionsModification(
-        options ->
-            options.testing.horizontallyMergedLambdaClassesConsumer =
-                ((dexItemFactory, horizontallyMergedLambdaClasses) ->
-                    inspector.accept(
-                        new HorizontallyMergedLambdaClassesInspector(
-                            dexItemFactory, horizontallyMergedLambdaClasses))));
-  }
-
   public T addVerticallyMergedClassesInspector(
       Consumer<VerticallyMergedClassesInspector> inspector) {
     return addOptionsModification(
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index 2216a2c..1629285 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -351,6 +351,10 @@
   }
 
   public T addKeepAttributes(String... attributes) {
+    return addKeepAttributes(Arrays.asList(attributes));
+  }
+
+  public T addKeepAttributes(List<String> attributes) {
     return addKeepRules("-keepattributes " + String.join(",", attributes));
   }
 
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
index f878fd5..4d62a05 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
@@ -36,9 +36,7 @@
             options ->
                 options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .addHorizontallyMergedClassesInspectorIf(
-            enableHorizontalClassMerging,
-            inspector ->
-                inspector.assertMerged(A.class, B.class).assertMergedIntoDifferentType(B.class))
+            enableHorizontalClassMerging, inspector -> inspector.assertMergedInto(B.class, A.class))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessOnMergedClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessOnMergedClassTest.java
index ce0425e..029035e 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessOnMergedClassTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessOnMergedClassTest.java
@@ -28,12 +28,8 @@
         .addOptionsModification(
             options ->
                 options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
-        .addHorizontallyMergedClassesInspector(
-            inspector -> {
-              if (enableHorizontalClassMerging) {
-                inspector.assertMerged(C.class, D.class).assertMergedIntoDifferentType(D.class);
-              }
-            })
+        .addHorizontallyMergedClassesInspectorIf(
+            enableHorizontalClassMerging, inspector -> inspector.assertMergedInto(D.class, C.class))
         .enableNeverClassInliningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java
index 79aae15..03b99e0 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java
@@ -28,13 +28,9 @@
         .addOptionsModification(
             options ->
                 options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
-        .addHorizontallyMergedClassesInspector(
-            inspector -> {
-              if (enableHorizontalClassMerging) {
-                inspector.assertMerged(HelloGreeting.class, WorldGreeting.class);
-                inspector.assertMergedIntoDifferentType(WorldGreeting.class);
-              }
-            })
+        .addHorizontallyMergedClassesInspectorIf(
+            enableHorizontalClassMerging,
+            inspector -> inspector.assertMergedInto(WorldGreeting.class, HelloGreeting.class))
         .enableNeverClassInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
         .enableNoVerticalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java b/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java
index ff841d7..86e11d0 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java
@@ -29,7 +29,7 @@
   @Parameters(name = "{0}, kotlinc: {1}")
   public static List<Object[]> setup() {
     return buildParameters(
-        TestParametersBuilder.builder().withAllRuntimes().withAllApiLevels().build(),
+        TestParametersBuilder.builder().withAllRuntimesAndApiLevels().build(),
         getKotlinCompilers());
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java
index aa5393a..55d4763 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.desugar.desugaredlibrary.kotlin;
 
 import static com.android.tools.r8.KotlinTestBase.getCompileMemoizer;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.equalTo;
@@ -16,14 +15,13 @@
 
 import com.android.tools.r8.D8TestRunResult;
 import com.android.tools.r8.DexIndexedConsumer.ArchiveConsumer;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
 import com.android.tools.r8.KotlinTestBase.KotlinCompileMemoizer;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.R8FullTestBuilder;
 import com.android.tools.r8.R8TestCompileResult;
 import com.android.tools.r8.R8TestRunResult;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.kotlin.KotlinMetadataWriter;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
@@ -45,30 +43,27 @@
 public class KotlinMetadataTest extends DesugaredLibraryTestBase {
 
   private static final String PKG = KotlinMetadataTest.class.getPackage().getName();
-  private final TestParameters parameters;
-  private final boolean shrinkDesugaredLibrary;
-  private final KotlinTargetVersion targetVersion;
-  private final KotlinCompiler kotlinCompiler;
   private static final String EXPECTED_OUTPUT = "Wuhuu, my special day is: 1997-8-29-2-14";
 
-  @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}, target: {2}, kotlinc: {3}")
+  private final TestParameters parameters;
+  private final KotlinTestParameters kotlinParameters;
+  private final boolean shrinkDesugaredLibrary;
+
+  @Parameters(name = "{0}, {1}, shrinkDesugaredLibrary: {2}")
   public static List<Object[]> data() {
     return buildParameters(
-        BooleanUtils.values(),
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
+        BooleanUtils.values());
   }
 
   public KotlinMetadataTest(
-      boolean shrinkDesugaredLibrary,
       TestParameters parameters,
-      KotlinTargetVersion targetVersion,
-      KotlinCompiler kotlinCompiler) {
-    this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+      KotlinTestParameters kotlinParameters,
+      boolean shrinkDesugaredLibrary) {
     this.parameters = parameters;
-    this.targetVersion = targetVersion;
-    this.kotlinCompiler = kotlinCompiler;
+    this.kotlinParameters = kotlinParameters;
+    this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
   }
 
   private static KotlinCompileMemoizer compiledJars =
@@ -83,9 +78,9 @@
   public void testCf() throws Exception {
     assumeTrue(parameters.getRuntime().isCf());
     testForRuntime(parameters)
-        .addProgramFiles(compiledJars.getForConfiguration(kotlinCompiler, targetVersion))
-        .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinCompiler))
-        .addProgramFiles(ToolHelper.getKotlinReflectJar(kotlinCompiler))
+        .addProgramFiles(compiledJars.getForConfiguration(kotlinParameters))
+        .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinParameters.getCompiler()))
+        .addProgramFiles(ToolHelper.getKotlinReflectJar(kotlinParameters.getCompiler()))
         .run(parameters.getRuntime(), PKG + ".MainKt")
         .assertSuccessWithOutputLines(EXPECTED_OUTPUT);
   }
@@ -97,9 +92,9 @@
     final File output = temp.newFile("output.zip");
     final D8TestRunResult d8TestRunResult =
         testForD8()
-            .addProgramFiles(compiledJars.getForConfiguration(kotlinCompiler, targetVersion))
-            .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinCompiler))
-            .addProgramFiles(ToolHelper.getKotlinReflectJar(kotlinCompiler))
+            .addProgramFiles(compiledJars.getForConfiguration(kotlinParameters))
+            .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinParameters.getCompiler()))
+            .addProgramFiles(ToolHelper.getKotlinReflectJar(kotlinParameters.getCompiler()))
             .setProgramConsumer(new ArchiveConsumer(output.toPath(), true))
             .setMinApi(parameters.getApiLevel())
             .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
@@ -126,9 +121,9 @@
     boolean desugarLibrary = parameters.isDexRuntime() && requiresAnyCoreLibDesugaring(parameters);
     final R8FullTestBuilder testBuilder =
         testForR8(parameters.getBackend())
-            .addProgramFiles(compiledJars.getForConfiguration(kotlinCompiler, targetVersion))
-            .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinCompiler))
-            .addProgramFiles(ToolHelper.getKotlinReflectJar(kotlinCompiler))
+            .addProgramFiles(compiledJars.getForConfiguration(kotlinParameters))
+            .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinParameters.getCompiler()))
+            .addProgramFiles(ToolHelper.getKotlinReflectJar(kotlinParameters.getCompiler()))
             .addKeepMainRule(PKG + ".MainKt")
             .addKeepAllClassesRule()
             .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/mergedcontext/MergedContextTest.java b/src/test/java/com/android/tools/r8/desugar/lambdas/mergedcontext/MergedContextTest.java
index 35e31d5..86d541e 100644
--- a/src/test/java/com/android/tools/r8/desugar/lambdas/mergedcontext/MergedContextTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/lambdas/mergedcontext/MergedContextTest.java
@@ -42,7 +42,7 @@
         .setMinApi(parameters.getApiLevel())
         .addHorizontallyMergedClassesInspector(
             inspector -> {
-              inspector.assertClassNotMerged(C.class);
+              inspector.assertClassesNotMerged(C.class);
               inspector.assertMergedInto(B.class, A.class);
             })
         .run(parameters.getRuntime(), TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/kotlin/SimpleKotlinEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/kotlin/SimpleKotlinEnumUnboxingTest.java
index 3c48865..6f4b565 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/kotlin/SimpleKotlinEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/kotlin/SimpleKotlinEnumUnboxingTest.java
@@ -6,15 +6,13 @@
 
 import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
 import static com.android.tools.r8.KotlinTestBase.getCompileMemoizer;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static org.hamcrest.core.StringContains.containsString;
 import static org.junit.Assume.assumeTrue;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
 import com.android.tools.r8.KotlinTestBase.KotlinCompileMemoizer;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.enumunboxing.EnumUnboxingTestBase;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.DescriptorUtils;
@@ -29,10 +27,9 @@
 public class SimpleKotlinEnumUnboxingTest extends EnumUnboxingTestBase {
 
   private final TestParameters parameters;
+  private final KotlinTestParameters kotlinParameters;
   private final boolean enumValueOptimization;
   private final EnumKeepRules enumKeepRules;
-  private final KotlinTargetVersion targetVersion;
-  private final KotlinCompiler kotlinCompiler;
 
   private static final String PKG = SimpleKotlinEnumUnboxingTest.class.getPackage().getName();
   private static final KotlinCompileMemoizer jars =
@@ -43,27 +40,24 @@
               DescriptorUtils.getBinaryNameFromJavaType(PKG),
               "Main.kt"));
 
-  @Parameters(name = "{0}, valueOpt: {1}, keep: {2}, kotlin targetVersion: {3}, kotlinc: {4}")
+  @Parameters(name = "{0}, {1}, valueOpt: {2}, keep: {3}")
   public static List<Object[]> enumUnboxingTestParameters() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
         BooleanUtils.values(),
-        getAllEnumKeepRules(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getAllEnumKeepRules());
   }
 
   public SimpleKotlinEnumUnboxingTest(
       TestParameters parameters,
+      KotlinTestParameters kotlinParameters,
       boolean enumValueOptimization,
-      EnumKeepRules enumKeepRules,
-      KotlinTargetVersion targetVersion,
-      KotlinCompiler kotlinCompiler) {
+      EnumKeepRules enumKeepRules) {
     this.parameters = parameters;
+    this.kotlinParameters = kotlinParameters;
     this.enumValueOptimization = enumValueOptimization;
     this.enumKeepRules = enumKeepRules;
-    this.targetVersion = targetVersion;
-    this.kotlinCompiler = kotlinCompiler;
   }
 
   @Test
@@ -71,8 +65,8 @@
     assumeTrue(parameters.isDexRuntime());
     testForR8(parameters.getBackend())
         .addProgramFiles(
-            jars.getForConfiguration(kotlinCompiler, targetVersion),
-            ToolHelper.getKotlinStdlibJar(kotlinCompiler))
+            jars.getForConfiguration(kotlinParameters),
+            ToolHelper.getKotlinStdlibJar(kotlinParameters.getCompiler()))
         .addKeepMainRule(PKG + ".MainKt")
         .addKeepRules(enumKeepRules.getKeepRules())
         .addKeepRuntimeVisibleAnnotations()
diff --git a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
index 0abfe16..0fb02d7 100644
--- a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
+++ b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
@@ -14,19 +14,17 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
 import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.R8FullTestBuilder;
 import com.android.tools.r8.R8TestRunResult;
 import com.android.tools.r8.ThrowableConsumer;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DexCode;
 import com.android.tools.r8.jasmin.JasminBuilder;
 import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
-import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -52,16 +50,13 @@
   private final List<Path> extraClasspath = new ArrayList<>();
 
   // Some tests defined in subclasses, e.g., Metadata tests, don't care about access relaxation.
-  protected AbstractR8KotlinTestBase(
-      KotlinTargetVersion kotlinTargetVersion, KotlinCompiler kotlinc) {
-    this(kotlinTargetVersion, kotlinc, false);
+  protected AbstractR8KotlinTestBase(KotlinTestParameters kotlinParameters) {
+    this(kotlinParameters, false);
   }
 
   protected AbstractR8KotlinTestBase(
-      KotlinTargetVersion kotlinTargetVersion,
-      KotlinCompiler kotlinc,
-      boolean allowAccessModification) {
-    super(kotlinTargetVersion, kotlinc);
+      KotlinTestParameters kotlinParameters, boolean allowAccessModification) {
+    super(kotlinParameters);
     this.allowAccessModification = allowAccessModification;
   }
 
@@ -142,15 +137,6 @@
     return fieldSubject;
   }
 
-  protected void checkFieldIsRemoved(
-      ClassSubject classSubject, String fieldType, String fieldName) {
-    // Field must exist in the input.
-    checkFieldPresenceInInput(classSubject.getOriginalName(), fieldType, fieldName, true);
-    FieldSubject fieldSubject = classSubject.field(fieldType, fieldName);
-    assertNotNull(fieldSubject);
-    assertThat(fieldSubject, not(isPresent()));
-  }
-
   protected void checkFieldIsAbsent(ClassSubject classSubject, String fieldType, String fieldName) {
     // Field must NOT exist in the input.
     checkFieldPresenceInInput(classSubject.getOriginalName(), fieldType, fieldName, false);
@@ -159,12 +145,6 @@
     assertFalse(fieldSubject.isPresent());
   }
 
-  protected FieldSubject checkFieldIsAbsent(ClassSubject classSubject, String fieldName) {
-    FieldSubject fieldSubject = classSubject.uniqueFieldWithName(fieldName);
-    assertThat(fieldSubject, not(isPresent()));
-    return fieldSubject;
-  }
-
   protected void checkMethodIsAbsent(ClassSubject classSubject, MethodSignature methodSignature) {
     checkMethodPresenceInInput(classSubject.getOriginalName(), methodSignature, false);
     checkMethodPresenceInOutput(classSubject, methodSignature, false);
@@ -187,11 +167,6 @@
     checkMethodIsKeptOrRemoved(classSubject, methodSignature, false);
   }
 
-  protected void checkMethodIsRemoved(ClassSubject classSubject, String methodName) {
-    MethodSubject methodSubject = classSubject.uniqueMethodWithName(methodName);
-    assertThat(methodSubject, not(isPresent()));
-  }
-
   protected MethodSubject checkMethodIsKeptOrRemoved(
       ClassSubject classSubject, MethodSignature methodSignature, boolean isPresent) {
     checkMethodPresenceInInput(classSubject.getOriginalName(), methodSignature, true);
@@ -266,14 +241,13 @@
         .addProgramFiles(classpath)
         .addKeepMainRule(mainClass)
         .allowAccessModification(allowAccessModification)
-        .allowDiagnosticMessages()
+        .allowDiagnosticWarningMessages()
         .enableProguardTestOptions()
         .noMinification()
         .apply(configuration)
         .compile()
         .assertAllWarningMessagesMatch(
             containsString("Resource 'META-INF/MANIFEST.MF' already exists."))
-        .assertAllInfoMessagesMatch(containsString("Unrecognized Kotlin lambda "))
         .run(mainClass)
         .assertSuccessWithOutput(javaResult.stdout);
   }
@@ -308,12 +282,6 @@
     }
   }
 
-  @FunctionalInterface
-  public interface AndroidAppInspector {
-
-    void inspectApp(AndroidApp androidApp) throws Exception;
-  }
-
   /**
    * Generates a "main" class which invokes the given static method (which has no argument and
    * return void type). This new class is then added to the test classpath.
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
index e4622be..608b92b 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -4,28 +4,20 @@
 
 package com.android.tools.r8.kotlin;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeTrue;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
-import com.android.tools.r8.R8FullTestBuilder;
-import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.KotlinTestParametersCollection;
 import com.android.tools.r8.TestShrinkerBuilder;
-import com.android.tools.r8.ThrowableConsumer;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.code.NewInstance;
 import com.android.tools.r8.code.SgetObject;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexCode;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
-import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -33,7 +25,6 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 import com.google.common.collect.Streams;
-import java.util.Collection;
 import java.util.List;
 import java.util.Set;
 import java.util.function.Predicate;
@@ -46,29 +37,27 @@
 @RunWith(Parameterized.class)
 public class KotlinClassInlinerTest extends AbstractR8KotlinTestBase {
 
-  @Parameterized.Parameters(name = "target: {0}, kotlinc: {1}, allowAccessModification: {2}")
-  public static Collection<Object[]> data() {
-    return buildParameters(
-        KotlinTargetVersion.values(), getKotlinCompilers(), BooleanUtils.values());
+  @Parameterized.Parameters(name = "{0}")
+  public static KotlinTestParametersCollection data() {
+    return getKotlinTestParameters().withAllCompilersAndTargetVersions().build();
   }
 
-  public KotlinClassInlinerTest(
-      KotlinTargetVersion targetVersion, KotlinCompiler kotlinc, boolean allowAccessModification) {
-    super(targetVersion, kotlinc, allowAccessModification);
+  public KotlinClassInlinerTest(KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters, true);
   }
 
   private static boolean isLambda(DexClass clazz) {
-    return !clazz.type.getPackageDescriptor().startsWith("kotlin") &&
-        (isKStyleLambdaOrGroup(clazz) || isJStyleLambdaOrGroup(clazz));
+    return !clazz.getType().getPackageDescriptor().startsWith("kotlin")
+        && (isKStyleLambda(clazz) || isJStyleLambda(clazz));
   }
 
-  private static boolean isKStyleLambdaOrGroup(DexClass clazz) {
-    return clazz.superType.descriptor.toString().equals("Lkotlin/jvm/internal/Lambda;");
+  private static boolean isKStyleLambda(DexClass clazz) {
+    return clazz.getSuperType().getTypeName().equals("kotlin.jvm.internal.Lambda");
   }
 
-  private static boolean isJStyleLambdaOrGroup(DexClass clazz) {
-    return clazz.superType.descriptor.toString().equals("Ljava/lang/Object;") &&
-        clazz.interfaces.size() == 1;
+  private static boolean isJStyleLambda(DexClass clazz) {
+    return clazz.getSuperType().getTypeName().equals(Object.class.getTypeName())
+        && clazz.getInterfaces().size() == 1;
   }
 
   private static Predicate<DexType> createLambdaCheck(CodeInspector inspector) {
@@ -82,9 +71,8 @@
 
   @Test
   public void testJStyleLambdas() throws Exception {
-    assumeTrue("Only work with -allowaccessmodification", allowAccessModification);
-    final String mainClassName = "class_inliner_lambda_j_style.MainKt";
-    runTestWithDefaults(
+    String mainClassName = "class_inliner_lambda_j_style.MainKt";
+    runTest(
             "class_inliner_lambda_j_style",
             mainClassName,
             testBuilder ->
@@ -92,21 +80,32 @@
                     // TODO(jsjeon): Introduce @NeverInline to kotlinR8TestResources
                     .addKeepRules("-neverinline class * { void test*State*(...); }")
                     .addDontWarnJetBrainsNotNullAnnotation()
+                    .addHorizontallyMergedClassesInspector(
+                        inspector ->
+                            inspector
+                                .assertIsCompleteMergeGroup(
+                                    "class_inliner_lambda_j_style.MainKt$testStateless$1",
+                                    "class_inliner_lambda_j_style.MainKt$testStateless$2",
+                                    "class_inliner_lambda_j_style.MainKt$testStateless$3")
+                                .assertIsCompleteMergeGroup(
+                                    "class_inliner_lambda_j_style.MainKt$testStateful$1",
+                                    "class_inliner_lambda_j_style.MainKt$testStateful$2",
+                                    "class_inliner_lambda_j_style.MainKt$testStateful$2$1",
+                                    "class_inliner_lambda_j_style.MainKt$testStateful$3",
+                                    "class_inliner_lambda_j_style.MainKt$testStateful2$1",
+                                    "class_inliner_lambda_j_style.MainKt$testStateful3$1"))
                     .noClassInlining())
         .inspect(
             inspector -> {
               assertThat(
+                  inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateless$1"),
+                  isPresent());
+              assertThat(
                   inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateful$1"),
                   isPresent());
-              assertThat(
-                  inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateful2$1"),
-                  isPresent());
-              assertThat(
-                  inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateful3$1"),
-                  isPresent());
             });
 
-    runTestWithDefaults(
+    runTest(
             "class_inliner_lambda_j_style",
             mainClassName,
             testBuilder ->
@@ -116,40 +115,22 @@
                     .addDontWarnJetBrainsNotNullAnnotation())
         .inspect(
             inspector -> {
-              Predicate<DexType> lambdaCheck = createLambdaCheck(inspector);
-              ClassSubject clazz = inspector.clazz(mainClassName);
+              // TODO(b/173337498): MainKt$testStateless$1 should be class inlined.
+              assertThat(
+                  inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateless$1"),
+                  isPresent());
 
-              assertEquals(
-                  Sets.newHashSet(), collectAccessedTypes(lambdaCheck, clazz, "testStateless"));
-
-              assertEquals(
-                  Sets.newHashSet(), collectAccessedTypes(lambdaCheck, clazz, "testStateful"));
-
+              // TODO(b/173337498): MainKt$testStateful$1 should be class inlined.
               assertThat(
                   inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateful$1"),
-                  not(isPresent()));
-
-              assertEquals(
-                  Sets.newHashSet(), collectAccessedTypes(lambdaCheck, clazz, "testStateful2"));
-
-              assertThat(
-                  inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateful2$1"),
-                  not(isPresent()));
-
-              assertEquals(
-                  Sets.newHashSet(), collectAccessedTypes(lambdaCheck, clazz, "testStateful3"));
-
-              assertThat(
-                  inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateful3$1"),
-                  not(isPresent()));
+                  isPresent());
             });
   }
 
   @Test
   public void testKStyleLambdas() throws Exception {
-    assumeTrue("Only work with -allowaccessmodification", allowAccessModification);
-    final String mainClassName = "class_inliner_lambda_k_style.MainKt";
-    runTestWithDefaults(
+    String mainClassName = "class_inliner_lambda_k_style.MainKt";
+    runTest(
             "class_inliner_lambda_k_style",
             mainClassName,
             testBuilder ->
@@ -160,6 +141,15 @@
                         "-neverinline class * { void testBigExtraMethod(...); }",
                         "-neverinline class * { void testBigExtraMethodReturningLambda(...); }")
                     .addDontWarnJetBrainsAnnotations()
+                    .addHorizontallyMergedClassesInspector(
+                        inspector ->
+                            inspector.assertIsCompleteMergeGroup(
+                                "class_inliner_lambda_k_style.MainKt$testBigExtraMethod$1",
+                                "class_inliner_lambda_k_style.MainKt$testBigExtraMethod2$1",
+                                "class_inliner_lambda_k_style.MainKt$testBigExtraMethod3$1",
+                                "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda$1",
+                                "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda2$1",
+                                "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda3$1"))
                     .noClassInlining())
         .inspect(
             inspector -> {
@@ -174,27 +164,9 @@
               assertThat(
                   inspector.clazz("class_inliner_lambda_k_style.MainKt$testBigExtraMethod$1"),
                   isPresent());
-              assertThat(
-                  inspector.clazz("class_inliner_lambda_k_style.MainKt$testBigExtraMethod2$1"),
-                  isPresent());
-              assertThat(
-                  inspector.clazz("class_inliner_lambda_k_style.MainKt$testBigExtraMethod3$1"),
-                  isPresent());
-              assertThat(
-                  inspector.clazz(
-                      "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda$1"),
-                  isPresent());
-              assertThat(
-                  inspector.clazz(
-                      "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda2$1"),
-                  isPresent());
-              assertThat(
-                  inspector.clazz(
-                      "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda3$1"),
-                  isPresent());
             });
 
-    runTestWithDefaults(
+    runTest(
             "class_inliner_lambda_k_style",
             mainClassName,
             testBuilder ->
@@ -207,20 +179,6 @@
                     .addDontWarnJetBrainsAnnotations())
         .inspect(
             inspector -> {
-              Predicate<DexType> lambdaCheck = createLambdaCheck(inspector);
-              ClassSubject clazz = inspector.clazz(mainClassName);
-
-              // TODO(b/173337498): Should be empty, but horizontal class merging interferes with
-              //  class inlining.
-              assertEquals(
-                  Sets.newHashSet(
-                      "class_inliner_lambda_k_style.MainKt$testKotlinSequencesStateless$1"),
-                  collectAccessedTypes(
-                      lambdaCheck,
-                      clazz,
-                      "testKotlinSequencesStateless",
-                      "kotlin.sequences.Sequence"));
-
               // TODO(b/173337498): Should be absent, but horizontal class merging interferes with
               //  class inlining.
               assertThat(
@@ -228,19 +186,6 @@
                       "class_inliner_lambda_k_style.MainKt$testKotlinSequencesStateless$1"),
                   isPresent());
 
-              // TODO(b/173337498): Should be empty, but horizontal class merging interferes with
-              //  class inlining.
-              assertEquals(
-                  Sets.newHashSet(
-                      "class_inliner_lambda_k_style.MainKt$testKotlinSequencesStateful$1"),
-                  collectAccessedTypes(
-                      lambdaCheck,
-                      clazz,
-                      "testKotlinSequencesStateful",
-                      "int",
-                      "int",
-                      "kotlin.sequences.Sequence"));
-
               // TODO(b/173337498): Should be absent, but horizontal class merging interferes with
               //  class inlining.
               assertThat(
@@ -248,57 +193,33 @@
                       "class_inliner_lambda_k_style.MainKt$testKotlinSequencesStateful$1"),
                   isPresent());
 
-              assertEquals(
-                  Sets.newHashSet(),
-                  collectAccessedTypes(lambdaCheck, clazz, "testBigExtraMethod"));
-
+              // TODO(b/173337498): Should be absent, but horizontal class merging interferes with
+              //  class inlining.
               assertThat(
                   inspector.clazz("class_inliner_lambda_k_style.MainKt$testBigExtraMethod$1"),
-                  not(isPresent()));
-              assertThat(
-                  inspector.clazz("class_inliner_lambda_k_style.MainKt$testBigExtraMethod2$1"),
-                  not(isPresent()));
-              assertThat(
-                  inspector.clazz("class_inliner_lambda_k_style.MainKt$testBigExtraMethod3$1"),
-                  not(isPresent()));
-
-              assertEquals(
-                  Sets.newHashSet(),
-                  collectAccessedTypes(lambdaCheck, clazz, "testBigExtraMethodReturningLambda"));
-
-              assertThat(
-                  inspector.clazz(
-                      "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda$1"),
-                  not(isPresent()));
-              assertThat(
-                  inspector.clazz(
-                      "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda2$1"),
-                  not(isPresent()));
-              assertThat(
-                  inspector.clazz(
-                      "class_inliner_lambda_k_style.MainKt$testBigExtraMethodReturningLambda3$1"),
-                  not(isPresent()));
+                  isPresent());
             });
   }
 
   @Test
   public void testDataClass() throws Exception {
-    assumeTrue("Only work with -allowaccessmodification", allowAccessModification);
-    final String mainClassName = "class_inliner_data_class.MainKt";
-    runTestWithDefaults(
+    String mainClassName = "class_inliner_data_class.MainKt";
+    runTest(
             "class_inliner_data_class",
             mainClassName,
             TestShrinkerBuilder::addDontWarnJetBrainsAnnotations)
         .inspect(
             inspector -> {
               ClassSubject clazz = inspector.clazz(mainClassName);
-              assertTrue(
+
+              // TODO(b/141719453): Data class should maybe be class inlined.
+              assertEquals(
+                  Sets.newHashSet("class_inliner_data_class.Alpha"),
                   collectAccessedTypes(
-                          type -> !type.toSourceString().startsWith("java."),
-                          clazz,
-                          "main",
-                          String[].class.getCanonicalName())
-                      .isEmpty());
+                      type -> !type.toSourceString().startsWith("java."),
+                      clazz,
+                      "main",
+                      String[].class.getCanonicalName()));
               assertEquals(
                   Lists.newArrayList(
                       "void kotlin.jvm.internal.Intrinsics.throwParameterIsNullException(java.lang.String)"),
@@ -325,39 +246,6 @@
         .collect(Collectors.toSet());
   }
 
-  private R8TestRunResult runTestWithDefaults(String folder, String mainClass) throws Exception {
-    return runTestWithDefaults(folder, mainClass, null);
-  }
-
-  private R8TestRunResult runTestWithDefaults(
-      String folder, String mainClass, ThrowableConsumer<R8FullTestBuilder> configuration)
-      throws Exception {
-    return runTest(
-        folder,
-        mainClass,
-        testBuilder ->
-            testBuilder
-                .addOptionsModification(
-                    options -> {
-                      options.enableInlining = true;
-                      options.enableLambdaMerging = false;
-
-                      // TODO(b/141719453): These limits should be removed if a possible or the test
-                      //  refactored. Tests check if specific lambdas are inlined or not, where some
-                      //  of target lambdas have at least 4 instructions.
-                      options.inliningInstructionLimit = 4;
-                      options.classInliningInstructionLimit = 40;
-
-                      // Class inlining depends on the processing order. We therefore insert all
-                      // call graph edges and verify that we can class inline everything under this
-                      // condition.
-                      options.testing.addCallEdgesForLibraryInvokes = true;
-
-                      options.horizontalClassMergerOptions().disableKotlinLambdaMerging();
-                    })
-                .apply(configuration));
-  }
-
   private List<String> collectStaticCalls(ClassSubject clazz, String methodName, String... params) {
     assertNotNull(clazz);
     MethodSignature signature = new MethodSignature(methodName, "void", params);
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
index e5d0d09..1598bb8 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
@@ -4,16 +4,14 @@
 
 package com.android.tools.r8.kotlin;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeTrue;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.R8TestRunResult;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
@@ -26,15 +24,16 @@
 @RunWith(Parameterized.class)
 public class KotlinClassStaticizerTest extends AbstractR8KotlinTestBase {
 
-  @Parameterized.Parameters(name = "target: {0}, kotlinc: {1}, allowAccessModification: {2}")
+  @Parameterized.Parameters(name = "{0}, allowAccessModification: {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
-        KotlinTargetVersion.values(), getKotlinCompilers(), BooleanUtils.values());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
+        BooleanUtils.values());
   }
 
   public KotlinClassStaticizerTest(
-      KotlinTargetVersion targetVersion, KotlinCompiler kotlinc, boolean allowAccessModification) {
-    super(targetVersion, kotlinc, allowAccessModification);
+      KotlinTestParameters kotlinParameters, boolean allowAccessModification) {
+    super(kotlinParameters, allowAccessModification);
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java
index 7407d46..e74d082 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java
@@ -3,16 +3,14 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeTrue;
 
 import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.StringUtils;
@@ -36,12 +34,11 @@
       o.enableInlining = false;
     };
 
-  @Parameterized.Parameters(name = "{0} target: {1}, kotlinc: {2}, allowAccessModification: {3}")
+  @Parameterized.Parameters(name = "{0}, {1}, allowAccessModification: {2}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers(),
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
         BooleanUtils.values());
   }
 
@@ -49,10 +46,9 @@
 
   public KotlinDuplicateAnnotationTest(
       TestParameters parameters,
-      KotlinTargetVersion targetVersion,
-      KotlinCompiler kotlinc,
+      KotlinTestParameters kotlinParameters,
       boolean allowAccessModification) {
-    super(targetVersion, kotlinc, allowAccessModification);
+    super(kotlinParameters, allowAccessModification);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java
index eb1ca57..dae8a7d 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java
@@ -3,15 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assume.assumeTrue;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -26,12 +24,11 @@
   private static final String FOLDER = "intrinsics";
   private static final String MAIN = FOLDER + ".InlineKt";
 
-  @Parameterized.Parameters(name = "{0} target: {1}, kotlinc: {2}, allowAccessModification: {3}")
+  @Parameterized.Parameters(name = "{0}, {1}, allowAccessModification: {2}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers(),
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
         BooleanUtils.values());
   }
 
@@ -39,10 +36,9 @@
 
   public KotlinIntrinsicsInlineTest(
       TestParameters parameters,
-      KotlinTargetVersion targetVersion,
-      KotlinCompiler kotlinc,
+      KotlinTestParameters kotlinParameters,
       boolean allowAccessModification) {
-    super(targetVersion, kotlinc, allowAccessModification);
+    super(kotlinParameters, allowAccessModification);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedArgumentsInLambdasTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedArgumentsInLambdasTest.java
index 5e25b92..fa3b505 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedArgumentsInLambdasTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedArgumentsInLambdasTest.java
@@ -3,15 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestShrinkerBuilder;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import com.google.common.collect.ImmutableList;
@@ -23,15 +21,16 @@
 @RunWith(Parameterized.class)
 public class KotlinUnusedArgumentsInLambdasTest extends AbstractR8KotlinTestBase {
 
-  @Parameterized.Parameters(name = "target: {0}, kotlinc: {1}, allowAccessModification: {2}")
+  @Parameterized.Parameters(name = "{0}, allowAccessModification: {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
-        KotlinTargetVersion.values(), getKotlinCompilers(), BooleanUtils.values());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
+        BooleanUtils.values());
   }
 
   public KotlinUnusedArgumentsInLambdasTest(
-      KotlinTargetVersion targetVersion, KotlinCompiler kotlinc, boolean allowAccessModification) {
-    super(targetVersion, kotlinc, allowAccessModification);
+      KotlinTestParameters kotlinParameters, boolean allowAccessModification) {
+    super(kotlinParameters, allowAccessModification);
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedSingletonTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedSingletonTest.java
index 6a8e196..226fb8b 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedSingletonTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedSingletonTest.java
@@ -3,16 +3,14 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestShrinkerBuilder;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.FieldSubject;
@@ -28,18 +26,19 @@
 @RunWith(Parameterized.class)
 public class KotlinUnusedSingletonTest extends AbstractR8KotlinTestBase {
 
-  @Parameterized.Parameters(name = "target: {0}, kotlinc: {1}, allowAccessModification: {1}")
+  @Parameterized.Parameters(name = "{0}, allowAccessModification: {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
-        KotlinTargetVersion.values(), getKotlinCompilers(), BooleanUtils.values());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
+        BooleanUtils.values());
   }
 
   private static final String printlnSignature =
       "void java.io.PrintStream.println(java.lang.Object)";
 
   public KotlinUnusedSingletonTest(
-      KotlinTargetVersion targetVersion, KotlinCompiler kotlinc, boolean allowAccessModification) {
-    super(targetVersion, kotlinc, allowAccessModification);
+      KotlinTestParameters kotlinParameters, boolean allowAccessModification) {
+    super(kotlinParameters, allowAccessModification);
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java
index 3137d61..9f92a95 100644
--- a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java
@@ -3,16 +3,14 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
 import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.R8FullTestBuilder;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestShrinkerBuilder;
 import com.android.tools.r8.ThrowableConsumer;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import java.util.Collection;
 import org.junit.Test;
@@ -24,17 +22,16 @@
 
   private final TestParameters parameters;
 
-  @Parameterized.Parameters(name = "{0} target: {1}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public ProcessKotlinReflectionLibTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java
index 7166aed..c2c68e4 100644
--- a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java
@@ -3,16 +3,14 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
 import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.R8FullTestBuilder;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestShrinkerBuilder;
 import com.android.tools.r8.ThrowableConsumer;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.google.common.collect.ImmutableList;
 import java.util.Collection;
@@ -24,17 +22,15 @@
 public class ProcessKotlinStdlibTest extends KotlinTestBase {
   private final TestParameters parameters;
 
-  @Parameterized.Parameters(name = "{0} target: {1}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
-  public ProcessKotlinStdlibTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+  public ProcessKotlinStdlibTest(TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
@@ -71,22 +67,6 @@
   }
 
   @Test
-  public void testDontShrinkAndDontOptimizeDifferently() throws Exception {
-    test(
-        ImmutableList.of("-keep,allowobfuscation class **.*Exception*"),
-        tb ->
-            tb.addDontWarnJetBrainsAnnotations()
-                .noTreeShaking()
-                .addOptionsModification(
-                    o -> {
-                      // Randomly choose a couple of optimizations.
-                      o.enableClassInlining = false;
-                      o.enableLambdaMerging = false;
-                      o.enableValuePropagation = false;
-                    }));
-  }
-
-  @Test
   public void testDontShrinkAndDontObfuscate() throws Exception {
     test(
         ImmutableList.of("-dontshrink", "-dontobfuscate"),
@@ -99,13 +79,6 @@
   }
 
   @Test
-  public void testDontShrinkDifferently() throws Exception {
-    test(
-        ImmutableList.of("-keep,allowobfuscation class **.*Exception*"),
-        tb -> tb.addDontWarnJetBrainsAnnotations().noTreeShaking());
-  }
-
-  @Test
   public void testDontOptimize() throws Exception {
     test(ImmutableList.of("-dontoptimize"));
   }
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
index 882bc22..a405400 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
@@ -4,16 +4,14 @@
 
 package com.android.tools.r8.kotlin;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.R8TestBuilder;
 import com.android.tools.r8.TestShrinkerBuilder;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.jasmin.JasminBuilder;
 import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
@@ -68,15 +66,16 @@
           .addProperty("property", JAVA_LANG_STRING, Visibility.PRIVATE)
           .addProperty("indirectPropertyGetter", JAVA_LANG_STRING, Visibility.PRIVATE);
 
-  @Parameterized.Parameters(name = "target: {0}, kotlinc: {1}, allowAccessModification: {2}")
+  @Parameterized.Parameters(name = "{0}, allowAccessModification: {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
-        KotlinTargetVersion.values(), getKotlinCompilers(), BooleanUtils.values());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
+        BooleanUtils.values());
   }
 
   public R8KotlinAccessorTest(
-      KotlinTargetVersion targetVersion, KotlinCompiler kotlinc, boolean allowAccessModification) {
-    super(targetVersion, kotlinc, allowAccessModification);
+      KotlinTestParameters kotlinParameters, boolean allowAccessModification) {
+    super(kotlinParameters, allowAccessModification);
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
index 3faf486..b2f0927 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
@@ -4,10 +4,7 @@
 
 package com.android.tools.r8.kotlin;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
-
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.graph.DexCode;
 import com.android.tools.r8.kotlin.TestKotlinClass.Visibility;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
@@ -45,15 +42,16 @@
 
   private Consumer<InternalOptions> disableClassInliner = o -> o.enableClassInlining = false;
 
-  @Parameterized.Parameters(name = "target: {0}, kotlinc: {2}, allowAccessModification: {1}")
+  @Parameterized.Parameters(name = "{0}, allowAccessModification: {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
-        KotlinTargetVersion.values(), getKotlinCompilers(), BooleanUtils.values());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
+        BooleanUtils.values());
   }
 
   public R8KotlinDataClassTest(
-      KotlinTargetVersion targetVersion, KotlinCompiler kotlinc, boolean allowAccessModification) {
-    super(targetVersion, kotlinc, allowAccessModification);
+      KotlinTestParameters kotlinParameters, boolean allowAccessModification) {
+    super(kotlinParameters, allowAccessModification);
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java
index 934ee16..11d1ea2 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java
@@ -4,10 +4,7 @@
 
 package com.android.tools.r8.kotlin;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
-
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -26,15 +23,16 @@
   private static final TestKotlinDataClass KOTLIN_INTRINSICS_CLASS =
       new TestKotlinDataClass("kotlin.jvm.internal.Intrinsics");
 
-  @Parameterized.Parameters(name = "target: {0}, kotlinc: {1}, allowAccessModification: {2}")
+  @Parameterized.Parameters(name = "{0}, allowAccessModification: {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
-        KotlinTargetVersion.values(), getKotlinCompilers(), BooleanUtils.values());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
+        BooleanUtils.values());
   }
 
   public R8KotlinIntrinsicsTest(
-      KotlinTargetVersion targetVersion, KotlinCompiler kotlinc, boolean allowAccessModification) {
-    super(targetVersion, kotlinc, allowAccessModification);
+      KotlinTestParameters kotlinParameters, boolean allowAccessModification) {
+    super(kotlinParameters, allowAccessModification);
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
index b9fefbaf4..edd2c8f 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
@@ -4,11 +4,9 @@
 
 package com.android.tools.r8.kotlin;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.kotlin.TestKotlinClass.Visibility;
 import com.android.tools.r8.naming.MemberNaming;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
@@ -95,15 +93,16 @@
         o.enableClassStaticizer = false;
       };
 
-  @Parameterized.Parameters(name = "target: {0}, kotlinc: {1}, allowAccessModification: {1}")
+  @Parameterized.Parameters(name = "{0}, allowAccessModification: {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
-        KotlinTargetVersion.values(), getKotlinCompilers(), BooleanUtils.values());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
+        BooleanUtils.values());
   }
 
   public R8KotlinPropertiesTest(
-      KotlinTargetVersion targetVersion, KotlinCompiler kotlinc, boolean allowAccessModification) {
-    super(targetVersion, kotlinc, allowAccessModification);
+      KotlinTestParameters kotlinParameters, boolean allowAccessModification) {
+    super(kotlinParameters, allowAccessModification);
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
index 64e3a80..c9fd417 100644
--- a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
@@ -3,11 +3,9 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static org.junit.Assert.assertEquals;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -23,15 +21,16 @@
   private static final String FOLDER = "non_null";
   private static final String STRING = "java.lang.String";
 
-  @Parameterized.Parameters(name = "target: {0}, kotlinc: {1}, allowAccessModification: {2}")
+  @Parameterized.Parameters(name = "{0}, allowAccessModification: {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
-        KotlinTargetVersion.values(), getKotlinCompilers(), BooleanUtils.values());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
+        BooleanUtils.values());
   }
 
   public SimplifyIfNotNullKotlinTest(
-      KotlinTargetVersion targetVersion, KotlinCompiler kotlinc, boolean allowAccessModification) {
-    super(targetVersion, kotlinc, allowAccessModification);
+      KotlinTestParameters kotlinParameters, boolean allowAccessModification) {
+    super(kotlinParameters, allowAccessModification);
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java b/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java
index 4a05b76..1241446 100644
--- a/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java
+++ b/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java
@@ -4,13 +4,11 @@
 
 package com.android.tools.r8.kotlin.coroutines;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static org.junit.Assert.assertEquals;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.kotlin.metadata.KotlinMetadataTestBase;
 import com.android.tools.r8.utils.ZipUtils;
@@ -50,19 +48,18 @@
   private Set<String> notWorkingTests =
       Sets.newHashSet("kotlinx.coroutines.test.TestDispatchersTest");
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   private final TestParameters parameters;
 
   public KotlinxCoroutinesTestRunner(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/JStyleKotlinLambdaMergingWithEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/JStyleKotlinLambdaMergingWithEnumUnboxingTest.java
index 7103667..787ae1f 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/JStyleKotlinLambdaMergingWithEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/JStyleKotlinLambdaMergingWithEnumUnboxingTest.java
@@ -9,13 +9,9 @@
 import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.NoHorizontalClassMerging;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.ir.optimize.lambda.kotlin.JStyleLambdaGroupIdFactory;
-import com.android.tools.r8.ir.optimize.lambda.kotlin.KotlinLambdaGroupIdFactory;
 import com.android.tools.r8.kotlin.lambda.JStyleKotlinLambdaMergingWithEnumUnboxingTest.Main.EnumUnboxingCandidate;
 import java.util.List;
 import org.junit.Test;
@@ -49,31 +45,17 @@
         .addDefaultRuntimeLibrary(parameters)
         .addLibraryFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
         .addKeepMainRule(Main.class)
-        .addOptionsModification(
-            options ->
-                options.testing.kotlinLambdaMergerFactoryForClass =
-                    this::getKotlinLambdaMergerFactoryForClass)
-        .addHorizontallyMergedLambdaClassesInspector(
-            inspector -> inspector.assertMerged(Lambda1.class, Lambda2.class))
+        .addHorizontallyMergedClassesInspector(
+            inspector -> inspector.assertMergedInto(Lambda2.class, Lambda1.class))
         .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(EnumUnboxingCandidate.class))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
-        .enableNoHorizontalClassMergingAnnotations()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLines("Lambda1.method()", "Lambda2.method()");
   }
 
-  private KotlinLambdaGroupIdFactory getKotlinLambdaMergerFactoryForClass(DexProgramClass clazz) {
-    String typeName = clazz.getType().toSourceString();
-    if (typeName.equals(Lambda1.class.getTypeName())
-        || typeName.equals(Lambda2.class.getTypeName())) {
-      return JStyleLambdaGroupIdFactory.getInstance();
-    }
-    return null;
-  }
-
   static class Main {
 
     @NeverClassInline
@@ -121,7 +103,6 @@
   }
 
   @NeverClassInline
-  @NoHorizontalClassMerging
   public static final class Lambda2 implements I {
 
     @NeverInline
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KStyleKotlinLambdaMergingWithEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KStyleKotlinLambdaMergingWithEnumUnboxingTest.java
index aea188c..fe0ef77 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KStyleKotlinLambdaMergingWithEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KStyleKotlinLambdaMergingWithEnumUnboxingTest.java
@@ -9,13 +9,9 @@
 import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.NoHorizontalClassMerging;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.ir.optimize.lambda.kotlin.KStyleLambdaGroupIdFactory;
-import com.android.tools.r8.ir.optimize.lambda.kotlin.KotlinLambdaGroupIdFactory;
 import com.android.tools.r8.kotlin.lambda.KStyleKotlinLambdaMergingWithEnumUnboxingTest.Main.EnumUnboxingCandidate;
 import java.util.List;
 import org.junit.Test;
@@ -48,17 +44,12 @@
         .addInnerClasses(getClass())
         .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
         .addKeepMainRule(Main.class)
-        .addOptionsModification(
-            options ->
-                options.testing.kotlinLambdaMergerFactoryForClass =
-                    this::getKotlinLambdaMergerFactoryForClass)
-        .addHorizontallyMergedLambdaClassesInspector(
-            inspector -> inspector.assertMerged(Lambda1.class, Lambda2.class))
+        .addHorizontallyMergedClassesInspector(
+            inspector -> inspector.assertMergedInto(Lambda2.class, Lambda1.class))
         .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(EnumUnboxingCandidate.class))
         .addDontWarnJetBrainsNotNullAnnotation()
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
-        .enableNoHorizontalClassMergingAnnotations()
         .noMinification()
         .setMinApi(parameters.getApiLevel())
         .compile()
@@ -66,15 +57,6 @@
         .assertSuccessWithOutputLines("Lambda1.method()", "Lambda2.method()");
   }
 
-  private KotlinLambdaGroupIdFactory getKotlinLambdaMergerFactoryForClass(DexProgramClass clazz) {
-    String typeName = clazz.getType().toSourceString();
-    if (typeName.equals(Lambda1.class.getTypeName())
-        || typeName.equals(Lambda2.class.getTypeName())) {
-      return KStyleLambdaGroupIdFactory.getInstance();
-    }
-    return null;
-  }
-
   static class Main {
 
     @NeverClassInline
@@ -123,7 +105,6 @@
   }
 
   @NeverClassInline
-  @NoHorizontalClassMerging
   public static final class Lambda2 extends kotlin.jvm.internal.Lambda<kotlin.Unit>
       implements kotlin.jvm.functions.Function0<kotlin.Unit> {
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergerValidationTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergerValidationTest.java
index 4f78df2..655ddb5 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergerValidationTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergerValidationTest.java
@@ -3,16 +3,14 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.lambda;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.junit.Assume.assumeTrue;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestRuntime;
 import com.android.tools.r8.TestRuntime.CfRuntime;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
 import com.android.tools.r8.utils.DescriptorUtils;
 import java.nio.file.Path;
@@ -26,17 +24,16 @@
 
   private final TestParameters parameters;
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public KotlinLambdaMergerValidationTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc, false);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters, false);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingCapturesKotlinStyleTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingCapturesKotlinStyleTest.java
new file mode 100644
index 0000000..0a49461
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingCapturesKotlinStyleTest.java
@@ -0,0 +1,161 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin.lambda;
+
+import static com.android.tools.r8.utils.PredicateUtils.not;
+import static junit.framework.TestCase.assertEquals;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+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 KotlinLambdaMergingCapturesKotlinStyleTest extends KotlinTestBase {
+
+  private final boolean allowAccessModification;
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}, {1}, allow access modification: {2}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters()
+            .withCfRuntime(CfVm.last())
+            .withDexRuntime(Version.last())
+            .withAllApiLevels()
+            .build(),
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
+        BooleanUtils.values());
+  }
+
+  public KotlinLambdaMergingCapturesKotlinStyleTest(
+      TestParameters parameters,
+      KotlinTestParameters kotlinParameters,
+      boolean allowAccessModification) {
+    super(kotlinParameters);
+    this.allowAccessModification = allowAccessModification;
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testJVM() throws Exception {
+    assumeFalse(allowAccessModification);
+    assumeTrue(parameters.isCfRuntime());
+    assumeTrue(kotlinParameters.isFirst());
+    testForJvm()
+        .addProgramFiles(getProgramFiles())
+        .run(parameters.getRuntime(), getMainClassName())
+        .assertSuccessWithOutput(getExpectedOutput());
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramFiles(getProgramFiles())
+        .addKeepMainRule(getMainClassName())
+        .addDontWarnJetBrainsAnnotations()
+        .addHorizontallyMergedClassesInspector(this::inspect)
+        .allowAccessModification(allowAccessModification)
+        .allowDiagnosticWarningMessages()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .assertAllWarningMessagesMatch(
+            containsString("Resource 'META-INF/MANIFEST.MF' already exists."))
+        .run(parameters.getRuntime(), getMainClassName())
+        .assertSuccessWithOutput(getExpectedOutput());
+  }
+
+  private void inspect(HorizontallyMergedClassesInspector inspector) throws IOException {
+    // Get the Kotlin lambdas in the input.
+    KotlinLambdasInInput lambdasInInput =
+        KotlinLambdasInInput.create(getProgramFiles(), getTestName());
+    assertEquals(0, lambdasInInput.getNumberOfJStyleLambdas());
+    assertEquals(26, lambdasInInput.getNumberOfKStyleLambdas());
+
+    // Only a subset of all K-style Kotlin lambdas are merged.
+    Set<ClassReference> unmergedLambdas =
+        ImmutableSet.of(
+            lambdasInInput.getKStyleLambdaReferenceFromTypeName(getTestName(), "MainKt$test1$15"),
+            lambdasInInput.getKStyleLambdaReferenceFromTypeName(getTestName(), "MainKt$test2$9"),
+            lambdasInInput.getKStyleLambdaReferenceFromTypeName(getTestName(), "MainKt$test2$10"),
+            lambdasInInput.getKStyleLambdaReferenceFromTypeName(getTestName(), "MainKt$test2$11"));
+    inspector
+        .assertClassReferencesMerged(
+            lambdasInInput.getKStyleLambdas().stream()
+                .filter(not(unmergedLambdas::contains))
+                .collect(Collectors.toList()))
+        .assertClassReferencesNotMerged(unmergedLambdas);
+  }
+
+  private String getExpectedOutput() {
+    return StringUtils.lines(
+        "a: 1 2 3",
+        "b: 2 3 1",
+        "c: 3 1 2",
+        "d: 1 A D(d=x)",
+        "e: 2 D(d=y) B",
+        "f: 3 D(d=z) D(d=x)",
+        "g: 7 D(d=z) 3",
+        "h: 8 9 1",
+        "i: A B C",
+        "j: D(d=x) D(d=y) D(d=z)",
+        "k: 7 8 9",
+        "l: A D(d=y) 9",
+        "n: 7 B D(d=z)",
+        "o: D(d=x) 8 C",
+        "p: 1 2 C",
+        "a: true 10 * 20 30 40 50.0 60.0 D(d=D) S null 70",
+        "a: true 10 D(d=D) S * 20 30 40 50.0 60.0 null 70",
+        "a: true * 20 40 50.0 60.0 S null 70 10 30 D(d=D)",
+        "a: D(d=D) S null 70 true 10 * 20 30 40 50.0 60.0",
+        "a: true 10 * 20 30 40 50.0 60.0 D(d=D) S $o3 $o4",
+        "a: true 10 * 20 30 40 50.0 60.0 D(d=D) $o2 $o3 70",
+        "a: true 10 * 20 30 40 50.0 60.0 $o1 $o2 null 70",
+        "a: true 10 * 20 30 40 50.0 60.0 $o1 S null $o4",
+        "x: true 10 * 20 30 40 50.0 60.0 D(d=D) S $o3 70",
+        "y: true 10 * 20 30 40 $f 60.0 D(d=D) S null 70",
+        "z: true 10 * $s 30 40 50.0 60.0 D(d=D) S null 70");
+  }
+
+  private Path getJavaJarFile() {
+    return getJavaJarFile(getTestName());
+  }
+
+  private String getMainClassName() {
+    return getTestName() + ".MainKt";
+  }
+
+  private List<Path> getProgramFiles() {
+    Path kotlinJarFile =
+        getCompileMemoizer(getKotlinFilesInResource(getTestName()), getTestName())
+            .configure(kotlinCompilerTool -> kotlinCompilerTool.includeRuntime().noReflect())
+            .getForConfiguration(kotlinc, targetVersion);
+    return ImmutableList.of(kotlinJarFile, getJavaJarFile());
+  }
+
+  private String getTestName() {
+    return "lambdas_kstyle_captures";
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingDebugTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingDebugTest.java
index 9cee958..61f417e 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingDebugTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingDebugTest.java
@@ -3,11 +3,10 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.lambda;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static org.hamcrest.CoreMatchers.equalTo;
 
 import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
@@ -24,14 +23,19 @@
   private static final String FOLDER = "reprocess_merged_lambdas_kstyle";
   private static final String MAIN_CLASS = "reprocess_merged_lambdas_kstyle.MainKt";
 
-  @Parameters(name = "{0}")
+  @Parameters(name = "{0}, {1}")
   public static List<Object[]> data() {
     return buildParameters(
-        getTestParameters().withDexRuntimes().withAllApiLevels().build(), getKotlinCompilers());
+        getTestParameters().withDexRuntimes().withAllApiLevels().build(),
+        getKotlinTestParameters()
+            .withAllCompilers()
+            .withTargetVersion(KotlinTargetVersion.JAVA_6)
+            .build());
   }
 
-  public KotlinLambdaMergingDebugTest(TestParameters parameters, KotlinCompiler kotlinc) {
-    super(KotlinTargetVersion.JAVA_6, kotlinc);
+  public KotlinLambdaMergingDebugTest(
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingKeepAttributesKotlinStyleTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingKeepAttributesKotlinStyleTest.java
new file mode 100644
index 0000000..a20a77d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingKeepAttributesKotlinStyleTest.java
@@ -0,0 +1,160 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin.lambda;
+
+import static com.android.tools.r8.shaking.ProguardKeepAttributes.ENCLOSING_METHOD;
+import static com.android.tools.r8.shaking.ProguardKeepAttributes.INNER_CLASSES;
+import static com.android.tools.r8.shaking.ProguardKeepAttributes.SIGNATURE;
+import static junit.framework.TestCase.assertEquals;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class KotlinLambdaMergingKeepAttributesKotlinStyleTest extends KotlinTestBase {
+
+  private final boolean allowAccessModification;
+  private final List<String> attributes;
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}, {1}, allow access modification: {2}, attributes: {3}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters()
+            .withCfRuntime(CfVm.last())
+            .withDexRuntime(Version.last())
+            .withAllApiLevels()
+            .build(),
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
+        BooleanUtils.values(),
+        ImmutableList.of(
+            Collections.emptyList(),
+            ImmutableList.of(ENCLOSING_METHOD, INNER_CLASSES),
+            ImmutableList.of(ENCLOSING_METHOD, INNER_CLASSES, SIGNATURE)));
+  }
+
+  public KotlinLambdaMergingKeepAttributesKotlinStyleTest(
+      TestParameters parameters,
+      KotlinTestParameters kotlinParameters,
+      boolean allowAccessModification,
+      List<String> attributes) {
+    super(kotlinParameters);
+    this.allowAccessModification = allowAccessModification;
+    this.attributes = attributes;
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testJVM() throws Exception {
+    assumeFalse(allowAccessModification);
+    assumeTrue(parameters.isCfRuntime());
+    assumeTrue(kotlinParameters.isFirst());
+    testForJvm()
+        .addProgramFiles(getProgramFiles())
+        .run(parameters.getRuntime(), getMainClassName())
+        .assertSuccessWithOutput(getExpectedOutput());
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramFiles(getProgramFiles())
+        .addKeepMainRule(getMainClassName())
+        .applyIf(!attributes.isEmpty(), builder -> builder.addKeepAttributes(attributes))
+        .addDontWarnJetBrainsAnnotations()
+        .addHorizontallyMergedClassesInspector(this::inspect)
+        .allowAccessModification(allowAccessModification)
+        .allowDiagnosticWarningMessages()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .assertAllWarningMessagesMatch(
+            containsString("Resource 'META-INF/MANIFEST.MF' already exists."))
+        .run(parameters.getRuntime(), getMainClassName())
+        .assertSuccessWithOutput(getExpectedOutput());
+  }
+
+  private void inspect(HorizontallyMergedClassesInspector inspector) throws IOException {
+    // Get the Kotlin lambdas in the input.
+    KotlinLambdasInInput lambdasInInput =
+        KotlinLambdasInInput.create(getProgramFiles(), getTestName());
+    assertEquals(0, lambdasInInput.getNumberOfJStyleLambdas());
+    assertEquals(24, lambdasInInput.getNumberOfKStyleLambdas());
+
+    // All K-style Kotlin lambdas are merged if no attributes are kept.
+    if (attributes.isEmpty()) {
+      inspector.assertClassReferencesMerged(lambdasInInput.getKStyleLambdas());
+    } else {
+      // TODO(b/179018501): allow merging classes with inner/outer classes.
+      inspector.assertClassReferencesNotMerged(lambdasInInput.getKStyleLambdas());
+    }
+  }
+
+  private String getExpectedOutput() {
+    return StringUtils.lines(
+        "Alpha(id=11)",
+        "Beta(id=12)",
+        "Gamma(payload={any}, id=13)",
+        "Alpha(id=14)",
+        "First-1-Beta(id=15)",
+        "First-2-Beta(id=16)",
+        "First-3-Beta(id=17)",
+        "First-A-Gamma(payload=18, id=19)-11",
+        "First-B-Gamma(payload=20, id=21)-11",
+        "First-C-Gamma(payload=22, id=23)-11",
+        "First-D-Gamma(payload=24, id=25)-11",
+        "First-E-Gamma(payload=26, id=27)-11",
+        "First-F-Gamma(payload=28, id=29)-11",
+        "Second-1-Beta(id=30)",
+        "Second-2-Beta(id=31)",
+        "Second-3-Beta(id=32)",
+        "Second-A-Gamma(payload=33, id=34)-22",
+        "Second-B-Gamma(payload=35, id=36)-22",
+        "Second-C-Gamma(payload=37, id=38)-22",
+        "Second-D-Gamma(payload=39, id=40)-22",
+        "Second-E-Gamma(payload=41, id=42)-22",
+        "Second-F-Gamma(payload=43, id=44)-22",
+        "4321 45 46 47",
+        "1234 Alpha(id=48) Beta(id=49) Gamma(payload=50, id=51)");
+  }
+
+  private Path getJavaJarFile() {
+    return getJavaJarFile(getTestName());
+  }
+
+  private String getMainClassName() {
+    return getTestName() + ".MainKt";
+  }
+
+  private List<Path> getProgramFiles() {
+    Path kotlinJarFile =
+        getCompileMemoizer(getKotlinFilesInResource(getTestName()), getTestName())
+            .configure(kotlinCompilerTool -> kotlinCompilerTool.includeRuntime().noReflect())
+            .getForConfiguration(kotlinc, targetVersion);
+    return ImmutableList.of(kotlinJarFile, getJavaJarFile());
+  }
+
+  private String getTestName() {
+    return "lambdas_kstyle_generics";
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingSingletonTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingSingletonTest.java
new file mode 100644
index 0000000..72abfe3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingSingletonTest.java
@@ -0,0 +1,155 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin.lambda;
+
+import static com.android.tools.r8.utils.PredicateUtils.not;
+import static junit.framework.TestCase.assertEquals;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+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 KotlinLambdaMergingSingletonTest extends KotlinTestBase {
+
+  private final boolean allowAccessModification;
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}, {1}, allow access modification: {2}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters()
+            .withCfRuntime(CfVm.last())
+            .withDexRuntime(Version.last())
+            .withAllApiLevels()
+            .build(),
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
+        BooleanUtils.values());
+  }
+
+  public KotlinLambdaMergingSingletonTest(
+      TestParameters parameters,
+      KotlinTestParameters kotlinParameters,
+      boolean allowAccessModification) {
+    super(kotlinParameters);
+    this.allowAccessModification = allowAccessModification;
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testJVM() throws Exception {
+    assumeFalse(allowAccessModification);
+    assumeTrue(parameters.isCfRuntime());
+    assumeTrue(kotlinParameters.isFirst());
+    testForJvm()
+        .addProgramFiles(getProgramFiles())
+        .run(parameters.getRuntime(), getMainClassName())
+        .assertSuccessWithOutput(getExpectedOutput());
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramFiles(getProgramFiles())
+        .addKeepMainRule(getMainClassName())
+        .addDontWarnJetBrainsAnnotations()
+        .addHorizontallyMergedClassesInspector(this::inspect)
+        .allowAccessModification(allowAccessModification)
+        .allowDiagnosticWarningMessages()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .assertAllWarningMessagesMatch(
+            containsString("Resource 'META-INF/MANIFEST.MF' already exists."))
+        .run(parameters.getRuntime(), getMainClassName())
+        .assertSuccessWithOutput(getExpectedOutput());
+  }
+
+  private void inspect(HorizontallyMergedClassesInspector inspector) throws IOException {
+    // Get the Kotlin lambdas in the input.
+    KotlinLambdasInInput lambdasInInput =
+        KotlinLambdasInInput.create(getProgramFiles(), getTestName());
+    assertEquals(2, lambdasInInput.getNumberOfJStyleLambdas());
+    assertEquals(7, lambdasInInput.getNumberOfKStyleLambdas());
+
+    // All J-style Kotlin lambdas should be merged into one class.
+    inspector.assertIsCompleteMergeGroup(lambdasInInput.getJStyleLambdas());
+
+    // A subset of the K-style Kotlin lambdas should be merged into one class.
+    Set<ClassReference> kStyleLambdaMergeGroup =
+        Sets.newHashSet(
+            lambdasInInput.getKStyleLambdaReferenceFromTypeName(
+                getTestName(), "MainKt$test2$$inlined$process$1"),
+            lambdasInInput.getKStyleLambdaReferenceFromTypeName(
+                getTestName(), "MainKt$test2$$inlined$process$2"),
+            lambdasInInput.getKStyleLambdaReferenceFromTypeName(
+                getTestName(), "MainKt$test2$$inlined$process$3"),
+            lambdasInInput.getKStyleLambdaReferenceFromTypeName(
+                getTestName(), "MainKt$test2$$inlined$process$4"));
+    if (allowAccessModification) {
+      kStyleLambdaMergeGroup.add(
+          lambdasInInput.getKStyleLambdaReferenceFromTypeName(
+              getTestName(), "MainKt$test2$lambda$1"));
+    }
+    inspector.assertIsCompleteMergeGroup(kStyleLambdaMergeGroup);
+
+    // The remaining lambdas are not merged.
+    inspector.assertClassReferencesNotMerged(
+        lambdasInInput.getAllLambdas().stream()
+            .filter(not(lambdasInInput::isJStyleLambda))
+            .filter(not(kStyleLambdaMergeGroup::contains))
+            .collect(Collectors.toSet()));
+  }
+
+  private String getExpectedOutput() {
+    return StringUtils.lines(
+        "(*000*001*002*003*004*005*006*007*008*009*)",
+        "(*000*001*002*003*004*005*006*007*008*009*)",
+        "(*010*011*)",
+        "(*012*013*014*)",
+        "(*015*016*)",
+        "(*017*018*019*)");
+  }
+
+  private Path getJavaJarFile() {
+    return getJavaJarFile(getTestName());
+  }
+
+  private String getMainClassName() {
+    return getTestName() + ".MainKt";
+  }
+
+  private List<Path> getProgramFiles() {
+    Path kotlinJarFile =
+        getCompileMemoizer(getKotlinFilesInResource(getTestName()), getTestName())
+            .configure(kotlinCompilerTool -> kotlinCompilerTool.includeRuntime().noReflect())
+            .getForConfiguration(kotlinc, targetVersion);
+    return ImmutableList.of(kotlinJarFile, getJavaJarFile());
+  }
+
+  private String getTestName() {
+    return "lambdas_singleton";
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java
deleted file mode 100644
index 4997fe3..0000000
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java
+++ /dev/null
@@ -1,580 +0,0 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.kotlin.lambda;
-
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
-import static com.google.common.base.Predicates.alwaysTrue;
-import static junit.framework.TestCase.assertEquals;
-import static junit.framework.TestCase.assertTrue;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.fail;
-
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.optimize.lambda.CaptureSignature;
-import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
-import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.google.common.collect.Lists;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class KotlinLambdaMergingTest extends AbstractR8KotlinTestBase {
-  private static final String KOTLIN_FUNCTION_IFACE = "Lkotlin/jvm/functions/Function";
-  private static final String KOTLIN_FUNCTION_IFACE_STR = "kotlin.jvm.functions.Function";
-
-  private void configure(InternalOptions options) {
-    options.enableClassInlining = false;
-    // The test checks that the generated lambdas inherit from Function, which is not true if
-    // the unused interface removal is enabled.
-    options.enableUnusedInterfaceRemoval = enableUnusedInterfaceRemoval;
-    // Ensure that enclosing method and inner class attributes are kept even on classes that are
-    // not explicitly mentioned by a keep rule.
-    options.forceProguardCompatibility = true;
-    options.horizontalClassMergerOptions().disableKotlinLambdaMerging();
-  }
-
-  private final boolean enableUnusedInterfaceRemoval;
-
-  @Parameterized.Parameters(
-      name =
-          "target: {0}, kotlinc: {1}, allow access modification: {2}, unused interface removal:"
-              + " {3}")
-  public static Collection<Object[]> data() {
-    return buildParameters(
-        KotlinTargetVersion.values(),
-        getKotlinCompilers(),
-        BooleanUtils.values(),
-        BooleanUtils.values());
-  }
-
-  public KotlinLambdaMergingTest(
-      KotlinTargetVersion targetVersion,
-      KotlinCompiler kotlinc,
-      boolean allowAccessModification,
-      boolean enableUnusedInterfaceRemoval) {
-    super(targetVersion, kotlinc, allowAccessModification);
-    this.enableUnusedInterfaceRemoval = enableUnusedInterfaceRemoval;
-  }
-
-  abstract static class LambdaOrGroup {
-    abstract boolean match(DexClass clazz);
-  }
-
-  static class Group extends LambdaOrGroup {
-    final String pkg;
-    final String capture;
-    final int arity;
-    final String sam;
-    final int singletons;
-
-    private Group(String pkg, String capture, int arity, String sam, int singletons) {
-      this.pkg = pkg;
-      this.capture = fixCapture(capture);
-      this.arity = arity;
-      this.sam = sam;
-      this.singletons = singletons;
-    }
-
-    private String fixCapture(String capture) {
-      capture += "I";
-      char[] chars = capture.toCharArray();
-      Arrays.sort(chars);
-      return new String(chars);
-    }
-
-    @Override
-    public String toString() {
-      return "group class " +
-          (pkg.length() == 0 ? "" : pkg + "/") +
-          "-$$LambdaGroup$XXXX (arity: " + arity +
-          ", capture: " + capture + ", iface: " + sam + ", sing: " + singletons + ")";
-    }
-
-    @Override
-    boolean match(DexClass clazz) {
-      return clazz.type.getPackageDescriptor().equals(pkg) &&
-          getLambdaOrGroupCapture(clazz).equals(capture) &&
-          getLambdaSam(clazz).equals(sam) &&
-          getLambdaSingletons(clazz) == singletons &&
-          getLambdaOrGroupArity(clazz) == arity;
-    }
-  }
-
-  private static Group kstyleImpl(String pkg, String capture, int arity, int singletons) {
-    return new Group(pkg, capture, arity, KOTLIN_FUNCTION_IFACE_STR + arity, singletons);
-  }
-
-  static Group kstyle(String pkg, int arity) {
-    return kstyleImpl(pkg, "", arity, 0);
-  }
-
-  static Group kstyle(String pkg, int arity, int singletons) {
-    assertTrue(singletons != 0);
-    return kstyleImpl(pkg, "", arity, singletons);
-  }
-
-  private static Group kstyle(String pkg, String capture, int arity) {
-    assertFalse(capture.isEmpty());
-    return kstyleImpl(pkg, capture, arity, 0);
-  }
-
-  private static Group jstyleImpl(
-      String pkg, String capture, int arity, String sam, int singletons) {
-    assertTrue(capture.isEmpty() || singletons == 0);
-    return new Group(pkg, capture, arity, sam, singletons);
-  }
-
-  private static Group jstyle(String pkg, String capture, int arity, String sam) {
-    return jstyleImpl(pkg, capture, arity, sam, 0);
-  }
-
-  private static Group jstyle(String pkg, int arity, String sam, int singletons) {
-    return jstyleImpl(pkg, "", arity, sam, singletons);
-  }
-
-  static class Lambda extends LambdaOrGroup {
-    final String pkg;
-    final String name;
-    final int arity;
-
-    Lambda(String pkg, String name, int arity) {
-      this.pkg = pkg;
-      this.name = name;
-      this.arity = arity;
-    }
-
-    @Override
-    public String toString() {
-      return "lambda class " +
-          (pkg.length() == 0 ? "" : pkg + "/") +
-          name + " (arity: " + arity + ")";
-    }
-
-    @Override
-    boolean match(DexClass clazz) {
-      return clazz.type.getPackageDescriptor().equals(pkg) &&
-          clazz.type.getName().equals(name) &&
-          getLambdaOrGroupArity(clazz) == arity;
-    }
-  }
-
-  static class Verifier {
-    final CodeInspector codeInspector;
-    final List<DexClass> lambdas = new ArrayList<>();
-    final List<DexClass> groups = new ArrayList<>();
-
-    Verifier(CodeInspector codeInspector) {
-      this.codeInspector = codeInspector;
-      initGroupsAndLambdas();
-    }
-
-    private void initGroupsAndLambdas() {
-      codeInspector.forAllClasses(
-          clazz -> {
-            DexClass dexClass = clazz.getDexProgramClass();
-            if (isLambdaOrGroup(dexClass)) {
-              if (isLambdaGroupClass(dexClass)) {
-                groups.add(dexClass);
-              } else {
-                lambdas.add(dexClass);
-              }
-            }
-          });
-    }
-
-    void assertLambdaGroups(Group... groups) {
-      assertLambdasOrGroups("Lambda group", this.groups, groups);
-    }
-
-    void assertLambdas(Lambda... lambdas) {
-      assertLambdasOrGroups("Lambda", this.lambdas, lambdas);
-    }
-
-    @SafeVarargs
-    private static <T extends LambdaOrGroup>
-    void assertLambdasOrGroups(String what, List<DexClass> objects, T... checks) {
-      ArrayList<DexClass> list = Lists.newArrayList(objects);
-      for (int i = 0; i < checks.length; i++) {
-        T check = checks[i];
-        for (DexClass clazz : list) {
-          if (check.match(clazz)) {
-            // Validate static initializer.
-            if (check instanceof Group) {
-              assertEquals(
-                  clazz.getMethodCollection().numberOfDirectMethods(),
-                  ((Group) check).singletons == 0 ? 1 : 2);
-            }
-
-            list.remove(clazz);
-            checks[i] = null;
-            break;
-          }
-        }
-      }
-
-      int notFound = 0;
-      for (T check : checks) {
-        if (check != null) {
-          System.err.println(what + " not found: " + check);
-          notFound++;
-        }
-      }
-
-      for (DexClass dexClass : list) {
-        System.err.println(what + " unexpected: " +
-            dexClass.type.descriptor.toString() +
-            ", arity: " + getLambdaOrGroupArity(dexClass) +
-            ", capture: " + getLambdaOrGroupCapture(dexClass) +
-            ", sam: " + getLambdaSam(dexClass) +
-            ", sing: " + getLambdaSingletons(dexClass));
-        notFound++;
-      }
-
-      assertTrue(what + "s match failed", 0 == notFound && 0 == list.size());
-    }
-  }
-
-  private static int getLambdaOrGroupArity(DexClass clazz) {
-    if (isKStyleLambdaOrGroup(clazz)) {
-      for (DexType iface : clazz.interfaces.values) {
-        String descr = iface.descriptor.toString();
-        if (descr.startsWith(KOTLIN_FUNCTION_IFACE)) {
-          return Integer.parseInt(
-              descr.substring(KOTLIN_FUNCTION_IFACE.length(), descr.length() - 1));
-        }
-      }
-
-    } else {
-      assertTrue(isJStyleLambdaOrGroup(clazz));
-      // Taking the number of any virtual method parameters seems to be good enough.
-      assertTrue(clazz.getMethodCollection().hasVirtualMethods());
-      return clazz.lookupVirtualMethod(alwaysTrue()).method.proto.parameters.size();
-    }
-    fail("Failed to get arity for " + clazz.type.descriptor.toString());
-    throw new AssertionError();
-  }
-
-  private static String getLambdaSam(DexClass clazz) {
-    assertEquals(1, clazz.interfaces.size());
-    return clazz.interfaces.values[0].toSourceString();
-  }
-
-  private static int getLambdaSingletons(DexClass clazz) {
-    assertEquals(1, clazz.interfaces.size());
-    return clazz.staticFields().size();
-  }
-
-  private static boolean isLambdaOrGroup(DexClass clazz) {
-    return !clazz.type.getPackageDescriptor().startsWith("kotlin") &&
-        (isKStyleLambdaOrGroup(clazz) || isJStyleLambdaOrGroup(clazz));
-  }
-
-  private static boolean isKStyleLambdaOrGroup(DexClass clazz) {
-    return clazz.superType.descriptor.toString().equals("Lkotlin/jvm/internal/Lambda;");
-  }
-
-  private static boolean isJStyleLambdaOrGroup(DexClass clazz) {
-    return clazz.superType.descriptor.toString().equals("Ljava/lang/Object;") &&
-        clazz.interfaces.size() == 1;
-  }
-
-  private static boolean isLambdaGroupClass(DexClass clazz) {
-    return clazz.type.getName().startsWith("-$$LambdaGroup$");
-  }
-
-  private static String getLambdaOrGroupCapture(DexClass clazz) {
-    return CaptureSignature.getCaptureSignature(clazz.instanceFields());
-  }
-
-  @Test
-  public void testTrivialKs() throws Exception {
-    final String mainClassName = "lambdas_kstyle_trivial.MainKt";
-    runTest(
-            "lambdas_kstyle_trivial",
-            mainClassName,
-            testBuilder ->
-                testBuilder
-                    .addKeepRules(
-                        "-keepunusedarguments class * extends kotlin.jvm.internal.Lambda {"
-                            + " invoke(int, short); }")
-                    .addDontWarnJetBrainsNotNullAnnotation()
-                    .addOptionsModification(this::configure))
-        .inspect(
-            inspector -> {
-              if (enableUnusedInterfaceRemoval) {
-                // Only test that the code generates the same output as the input code does on the
-                // JVM.
-                return;
-              }
-
-              Verifier verifier = new Verifier(inspector);
-              String pkg = "lambdas_kstyle_trivial";
-
-              verifier.assertLambdaGroups(
-                  allowAccessModification
-                      ? new Group[] {
-                        kstyle("", 0, 4),
-                        kstyle("", 1, 9),
-                        kstyle("", 2, 2), // -\
-                        kstyle("", 2, 5), // - 3 groups different by main method
-                        kstyle("", 2, 4), // -/
-                        kstyle("", 3, 2),
-                        kstyle("", 22, 2)
-                      }
-                      : new Group[] {
-                        kstyle(pkg, 0, 2),
-                        kstyle(pkg, 1, 5),
-                        kstyle(pkg, 2, 5), // - 2 groups different by main method
-                        kstyle(pkg, 2, 4), // -/
-                        kstyle(pkg, 3, 2),
-                        kstyle(pkg, 22, 2),
-                        kstyle(pkg + "/inner", 0, 2),
-                        kstyle(pkg + "/inner", 1, 4)
-                      });
-
-              verifier.assertLambdas(
-                  allowAccessModification
-                      ? new Lambda[] {}
-                      : new Lambda[] {
-                        new Lambda(pkg, "MainKt$testStateless$8", 2),
-                        new Lambda(pkg + "/inner", "InnerKt$testInnerStateless$7", 2)
-                      });
-            });
-  }
-
-  @Test
-  public void testCapturesKs() throws Exception {
-    final String mainClassName = "lambdas_kstyle_captures.MainKt";
-    runTest(
-            "lambdas_kstyle_captures",
-            mainClassName,
-            testBuilder ->
-                testBuilder
-                    .addDontWarnJetBrainsAnnotations()
-                    .addOptionsModification(this::configure))
-        .inspect(
-            inspector -> {
-              if (enableUnusedInterfaceRemoval) {
-                // Only test that the code generates the same output as the input code does on the
-                // JVM.
-                return;
-              }
-
-              Verifier verifier = new Verifier(inspector);
-              String pkg = "lambdas_kstyle_captures";
-              String grpPkg = allowAccessModification ? "" : pkg;
-
-              verifier.assertLambdaGroups(
-                  kstyle(grpPkg, "LLL", 0),
-                  kstyle(grpPkg, "ILL", 0),
-                  kstyle(grpPkg, "III", 0),
-                  kstyle(grpPkg, "BCDFIJLLLLSZ", 0),
-                  kstyle(grpPkg, "BCDFIJLLSZ", 0));
-
-              verifier.assertLambdas(
-                  new Lambda(pkg, "MainKt$test1$15", 0),
-                  new Lambda(pkg, "MainKt$test2$10", 0),
-                  new Lambda(pkg, "MainKt$test2$11", 0),
-                  new Lambda(pkg, "MainKt$test2$9", 0));
-            });
-  }
-
-  @Test
-  public void testGenericsNoSignatureKs() throws Exception {
-    final String mainClassName = "lambdas_kstyle_generics.MainKt";
-    runTest(
-            "lambdas_kstyle_generics",
-            mainClassName,
-            testBuilder ->
-                testBuilder
-                    .addDontWarnJetBrainsAnnotations()
-                    .addOptionsModification(this::configure))
-        .inspect(
-            inspector -> {
-              if (enableUnusedInterfaceRemoval) {
-                // Only test that the code generates the same output as the input code does on the
-                // JVM.
-                return;
-              }
-
-              Verifier verifier = new Verifier(inspector);
-              String pkg = "lambdas_kstyle_generics";
-              String grpPkg = allowAccessModification ? "" : pkg;
-
-              verifier.assertLambdaGroups(
-                  kstyle(grpPkg, 1, 3), // Group for Any
-                  kstyle(grpPkg, "L", 1), // Group for Beta
-                  kstyle(grpPkg, "LS", 1), // Group for Gamma
-                  kstyle(grpPkg, 1, 2) // Group for int
-                  );
-
-              verifier.assertLambdas(new Lambda(pkg, "MainKt$main$4", 1));
-            });
-  }
-
-  @Test
-  public void testInnerClassesAndEnclosingMethodsKs() throws Exception {
-    final String mainClassName = "lambdas_kstyle_generics.MainKt";
-    runTest(
-            "lambdas_kstyle_generics",
-            mainClassName,
-            testBuilder ->
-                testBuilder
-                    .addDontWarnJetBrainsAnnotations()
-                    .addKeepAttributeInnerClassesAndEnclosingMethod()
-                    .addOptionsModification(this::configure))
-        .inspect(
-            inspector -> {
-              if (enableUnusedInterfaceRemoval) {
-                // Only test that the code generates the same output as the input code does on the
-                // JVM.
-                return;
-              }
-
-              Verifier verifier = new Verifier(inspector);
-              String pkg = "lambdas_kstyle_generics";
-              String grpPkg = allowAccessModification ? "" : pkg;
-
-              verifier.assertLambdaGroups(
-                  kstyle(grpPkg, 1, 3), // Group for Any
-                  kstyle(grpPkg, "L", 1), // Group for Beta   // First
-                  kstyle(grpPkg, "L", 1), // Group for Beta   // Second
-                  kstyle(grpPkg, "LS", 1), // Group for Gamma // First
-                  kstyle(grpPkg, "LS", 1), // Group for Gamma // Second
-                  kstyle(grpPkg, 1, 2) // Group for int
-                  );
-
-              verifier.assertLambdas(new Lambda(pkg, "MainKt$main$4", 1));
-            });
-  }
-
-  @Test
-  public void testGenericsSignatureInnerEnclosingKs() throws Exception {
-    final String mainClassName = "lambdas_kstyle_generics.MainKt";
-    runTest(
-            "lambdas_kstyle_generics",
-            mainClassName,
-            // KEEP_SIGNATURE_INNER_ENCLOSING,
-            testBuilder ->
-                testBuilder
-                    .addDontWarnJetBrainsAnnotations()
-                    .addKeepAttributeInnerClassesAndEnclosingMethod()
-                    .addKeepAttributeSignature()
-                    .addOptionsModification(this::configure))
-        .inspect(
-            inspector -> {
-              if (enableUnusedInterfaceRemoval) {
-                // Only test that the code generates the same output as the input code does on the
-                // JVM.
-                return;
-              }
-
-              Verifier verifier = new Verifier(inspector);
-              String pkg = "lambdas_kstyle_generics";
-              String grpPkg = allowAccessModification ? "" : pkg;
-
-              verifier.assertLambdaGroups(
-                  kstyle(grpPkg, 1, 3), // Group for Any
-                  kstyle(grpPkg, "L", 1), // Group for Beta in First
-                  kstyle(grpPkg, "L", 1), // Group for Beta in Second
-                  kstyle(grpPkg, "LS", 1), // Group for Gamma<String> in First
-                  kstyle(grpPkg, "LS", 1), // Group for Gamma<Integer> in First
-                  kstyle(grpPkg, "LS", 1), // Group for Gamma<String> in Second
-                  kstyle(grpPkg, "LS", 1), // Group for Gamma<Integer> in Second
-                  kstyle(grpPkg, 1, 2) // Group for int
-                  );
-
-              verifier.assertLambdas(new Lambda(pkg, "MainKt$main$4", 1));
-            });
-  }
-
-  @Test
-  public void testTrivialJs() throws Exception {
-    final String mainClassName = "lambdas_jstyle_trivial.MainKt";
-    runTest(
-            "lambdas_jstyle_trivial",
-            mainClassName,
-            testBuilder ->
-                testBuilder
-                    .addDontWarnJetBrainsAnnotations()
-                    .addOptionsModification(this::configure))
-        .inspect(
-            inspector -> {
-              Verifier verifier = new Verifier(inspector);
-              String pkg = "lambdas_jstyle_trivial";
-              String grp = allowAccessModification ? "" : pkg;
-
-              String supplier = "lambdas_jstyle_trivial.Lambdas$Supplier";
-              String intSupplier = "lambdas_jstyle_trivial.Lambdas$IntSupplier";
-              String consumer = "lambdas_jstyle_trivial.Lambdas$Consumer";
-              String intConsumer = "lambdas_jstyle_trivial.Lambdas$IntConsumer";
-              String multiFunction = "lambdas_jstyle_trivial.Lambdas$MultiFunction";
-
-              verifier.assertLambdaGroups(
-                  jstyle(grp, 0, intSupplier, 2),
-                  jstyle(grp, "L", 0, supplier),
-                  jstyle(grp, "LL", 0, supplier),
-                  jstyle(grp, "LLL", 0, supplier),
-                  jstyle(grp, 1, intConsumer, allowAccessModification ? 3 : 2),
-                  jstyle(grp, "I", 1, consumer),
-                  jstyle(grp, "II", 1, consumer),
-                  jstyle(grp, "III", 1, consumer),
-                  jstyle(grp, "IIII", 1, consumer),
-                  jstyle(grp, 3, multiFunction, 2),
-                  jstyle(grp, 3, multiFunction, 2),
-                  jstyle(grp, 3, multiFunction, 4),
-                  jstyle(grp, 3, multiFunction, 6));
-
-              verifier.assertLambdas(
-                  allowAccessModification
-                      ? new Lambda[] {
-                        new Lambda(pkg + "/inner", "InnerKt$testInner1$4", 1),
-                        new Lambda(pkg + "/inner", "InnerKt$testInner1$5", 1)
-                      }
-                      : new Lambda[] {
-                        new Lambda(pkg + "/inner", "InnerKt$testInner1$1", 1),
-                        new Lambda(pkg + "/inner", "InnerKt$testInner1$2", 1),
-                        new Lambda(pkg + "/inner", "InnerKt$testInner1$3", 1),
-                        new Lambda(pkg + "/inner", "InnerKt$testInner1$4", 1),
-                        new Lambda(pkg + "/inner", "InnerKt$testInner1$5", 1)
-                      });
-            });
-  }
-
-  @Test
-  public void testSingleton() throws Exception {
-    final String mainClassName = "lambdas_singleton.MainKt";
-    runTest(
-            "lambdas_singleton",
-            mainClassName,
-            testBuilder ->
-                testBuilder
-                    .addDontWarnJetBrainsAnnotations()
-                    .addOptionsModification(this::configure)
-                    .noHorizontalClassMerging())
-        .inspect(
-            inspector -> {
-              Verifier verifier = new Verifier(inspector);
-              String pkg = "lambdas_singleton";
-              String grp = allowAccessModification ? "" : pkg;
-
-              verifier.assertLambdaGroups(
-                  kstyle(grp, 1 /* 1 out of 5 lambdas in the group */),
-                  jstyle(grp, 2, "java.util.Comparator", 0 /* 0 out of 2 lambdas in the group */));
-
-              verifier.assertLambdas(/* None */ );
-            });
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialJavaStyleTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialJavaStyleTest.java
new file mode 100644
index 0000000..1e83c00
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialJavaStyleTest.java
@@ -0,0 +1,210 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin.lambda;
+
+import static com.android.tools.r8.utils.PredicateUtils.not;
+import static junit.framework.TestCase.assertEquals;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+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 KotlinLambdaMergingTrivialJavaStyleTest extends KotlinTestBase {
+
+  private final boolean allowAccessModification;
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}, {1}, allow access modification: {2}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters()
+            .withCfRuntime(CfVm.last())
+            .withDexRuntime(Version.last())
+            .withAllApiLevels()
+            .build(),
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
+        BooleanUtils.values());
+  }
+
+  public KotlinLambdaMergingTrivialJavaStyleTest(
+      TestParameters parameters,
+      KotlinTestParameters kotlinParameters,
+      boolean allowAccessModification) {
+    super(kotlinParameters);
+    this.allowAccessModification = allowAccessModification;
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testJVM() throws Exception {
+    assumeFalse(allowAccessModification);
+    assumeTrue(parameters.isCfRuntime());
+    assumeTrue(kotlinParameters.isFirst());
+    testForJvm()
+        .addProgramFiles(getProgramFiles())
+        .run(parameters.getRuntime(), getMainClassName())
+        .assertSuccessWithOutput(getExpectedOutput());
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramFiles(getProgramFiles())
+        .addKeepMainRule(getMainClassName())
+        .addDontWarnJetBrainsAnnotations()
+        .addHorizontallyMergedClassesInspector(this::inspect)
+        .allowAccessModification(allowAccessModification)
+        .allowDiagnosticWarningMessages()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .assertAllWarningMessagesMatch(
+            containsString("Resource 'META-INF/MANIFEST.MF' already exists."))
+        .run(parameters.getRuntime(), getMainClassName())
+        .assertSuccessWithOutput(getExpectedOutput());
+  }
+
+  private void inspect(HorizontallyMergedClassesInspector inspector) throws IOException {
+    // Get the Kotlin lambdas in the input.
+    KotlinLambdasInInput lambdasInInput =
+        KotlinLambdasInInput.create(getProgramFiles(), getTestName());
+    assertEquals(39, lambdasInInput.getNumberOfJStyleLambdas());
+    assertEquals(0, lambdasInInput.getNumberOfKStyleLambdas());
+
+    if (!allowAccessModification) {
+      // Only a subset of all J-style Kotlin lambdas are merged without -allowaccessmodification.
+      Set<ClassReference> unmergedLambdas =
+          ImmutableSet.of(
+              lambdasInInput.getJStyleLambdaReferenceFromTypeName(
+                  getTestName(), "inner.InnerKt$testInner1$1"),
+              lambdasInInput.getJStyleLambdaReferenceFromTypeName(
+                  getTestName(), "inner.InnerKt$testInner1$2"),
+              lambdasInInput.getJStyleLambdaReferenceFromTypeName(
+                  getTestName(), "inner.InnerKt$testInner1$3"),
+              lambdasInInput.getJStyleLambdaReferenceFromTypeName(
+                  getTestName(), "inner.InnerKt$testInner1$4"),
+              lambdasInInput.getJStyleLambdaReferenceFromTypeName(
+                  getTestName(), "inner.InnerKt$testInner1$5"));
+      inspector
+          .assertClassReferencesMerged(
+              lambdasInInput.getJStyleLambdas().stream()
+                  .filter(not(unmergedLambdas::contains))
+                  .collect(Collectors.toList()))
+          .assertClassReferencesNotMerged(unmergedLambdas);
+      return;
+    }
+
+    // All J-style Kotlin lambdas are merged with -allowaccessmodification.
+    inspector.assertClassReferencesMerged(lambdasInInput.getJStyleLambdas());
+  }
+
+  private String getExpectedOutput() {
+    return StringUtils.lines(
+        "{005:4}",
+        "{007:6}",
+        "009:{008}:{0}",
+        "011:{010}:{0}",
+        "013:{012}:{0}:{1}",
+        "015:{014}:{0}:{1}",
+        "017:{Local(id=016)}:{0}:{1}:{2}",
+        "019:{Local(id=018)}:{0}:{1}:{2}",
+        "021:{Local(id=Local(id=020))}:{0}:{1}:{2}:{3}",
+        "023:{Local(id=Local(id=022))}:{0}:{1}:{2}:{3}",
+        "27",
+        "kotlin.Unit",
+        "28",
+        "kotlin.Unit",
+        "029:024",
+        "kotlin.Unit",
+        "030:024",
+        "kotlin.Unit",
+        "031:024",
+        "kotlin.Unit",
+        "032:024",
+        "kotlin.Unit",
+        "Local(id=033):024:025",
+        "kotlin.Unit",
+        "Local(id=034):024:025",
+        "kotlin.Unit",
+        "Local(id=Local(id=035)):024:025:026",
+        "kotlin.Unit",
+        "Local(id=Local(id=036)):024:025:026",
+        "kotlin.Unit",
+        "037:038:039",
+        "039:037:038",
+        "038:039:037",
+        "Local(id=037):038:039",
+        "Local(id=038):037:039",
+        "037:Local(id=038):039",
+        "037:Local(id=039):038",
+        "Local(id=Local(id=037)):Local(id=Local(id=038)):Local(id=Local(id=039))",
+        "Local(id=Local(id=039)):Local(id=Local(id=037)):Local(id=Local(id=038))",
+        "040:Local(id=041):Local(id=Local(id=042))",
+        "Local(id=Local(id=042)):040:Local(id=041)",
+        "Local(id=041):Local(id=Local(id=042)):040",
+        "Local(id=040):Local(id=041):Local(id=Local(id=042))",
+        "Local(id=Local(id=041)):040:Local(id=Local(id=042))",
+        "040:Local(id=Local(id=041)):Local(id=Local(id=042))",
+        "040:Local(id=Local(id=Local(id=042))):Local(id=041)",
+        "Local(id=Local(id=040)):Local(id=Local(id=Local(id=041))):Local(id=Local(id=Local(id=Local(id=042))))",
+        "Local(id=Local(id=Local(id=Local(id=042)))):Local(id=Local(id=040)):Local(id=Local(id=Local(id=041)))",
+        "043:044:045",
+        "045:043:044",
+        "044:045:043",
+        "Local(id=043):044:045",
+        "Local(id=044):043:045",
+        "046:Local(id=047):Local(id=048)",
+        "Local(id=048):046:Local(id=047)",
+        "Local(id=047):Local(id=048):046",
+        "Local(id=046):Local(id=047):Local(id=048)",
+        "Local(id=Local(id=047)):046:Local(id=048)",
+        "{053:100}",
+        "055:{054}:{49}",
+        "057:{056}:{49}:{50}",
+        "059:{InnerLocal(id=058)}:{49}:{50}:{51}",
+        "061:{InnerLocal(id=InnerLocal(id=060))}:{49}:{50}:{51}:{52");
+  }
+
+  private Path getJavaJarFile() {
+    return getJavaJarFile(getTestName());
+  }
+
+  private String getMainClassName() {
+    return getTestName() + ".MainKt";
+  }
+
+  private List<Path> getProgramFiles() {
+    Path kotlinJarFile =
+        getCompileMemoizer(getKotlinFilesInResource(getTestName()), getTestName())
+            .configure(kotlinCompilerTool -> kotlinCompilerTool.includeRuntime().noReflect())
+            .getForConfiguration(kotlinc, targetVersion);
+    return ImmutableList.of(kotlinJarFile, getJavaJarFile());
+  }
+
+  private String getTestName() {
+    return "lambdas_jstyle_trivial";
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialKotlinStyleTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialKotlinStyleTest.java
new file mode 100644
index 0000000..3515109
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialKotlinStyleTest.java
@@ -0,0 +1,171 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin.lambda;
+
+import static com.android.tools.r8.utils.PredicateUtils.not;
+import static junit.framework.TestCase.assertEquals;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+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 KotlinLambdaMergingTrivialKotlinStyleTest extends KotlinTestBase {
+
+  private final boolean allowAccessModification;
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}, {1}, allow access modification: {2}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters()
+            .withCfRuntime(CfVm.last())
+            .withDexRuntime(Version.last())
+            .withAllApiLevels()
+            .build(),
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
+        BooleanUtils.values());
+  }
+
+  public KotlinLambdaMergingTrivialKotlinStyleTest(
+      TestParameters parameters,
+      KotlinTestParameters kotlinParameters,
+      boolean allowAccessModification) {
+    super(kotlinParameters);
+    this.allowAccessModification = allowAccessModification;
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testJVM() throws Exception {
+    assumeFalse(allowAccessModification);
+    assumeTrue(parameters.isCfRuntime());
+    assumeTrue(kotlinParameters.isFirst());
+    testForJvm()
+        .addProgramFiles(getProgramFiles())
+        .run(parameters.getRuntime(), getMainClassName())
+        .assertSuccessWithOutput(getExpectedOutput());
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramFiles(getProgramFiles())
+        .addKeepMainRule(getMainClassName())
+        .addDontWarnJetBrainsNotNullAnnotation()
+        .addHorizontallyMergedClassesInspector(this::inspect)
+        .allowAccessModification(allowAccessModification)
+        .allowDiagnosticWarningMessages()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .assertAllWarningMessagesMatch(
+            containsString("Resource 'META-INF/MANIFEST.MF' already exists."))
+        .run(parameters.getRuntime(), getMainClassName())
+        .assertSuccessWithOutput(getExpectedOutput());
+  }
+
+  private void inspect(HorizontallyMergedClassesInspector inspector) throws IOException {
+    // Get the Kotlin lambdas in the input.
+    KotlinLambdasInInput lambdasInInput =
+        KotlinLambdasInInput.create(getProgramFiles(), getTestName());
+    assertEquals(0, lambdasInInput.getNumberOfJStyleLambdas());
+    assertEquals(28, lambdasInInput.getNumberOfKStyleLambdas());
+
+    if (!allowAccessModification) {
+      // Only a subset of all K-style Kotlin lambdas are merged without -allowaccessmodification.
+      Set<ClassReference> unmergedLambdas =
+          ImmutableSet.of(
+              lambdasInInput.getKStyleLambdaReferenceFromTypeName(
+                  getTestName(), "inner.InnerKt$testInnerStateless$7"));
+      inspector
+          .assertClassReferencesMerged(
+              lambdasInInput.getKStyleLambdas().stream()
+                  .filter(not(unmergedLambdas::contains))
+                  .collect(Collectors.toList()))
+          .assertClassReferencesNotMerged(unmergedLambdas);
+      return;
+    }
+
+    // All K-style Kotlin lambdas are merged with -allowaccessmodification.
+    inspector.assertClassReferencesMerged(lambdasInInput.getKStyleLambdas());
+  }
+
+  private String getExpectedOutput() {
+    return StringUtils.lines(
+        "first empty",
+        "second empty",
+        "first single",
+        "second single",
+        "third single",
+        "caught: exception#14",
+        "15",
+        "16-17",
+        "181920",
+        "one-two-three",
+        "one-two-...-twentythree",
+        "46474849505152535455565758596061626364656667",
+        "first empty",
+        "second empty",
+        "first single",
+        "second single",
+        "third single",
+        "71",
+        "72-73",
+        "1",
+        "5",
+        "8",
+        "20",
+        "5",
+        "",
+        "kotlin.Unit",
+        "10",
+        "kotlin.Unit",
+        "13",
+        "kotlin.Unit",
+        "14 -- 10",
+        "kotlin.Unit");
+  }
+
+  private Path getJavaJarFile() {
+    return getJavaJarFile(getTestName());
+  }
+
+  private String getMainClassName() {
+    return getTestName() + ".MainKt";
+  }
+
+  private List<Path> getProgramFiles() {
+    Path kotlinJarFile =
+        getCompileMemoizer(getKotlinFilesInResource(getTestName()), getTestName())
+            .configure(kotlinCompilerTool -> kotlinCompilerTool.includeRuntime().noReflect())
+            .getForConfiguration(kotlinc, targetVersion);
+    return ImmutableList.of(kotlinJarFile, getJavaJarFile());
+  }
+
+  private String getTestName() {
+    return "lambdas_kstyle_trivial";
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithReprocessingTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithReprocessingTest.java
index b8fb366..8258582 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithReprocessingTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithReprocessingTest.java
@@ -3,10 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.lambda;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
-
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestShrinkerBuilder;
 import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
 import com.android.tools.r8.utils.BooleanUtils;
 import java.util.Collection;
@@ -17,15 +15,16 @@
 @RunWith(Parameterized.class)
 public class KotlinLambdaMergingWithReprocessingTest extends AbstractR8KotlinTestBase {
 
-  @Parameterized.Parameters(name = "target: {0}, kotlinc: {1}, allowAccessModification: {2}")
+  @Parameterized.Parameters(name = "{0}, allowAccessModification: {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
-        KotlinTargetVersion.values(), getKotlinCompilers(), BooleanUtils.values());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
+        BooleanUtils.values());
   }
 
   public KotlinLambdaMergingWithReprocessingTest(
-      KotlinTargetVersion targetVersion, KotlinCompiler kotlinc, boolean allowAccessModification) {
-    super(targetVersion, kotlinc, allowAccessModification);
+      KotlinTestParameters kotlinParameters, boolean allowAccessModification) {
+    super(kotlinParameters, allowAccessModification);
   }
 
   @Test
@@ -34,14 +33,6 @@
     runTest(
         "reprocess_merged_lambdas_kstyle",
         mainClassName,
-        testBuilder ->
-            testBuilder
-                .addDontWarnJetBrainsNotNullAnnotation()
-                .addOptionsModification(
-                    options -> {
-                      options.enableInlining = true;
-                      options.enableClassInlining = true;
-                      options.enableLambdaMerging = true;
-                    }));
+        TestShrinkerBuilder::addDontWarnJetBrainsNotNullAnnotation);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java
index 5032ea2..087a81b 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java
@@ -3,10 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.lambda;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
-
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
 import com.android.tools.r8.utils.BooleanUtils;
 import java.util.Collection;
@@ -17,15 +14,16 @@
 @RunWith(Parameterized.class)
 public class KotlinLambdaMergingWithSmallInliningBudgetTest extends AbstractR8KotlinTestBase {
 
-  @Parameterized.Parameters(name = "target: {0}, kotlinc: {1}, allowAccessModification: {2}")
+  @Parameterized.Parameters(name = "{0}, allowAccessModification: {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
-        KotlinTargetVersion.values(), getKotlinCompilers(), BooleanUtils.values());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
+        BooleanUtils.values());
   }
 
   public KotlinLambdaMergingWithSmallInliningBudgetTest(
-      KotlinTargetVersion targetVersion, KotlinCompiler kotlinc, boolean allowAccessModification) {
-    super(targetVersion, kotlinc, allowAccessModification);
+      KotlinTestParameters kotlinParameters, boolean allowAccessModification) {
+    super(kotlinParameters, allowAccessModification);
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdasInInput.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdasInInput.java
new file mode 100644
index 0000000..3517ff3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdasInInput.java
@@ -0,0 +1,125 @@
+// 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.kotlin.lambda;
+
+import static junit.framework.TestCase.assertTrue;
+
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.SetUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class KotlinLambdasInInput {
+
+  private final Set<ClassReference> jStyleLambdas;
+  private final Set<ClassReference> kStyleLambdas;
+
+  private KotlinLambdasInInput(
+      Set<ClassReference> jStyleLambdas, Set<ClassReference> kStyleLambdas) {
+    this.jStyleLambdas = jStyleLambdas;
+    this.kStyleLambdas = kStyleLambdas;
+  }
+
+  public static KotlinLambdasInInput create(List<Path> programFiles, String testName)
+      throws IOException {
+    CodeInspector inputInspector = new CodeInspector(programFiles);
+    Set<ClassReference> jStyleLambdas = new HashSet<>();
+    Set<ClassReference> kStyleLambdas = new HashSet<>();
+    for (FoundClassSubject classSubject : inputInspector.allClasses()) {
+      DexProgramClass clazz = classSubject.getDexProgramClass();
+      if (!clazz.getType().getPackageName().startsWith(testName)) {
+        continue;
+      }
+      if (internalIsJStyleLambda(clazz)) {
+        jStyleLambdas.add(Reference.classFromTypeName(clazz.getTypeName()));
+      } else if (internalIsKStyleLambda(clazz)) {
+        kStyleLambdas.add(Reference.classFromTypeName(clazz.getTypeName()));
+      }
+    }
+    return new KotlinLambdasInInput(jStyleLambdas, kStyleLambdas);
+  }
+
+  private static boolean internalIsKStyleLambda(DexProgramClass clazz) {
+    return clazz.getSuperType().getTypeName().equals("kotlin.jvm.internal.Lambda");
+  }
+
+  private static boolean internalIsJStyleLambda(DexProgramClass clazz) {
+    if (!clazz.getSuperType().getTypeName().equals(Object.class.getTypeName())
+        || clazz.getInterfaces().size() != 1
+        || clazz.getMethodCollection().numberOfVirtualMethods() == 0) {
+      return false;
+    }
+    if (clazz
+        .getMethodCollection()
+        .hasDirectMethods(method -> method.isStatic() && !method.isClassInitializer())) {
+      return false;
+    }
+    int numberOfFinalNonBridgeNonSyntheticMethods = 0;
+    for (DexEncodedMethod method : clazz.virtualMethods()) {
+      if (method.isFinal() && !method.isBridge() && !method.isSyntheticMethod()) {
+        numberOfFinalNonBridgeNonSyntheticMethods++;
+      }
+    }
+    return numberOfFinalNonBridgeNonSyntheticMethods == 1;
+  }
+
+  public Set<ClassReference> getAllLambdas() {
+    return SetUtils.newIdentityHashSet(jStyleLambdas, kStyleLambdas);
+  }
+
+  public Set<ClassReference> getJStyleLambdas() {
+    return jStyleLambdas;
+  }
+
+  public ClassReference getJStyleLambdaReferenceFromTypeName(String testName, String simpleName) {
+    ClassReference classReference = Reference.classFromTypeName(testName + "." + simpleName);
+    assertTrue(jStyleLambdas.contains(classReference));
+    return classReference;
+  }
+
+  public Set<ClassReference> getKStyleLambdas() {
+    return kStyleLambdas;
+  }
+
+  public ClassReference getKStyleLambdaReferenceFromTypeName(String testName, String simpleName) {
+    ClassReference classReference = Reference.classFromTypeName(testName + "." + simpleName);
+    assertTrue(
+        "Class is not a Kotlin-style lambda: " + classReference.getTypeName(),
+        kStyleLambdas.contains(classReference));
+    return classReference;
+  }
+
+  public int getNumberOfJStyleLambdas() {
+    return jStyleLambdas.size();
+  }
+
+  public int getNumberOfKStyleLambdas() {
+    return kStyleLambdas.size();
+  }
+
+  public boolean isJStyleLambda(ClassReference classReference) {
+    return jStyleLambdas.contains(classReference);
+  }
+
+  public boolean isKStyleLambda(ClassReference classReference) {
+    return kStyleLambdas.contains(classReference);
+  }
+
+  public void print() {
+    System.out.println("Java-style Kotlin lambdas:");
+    jStyleLambdas.forEach(lambda -> System.out.println(lambda.getTypeName()));
+    System.out.println();
+    System.out.println("Kotlin-style Kotlin lambdas:");
+    kStyleLambdas.forEach(lambda -> System.out.println(lambda.getTypeName()));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinxMetadataExtensionsServiceTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinxMetadataExtensionsServiceTest.java
deleted file mode 100644
index 26cc829..0000000
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinxMetadataExtensionsServiceTest.java
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.kotlin.lambda;
-
-import static com.android.tools.r8.ToolHelper.EXAMPLES_KOTLIN_RESOURCE_DIR;
-import static com.android.tools.r8.kotlin.lambda.KotlinLambdaMergingTest.kstyle;
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.MatcherAssert.assertThat;
-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.ToolHelper;
-import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.kotlin.lambda.KotlinLambdaMergingTest.Group;
-import com.android.tools.r8.kotlin.lambda.KotlinLambdaMergingTest.Lambda;
-import com.android.tools.r8.kotlin.lambda.KotlinLambdaMergingTest.Verifier;
-import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.google.common.collect.ImmutableList;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.List;
-import org.junit.Test;
-
-public class KotlinxMetadataExtensionsServiceTest extends TestBase {
-
-  private void forkR8_kstyle_trivial(boolean allowAccessModification) throws Exception {
-    if (!isRunR8Jar()) {
-      return;
-    }
-    Path working = temp.getRoot().toPath();
-    Path kotlinJar =
-        Paths.get(EXAMPLES_KOTLIN_RESOURCE_DIR, "JAVA_8", "lambdas_kstyle_trivial.jar")
-            .toAbsolutePath();
-    Path output = working.resolve("classes.dex");
-    assertFalse(Files.exists(output));
-    Path proguardConfiguration = temp.newFile("test.conf").toPath();
-    List<String> lines = ImmutableList.of(
-        "-keepattributes Signature,InnerClasses,EnclosingMethod",
-        "-keep class **MainKt {",
-        "  public static void main(...);",
-        "}",
-        "-printmapping",
-        "-dontobfuscate",
-        allowAccessModification ? "-allowaccessmodification" : ""
-    );
-    FileUtils.writeTextFile(proguardConfiguration, lines);
-    ProcessResult result = ToolHelper.forkR8Jar(working,
-        "--pg-conf", proguardConfiguration.toString(),
-        "--lib", ToolHelper.getAndroidJar(AndroidApiLevel.O).toAbsolutePath().toString(),
-        kotlinJar.toString());
-    assertEquals(0, result.exitCode);
-    assertThat(result.stderr, not(containsString(
-        "No MetadataExtensions instances found in the classpath")));
-    assertTrue(Files.exists(output));
-
-    CodeInspector inspector = new CodeInspector(output);
-    Verifier verifier = new Verifier(inspector);
-    String pkg = "lambdas_kstyle_trivial";
-    verifier.assertLambdaGroups(
-        allowAccessModification
-            ? new Group[] {
-              kstyle("", 0, 4),
-              kstyle("", 1, 9),
-              kstyle("", 2, 2), // -\
-              kstyle("", 2, 5), // - 3 groups different by main method
-              kstyle("", 2, 4), // -/
-              kstyle("", 3, 2),
-              kstyle("", 22, 2)
-            }
-            : new Group[] {
-              kstyle(pkg, 0, 2),
-              kstyle(pkg, 1, 5),
-              kstyle(pkg, 2, 5), // - 2 groups different by main method
-              kstyle(pkg, 2, 4), // -/
-              kstyle(pkg, 3, 2),
-              kstyle(pkg, 22, 2),
-              kstyle(pkg + "/inner", 0, 2),
-              kstyle(pkg + "/inner", 1, 4)
-            });
-
-    verifier.assertLambdas(
-        allowAccessModification
-            ? new Lambda[] {}
-            : new Lambda[] {
-              new Lambda(pkg, "MainKt$testStateless$8", 2),
-              new Lambda(pkg + "/inner", "InnerKt$testInnerStateless$7", 2)
-            });
-  }
-
-  @Test
-  public void testTrivialKs_allowAccessModification() throws Exception {
-    forkR8_kstyle_trivial(true);
-  }
-
-  @Test
-  public void testTrivialKs_notAllowAccessModification() throws Exception {
-    forkR8_kstyle_trivial(false);
-  }
-
-}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/B148525512.java b/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/B148525512.java
index e9f4e46..ac22198 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/B148525512.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/B148525512.java
@@ -4,31 +4,19 @@
 
 package com.android.tools.r8.kotlin.lambda.b148525512;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static junit.framework.TestCase.assertEquals;
-import static junit.framework.TestCase.assertTrue;
 import static org.hamcrest.CoreMatchers.equalTo;
-import static org.hamcrest.MatcherAssert.assertThat;
 
 import com.android.tools.r8.DexIndexedConsumer.ArchiveConsumer;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
 import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.R8TestCompileResult;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.utils.ArchiveResourceProvider;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
-import com.android.tools.r8.utils.codeinspector.InstructionSubject;
-import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.Collection;
-import java.util.List;
-import java.util.stream.Collectors;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -57,17 +45,15 @@
               });
   private final TestParameters parameters;
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withDexRuntimes().withAllApiLevels().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
-  public B148525512(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+  public B148525512(TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
@@ -84,35 +70,6 @@
     }
   }
 
-  private void checkLambdaGroups(CodeInspector inspector) {
-    List<FoundClassSubject> lambdaGroups =
-        inspector.allClasses().stream()
-            .filter(clazz -> clazz.getOriginalName().contains("LambdaGroup"))
-            .collect(Collectors.toList());
-    assertEquals(1, lambdaGroups.size());
-    MethodSubject invokeMethod = lambdaGroups.get(0).uniqueMethodWithName("invoke");
-    assertThat(invokeMethod, isPresent());
-    // The lambda group has 2 captures which capture "Base".
-    assertEquals(
-        2,
-        invokeMethod
-            .streamInstructions()
-            .filter(InstructionSubject::isCheckCast)
-            .filter(
-                instruction ->
-                    instruction.asCheckCast().getType().toSourceString().contains("Base"))
-            .count());
-    // The lambda group has no captures which capture "Feature" (lambdas in the feature are not
-    // in this lambda group).
-    assertTrue(
-        invokeMethod
-            .streamInstructions()
-            .filter(InstructionSubject::isCheckCast)
-            .noneMatch(
-                instruction ->
-                    instruction.asCheckCast().getType().toSourceString().contains("Feature")));
-  }
-
   @Test
   public void test() throws Exception {
     Path featureCode = temp.newFile("feature.zip").toPath();
@@ -125,10 +82,16 @@
             .addKeepClassAndMembersRules(baseClassName)
             .addKeepClassAndMembersRules(featureKtClassNamet)
             .addKeepClassAndMembersRules(FeatureAPI.class)
-            .addOptionsModification(
-                options -> options.horizontalClassMergerOptions().disableKotlinLambdaMerging())
+            .addHorizontallyMergedClassesInspector(
+                inspector ->
+                    inspector
+                        .assertIsCompleteMergeGroup(
+                            "com.android.tools.r8.kotlin.lambda.b148525512.BaseKt$main$1",
+                            "com.android.tools.r8.kotlin.lambda.b148525512.BaseKt$main$2")
+                        .assertIsCompleteMergeGroup(
+                            "com.android.tools.r8.kotlin.lambda.b148525512.FeatureKt$feature$1",
+                            "com.android.tools.r8.kotlin.lambda.b148525512.FeatureKt$feature$2"))
             .setMinApi(parameters.getApiLevel())
-            .noMinification() // The check cast inspection above relies on original names.
             .addFeatureSplit(
                 builder ->
                     builder
@@ -142,10 +105,9 @@
             .addDontWarnJetBrainsNotNullAnnotation()
             .compile()
             .assertAllWarningMessagesMatch(
-                equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
-            .inspect(this::checkLambdaGroups);
+                equalTo("Resource 'META-INF/MANIFEST.MF' already exists."));
 
-    // Run the code without the feature code present.
+    // Run the code without the feature code.
     compileResult
         .run(parameters.getRuntime(), baseKtClassName)
         .assertSuccessWithOutputLines("1", "2");
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaGroupGCLimitTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaGroupGCLimitTest.java
index c33d03b..8d476be 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaGroupGCLimitTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaGroupGCLimitTest.java
@@ -11,21 +11,19 @@
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.R8FullTestBuilder;
 import com.android.tools.r8.R8TestCompileResult;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.InternalOptions.HorizontalClassMergerOptions;
-import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import com.google.common.collect.ImmutableList;
 import java.io.IOException;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
-import java.util.stream.Collectors;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -34,36 +32,61 @@
 @RunWith(Parameterized.class)
 public class LambdaGroupGCLimitTest extends TestBase {
 
-  private final boolean enableHorizontalClassMergingOfKotlinLambdas;
-  private final TestParameters parameters;
-  private final int LAMBDA_HOLDER_LIMIT = 50;
-  private final int LAMBDAS_PER_CLASS_LIMIT = 100;
+  private static final int LAMBDA_HOLDER_LIMIT = 50;
+  private static final int LAMBDAS_PER_CLASS_LIMIT = 100;
 
-  @Parameters(name = "{1}, horizontal class merging: {0}")
-  public static List<Object[]> data() {
-    return buildParameters(
-        BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDexRuntimes().withAllApiLevels().build();
   }
 
-  public LambdaGroupGCLimitTest(
-      boolean enableHorizontalClassMergingOfKotlinLambdas, TestParameters parameters) {
-    this.enableHorizontalClassMergingOfKotlinLambdas = enableHorizontalClassMergingOfKotlinLambdas;
+  public LambdaGroupGCLimitTest(TestParameters parameters) {
     this.parameters = parameters;
   }
 
   @Test
   public void testR8() throws ExecutionException, CompilationFailedException, IOException {
     String PKG_NAME = LambdaGroupGCLimitTest.class.getPackage().getName();
-    R8FullTestBuilder testBuilder =
+    R8TestCompileResult compileResult =
         testForR8(parameters.getBackend())
-            .addProgramFiles(ToolHelper.getKotlinStdlibJar(ToolHelper.getKotlinC_1_3_72()))
-            .addOptionsModification(
-                options ->
-                    options
-                        .horizontalClassMergerOptions()
-                        .enableKotlinLambdaMergingIf(enableHorizontalClassMergingOfKotlinLambdas))
+            .addProgramFiles(getProgramFiles())
             .setMinApi(parameters.getApiLevel())
-            .noMinification();
+            .apply(
+                builder -> {
+                  for (int mainId = 0; mainId < LAMBDA_HOLDER_LIMIT; mainId++) {
+                    builder.addKeepClassAndMembersRules(PKG_NAME + ".MainKt" + mainId);
+                  }
+                })
+            .addDontWarnJetBrainsNotNullAnnotation()
+            .addHorizontallyMergedClassesInspector(
+                inspector -> {
+                  HorizontalClassMergerOptions defaultHorizontalClassMergerOptions =
+                      new HorizontalClassMergerOptions();
+                  assertEquals(4833, inspector.getSources().size());
+                  assertEquals(167, inspector.getTargets().size());
+                  assertTrue(
+                      inspector.getMergeGroups().stream()
+                          .allMatch(
+                              mergeGroup ->
+                                  mergeGroup.size()
+                                      <= defaultHorizontalClassMergerOptions.getMaxGroupSize()));
+                })
+            .compile();
+    Path path = compileResult.writeToZip();
+    compileResult
+        .run(parameters.getRuntime(), PKG_NAME + ".MainKt0")
+        .assertSuccessWithOutputLines("3");
+    Path oatFile = temp.newFile("out.oat").toPath();
+    ProcessResult processResult =
+        ToolHelper.runDex2OatRaw(path, oatFile, parameters.getRuntime().asDex().getVm());
+    assertEquals(0, processResult.exitCode);
+    assertThat(
+        processResult.stderr, not(containsString("Method exceeds compiler instruction limit")));
+  }
+
+  private List<Path> getProgramFiles() throws IOException {
     Path classFiles = temp.newFile("classes.jar").toPath();
     List<byte[]> classFileData = new ArrayList<>();
     for (int mainId = 0; mainId < LAMBDA_HOLDER_LIMIT; mainId++) {
@@ -71,50 +94,9 @@
       for (int lambdaId = 0; lambdaId < LAMBDAS_PER_CLASS_LIMIT; lambdaId++) {
         classFileData.add(MainKt$main$1Dump.dump(mainId, lambdaId));
       }
-      testBuilder.addKeepClassAndMembersRules(PKG_NAME + ".MainKt" + mainId);
     }
     writeClassFileDataToJar(classFiles, classFileData);
-    R8TestCompileResult compileResult =
-        testBuilder
-            .addProgramFiles(classFiles)
-            .addHorizontallyMergedClassesInspector(
-                inspector -> {
-                  if (enableHorizontalClassMergingOfKotlinLambdas) {
-                    HorizontalClassMergerOptions defaultHorizontalClassMergerOptions =
-                        new HorizontalClassMergerOptions();
-                    assertEquals(4833, inspector.getSources().size());
-                    assertEquals(167, inspector.getTargets().size());
-                    assertTrue(
-                        inspector.getMergeGroups().stream()
-                            .allMatch(
-                                mergeGroup ->
-                                    mergeGroup.size()
-                                        <= defaultHorizontalClassMergerOptions.getMaxGroupSize()));
-                  } else {
-                    inspector.assertNoClassesMerged();
-                  }
-                })
-            .addDontWarnJetBrainsNotNullAnnotation()
-            .compile();
-    Path path = compileResult.writeToZip();
-    compileResult
-        .run(parameters.getRuntime(), PKG_NAME + ".MainKt0")
-        .assertSuccessWithOutputLines("3")
-        .inspect(
-            codeInspector -> {
-              List<FoundClassSubject> lambdaGroups =
-                  codeInspector.allClasses().stream()
-                      .filter(c -> c.getFinalName().contains("LambdaGroup"))
-                      .collect(Collectors.toList());
-              assertEquals(
-                  1 - BooleanUtils.intValue(enableHorizontalClassMergingOfKotlinLambdas),
-                  lambdaGroups.size());
-            });
-    Path oatFile = temp.newFile("out.oat").toPath();
-    ProcessResult processResult =
-        ToolHelper.runDex2OatRaw(path, oatFile, parameters.getRuntime().asDex().getVm());
-    assertEquals(0, processResult.exitCode);
-    assertThat(
-        processResult.stderr, not(containsString("Method exceeds compiler instruction limit")));
+    return ImmutableList.of(
+        classFiles, ToolHelper.getKotlinStdlibJar(ToolHelper.getKotlinC_1_3_72()));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java
index 663c081..79cef64 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java
@@ -4,24 +4,25 @@
 
 package com.android.tools.r8.kotlin.lambda.b159688129;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestRuntime;
 import com.android.tools.r8.TestRuntime.CfRuntime;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import java.nio.file.Path;
 import java.util.List;
-import java.util.stream.Collectors;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -31,26 +32,20 @@
 public class LambdaSplitByCodeCorrectnessTest extends AbstractR8KotlinTestBase {
 
   private final TestParameters parameters;
-  private final KotlinTargetVersion targetVersion;
   private final boolean splitGroup;
 
-  @Parameters(name = "{0}, kotlinc: {2} targetVersion: {1}, splitGroup: {3}")
+  @Parameters(name = "{0}, {1}, splitGroup: {2}")
   public static List<Object[]> data() {
     return buildParameters(
         getTestParameters().withDexRuntimes().withAllApiLevels().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers(),
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
         BooleanUtils.values());
   }
 
   public LambdaSplitByCodeCorrectnessTest(
-      TestParameters parameters,
-      KotlinTargetVersion targetVersion,
-      KotlinCompiler kotlinc,
-      boolean splitGroup) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters, boolean splitGroup) {
+    super(kotlinParameters);
     this.parameters = parameters;
-    this.targetVersion = targetVersion;
     this.splitGroup = splitGroup;
   }
 
@@ -67,8 +62,6 @@
     testForR8(parameters.getBackend())
         .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
         .addProgramFiles(ktClasses)
-        .addOptionsModification(
-            options -> options.horizontalClassMergerOptions().disableKotlinLambdaMerging())
         .setMinApi(parameters.getApiLevel())
         .addKeepMainRule(PKG_NAME + ".SimpleKt")
         .addDontWarnJetBrainsNotNullAnnotation()
@@ -77,25 +70,40 @@
             b ->
                 b.addOptionsModification(
                     internalOptions ->
-                        // Setting verificationSizeLimitInBytesOverride = 1 will force a a chain
-                        // having only a single implementation method in each.
-                        internalOptions.testing.verificationSizeLimitInBytesOverride =
-                            splitGroup ? 1 : -1))
-        .noMinification()
+                        // Setting inliningInstructionAllowance = 1 will force each switch branch to
+                        // contain an invoke instruction to a private method.
+                        internalOptions.inliningInstructionAllowance = 1))
+        .addHorizontallyMergedClassesInspector(
+            inspector ->
+                inspector.assertIsCompleteMergeGroup(
+                    "com.android.tools.r8.kotlin.lambda.b159688129.SimpleKt$main$1",
+                    "com.android.tools.r8.kotlin.lambda.b159688129.SimpleKt$main$2",
+                    "com.android.tools.r8.kotlin.lambda.b159688129.SimpleKt$main$3",
+                    "com.android.tools.r8.kotlin.lambda.b159688129.SimpleKt$main$4",
+                    "com.android.tools.r8.kotlin.lambda.b159688129.SimpleKt$main$5",
+                    "com.android.tools.r8.kotlin.lambda.b159688129.SimpleKt$main$6"))
         .allowDiagnosticWarningMessages()
         .compile()
         .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
         .inspect(
             codeInspector -> {
-              List<FoundClassSubject> lambdaGroups =
-                  codeInspector.allClasses().stream()
-                      .filter(c -> c.getFinalName().contains("LambdaGroup"))
-                      .collect(Collectors.toList());
-              assertEquals(1, lambdaGroups.size());
-              FoundClassSubject lambdaGroup = lambdaGroups.get(0);
-              List<FoundMethodSubject> invokeChain =
-                  lambdaGroup.allMethods(method -> method.getFinalName().contains("invoke$"));
-              assertEquals(splitGroup ? 5 : 0, invokeChain.size());
+              ClassSubject mergeTarget =
+                  codeInspector.clazz(
+                      "com.android.tools.r8.kotlin.lambda.b159688129.SimpleKt$main$1");
+              assertThat(mergeTarget, isPresent());
+
+              MethodSubject virtualMethodSubject =
+                  mergeTarget.uniqueMethodThatMatches(
+                      method -> method.isVirtual() && !method.isSynthetic());
+              assertThat(virtualMethodSubject, isPresent());
+
+              int found = 0;
+              for (FoundMethodSubject directMethodSubject :
+                  mergeTarget.allMethods(x -> x.isPrivate() && !x.isSynthetic())) {
+                assertThat(virtualMethodSubject, invokesMethod(directMethodSubject));
+                found++;
+              }
+              assertEquals(splitGroup ? 6 : 0, found);
             })
         .run(parameters.getRuntime(), PKG_NAME + ".SimpleKt")
         .assertSuccessWithOutputLines("Hello1", "Hello2", "Hello3", "Hello4", "Hello5", "Hello6");
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java b/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java
index e158fc7..3765187 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java
@@ -8,8 +8,7 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertNull;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
 import com.android.tools.r8.kotlin.KotlinMetadataWriter;
 import com.android.tools.r8.utils.DescriptorUtils;
@@ -22,8 +21,8 @@
 
 public abstract class KotlinMetadataTestBase extends AbstractR8KotlinTestBase {
 
-  public KotlinMetadataTestBase(KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+  public KotlinMetadataTestBase(KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
   }
 
   static final String PKG = KotlinMetadataTestBase.class.getPackage().getName();
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrimitiveTypeRewriteTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrimitiveTypeRewriteTest.java
index b4fd614..b0a30d7 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrimitiveTypeRewriteTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrimitiveTypeRewriteTest.java
@@ -4,17 +4,15 @@
 
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import com.android.tools.r8.JvmTestRunResult;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -32,17 +30,16 @@
   private static final String PKG_LIB = PKG + ".primitive_type_rewrite_lib";
   private static final String PKG_APP = PKG + ".primitive_type_rewrite_app";
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, compiler: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public MetadataPrimitiveTypeRewriteTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrunedFieldsTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrunedFieldsTest.java
index a048351..09c2a9d 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrunedFieldsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrunedFieldsTest.java
@@ -4,16 +4,14 @@
 
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.kotlin.metadata.metadata_pruned_fields.Main;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -28,17 +26,16 @@
 
   private final TestParameters parameters;
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public MetadataPrunedFieldsTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAllowAccessModificationTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAllowAccessModificationTest.java
index b6d7701..71bd275 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAllowAccessModificationTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAllowAccessModificationTest.java
@@ -4,16 +4,14 @@
 
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static junit.framework.TestCase.assertEquals;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.core.StringContains.containsString;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.StringUtils;
@@ -52,17 +50,16 @@
           "staticPrivate",
           "staticInternal");
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public MetadataRewriteAllowAccessModificationTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnnotationTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnnotationTest.java
index a285149..ebe0dac 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnnotationTest.java
@@ -4,17 +4,15 @@
 
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.StringUtils;
@@ -58,17 +56,16 @@
   private static final String FOO_ORIGINAL_NAME = PKG_LIB + ".Foo";
   private static final String FOO_FINAL_NAME = "a.b.c";
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public MetadataRewriteAnnotationTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnonymousTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnonymousTest.java
index 8db1944..374c3fd 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnonymousTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnonymousTest.java
@@ -4,14 +4,12 @@
 
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
 import static org.hamcrest.MatcherAssert.assertThat;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.kotlin.KotlinMetadataWriter;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -27,17 +25,16 @@
 
   private final String EXPECTED = "foo";
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public MetadataRewriteAnonymousTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteBoxedTypesTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteBoxedTypesTest.java
index 4e7d224..16d3765 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteBoxedTypesTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteBoxedTypesTest.java
@@ -4,17 +4,15 @@
 
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static junit.framework.TestCase.assertEquals;
 import static junit.framework.TestCase.assertNotNull;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertNull;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.kotlin.KotlinMetadataWriter;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.StringUtils;
@@ -37,17 +35,16 @@
   private final String EXPECTED =
       StringUtils.lines("false", "0", "a", "0.042", "0.42", "42", "442", "1", "2", "42", "42");
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public MetadataRewriteBoxedTypesTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineAnonFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineAnonFunctionTest.java
index 065d63f..989512b 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineAnonFunctionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineAnonFunctionTest.java
@@ -4,12 +4,9 @@
 
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
-
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.StringUtils;
 import java.nio.file.Path;
@@ -25,17 +22,16 @@
   private static final String PKG_LIB = PKG + ".crossinline_anon_lib";
   private static final String PKG_APP = PKG + ".crossinline_anon_app";
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public MetadataRewriteCrossinlineAnonFunctionTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineConcreteFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineConcreteFunctionTest.java
index 15f68ea..d969f34 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineConcreteFunctionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineConcreteFunctionTest.java
@@ -4,12 +4,9 @@
 
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
-
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.StringUtils;
 import java.nio.file.Path;
@@ -25,17 +22,16 @@
   private static final String PKG_LIB = PKG + ".crossinline_concrete_lib";
   private static final String PKG_APP = PKG + ".crossinline_concrete_app";
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public MetadataRewriteCrossinlineConcreteFunctionTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java
index b43f846..bc8051d 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java
@@ -4,12 +4,9 @@
 
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
-
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.StringUtils;
@@ -33,17 +30,16 @@
           "null",
           "New value has been read in CustomDelegate from 'x'");
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public MetadataRewriteDelegatedPropertyTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDependentKeep.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDependentKeep.java
index 2ccbb3a..3f42968 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDependentKeep.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDependentKeep.java
@@ -4,15 +4,13 @@
 
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 
 import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -27,19 +25,18 @@
 @RunWith(Parameterized.class)
 public class MetadataRewriteDependentKeep extends KotlinMetadataTestBase {
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   private final TestParameters parameters;
 
   public MetadataRewriteDependentKeep(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDoNotEmitValuesIfEmpty.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDoNotEmitValuesIfEmpty.java
index feca0f8..1caec7d 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDoNotEmitValuesIfEmpty.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDoNotEmitValuesIfEmpty.java
@@ -4,14 +4,12 @@
 
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.graph.DexAnnotationElement;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.codeinspector.AnnotationSubject;
@@ -29,19 +27,18 @@
 
   private final Set<String> nullableFieldKeys = Sets.newHashSet("pn", "xs", "xi");
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   private final TestParameters parameters;
 
   public MetadataRewriteDoNotEmitValuesIfEmpty(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteFlexibleUpperBoundTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteFlexibleUpperBoundTest.java
index fbfc10f..91ede26 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteFlexibleUpperBoundTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteFlexibleUpperBoundTest.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
@@ -12,10 +11,9 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -39,17 +37,16 @@
   private final String EXPECTED = StringUtils.lines("B.foo(): 42");
   private final String PKG_LIB = PKG + ".flexible_upper_bound_lib";
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public MetadataRewriteFlexibleUpperBoundTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java
index c7b9ea6..eebb77f 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -11,10 +10,9 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -35,17 +33,16 @@
 
   private final TestParameters parameters;
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public MetadataRewriteInClasspathTypeTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java
index bdcb905..9106a3b 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
@@ -12,10 +11,9 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -44,17 +42,16 @@
 
   private final TestParameters parameters;
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public MetadataRewriteInCompanionTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
index f9124bd..f46402b 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -13,10 +12,9 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -41,17 +39,16 @@
 
   private final TestParameters parameters;
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public MetadataRewriteInExtensionFunctionTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java
index c78cea1..c2a923f 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionProperty;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
@@ -14,10 +13,9 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -40,17 +38,16 @@
 
   private final TestParameters parameters;
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public MetadataRewriteInExtensionPropertyTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java
index 5c8468d..48044e2 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -13,10 +12,9 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -39,17 +37,16 @@
 
   private final TestParameters parameters;
 
-  @Parameterized.Parameters(name = "{0} target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public MetadataRewriteInFunctionTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithDefaultValueTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithDefaultValueTest.java
index db348d0..746c690 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithDefaultValueTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithDefaultValueTest.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -12,10 +11,9 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -39,17 +37,16 @@
 
   private final TestParameters parameters;
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public MetadataRewriteInFunctionWithDefaultValueTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java
index 65207dd..262d8a2 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -13,10 +12,9 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -40,17 +38,16 @@
 
   private final TestParameters parameters;
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public MetadataRewriteInFunctionWithVarargTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java
index f3c59d9..d583ea7 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
 import static org.hamcrest.CoreMatchers.anyOf;
@@ -11,10 +10,9 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -32,17 +30,16 @@
 
   private final TestParameters parameters;
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public MetadataRewriteInLibraryTypeTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInMultifileClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInMultifileClassTest.java
index 3949d6e..6411cfc 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInMultifileClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInMultifileClassTest.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -16,10 +15,9 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.DescriptorUtils;
@@ -44,17 +42,16 @@
 
   private final TestParameters parameters;
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public MetadataRewriteInMultifileClassTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInNestedClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInNestedClassTest.java
index 8bb4603..f2aa77d 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInNestedClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInNestedClassTest.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.DescriptorUtils.descriptorToJavaType;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -12,10 +11,9 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -34,17 +32,16 @@
 
   private final TestParameters parameters;
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public MetadataRewriteInNestedClassTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java
index f84fa48..abce684 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java
@@ -3,17 +3,15 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -32,17 +30,16 @@
 
   private final TestParameters parameters;
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public MetadataRewriteInParameterTypeTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
index af4c9b6..693c7fc 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionProperty;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -14,10 +13,9 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -38,17 +36,16 @@
 
   private final TestParameters parameters;
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public MetadataRewriteInPropertyTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java
index 0113f19..cb18acb 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java
@@ -3,17 +3,15 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -32,17 +30,16 @@
 
   private final TestParameters parameters;
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public MetadataRewriteInPropertyTypeTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInRenamedTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInRenamedTypeTest.java
index a4a3a68..7badf74 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInRenamedTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInRenamedTypeTest.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.DescriptorUtils.getDescriptorFromKotlinClassifier;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -12,10 +11,9 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.codeinspector.AnnotationSubject;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -33,17 +31,16 @@
 
   private final TestParameters parameters;
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public MetadataRewriteInRenamedTypeTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java
index c8144f4..dbaf933 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java
@@ -3,17 +3,15 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -32,17 +30,16 @@
 
   private final TestParameters parameters;
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public MetadataRewriteInReturnTypeTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassNestedTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassNestedTest.java
index db927a8..e479bba 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassNestedTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassNestedTest.java
@@ -3,12 +3,9 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
-
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.StringUtils;
@@ -31,17 +28,16 @@
 
   private final TestParameters parameters;
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public MetadataRewriteInSealedClassNestedTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java
index ef43f6e..5156b72 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.DescriptorUtils.descriptorToJavaType;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
@@ -15,10 +14,9 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.StringUtils;
@@ -39,17 +37,16 @@
 
   private final TestParameters parameters;
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public MetadataRewriteInSealedClassTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java
index 9b0da43..ccc94e5 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
@@ -11,10 +10,9 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -33,17 +31,16 @@
 
   private final TestParameters parameters;
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public MetadataRewriteInSuperTypeTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java
index e680d6e..98102c5 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isDexClass;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -13,10 +12,9 @@
 import static junit.framework.TestCase.assertTrue;
 import static org.hamcrest.MatcherAssert.assertThat;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.kotlin.Kotlin.ClassClassifiers;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.DescriptorUtils;
@@ -61,17 +59,16 @@
 
   private final TestParameters parameters;
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public MetadataRewriteInTypeAliasTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java
index acdda5b..097c832 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isDexClass;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
@@ -12,10 +11,9 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -77,17 +75,16 @@
 
   private final TestParameters parameters;
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public MetadataRewriteInTypeArgumentsTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInlinePropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInlinePropertyTest.java
index ac232d7..d5e9e1a 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInlinePropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInlinePropertyTest.java
@@ -4,15 +4,13 @@
 
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertNull;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.kotlin.KotlinMetadataWriter;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.StringUtils;
@@ -34,17 +32,16 @@
 
   private final String EXPECTED = StringUtils.lines("true", "false", "false", "true");
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public MetadataRewriteInlinePropertyTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteJvmStaticTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteJvmStaticTest.java
index 0558ebc..e03a72c 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteJvmStaticTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteJvmStaticTest.java
@@ -3,12 +3,11 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
@@ -40,12 +39,18 @@
 
   @Parameterized.Parameters(name = "{0}, kotlinc: {1}")
   public static List<Object[]> data() {
-    return buildParameters(getTestParameters().withCfRuntimes().build(), getKotlinCompilers());
+    // We are testing static methods on interfaces which requires java 8.
+    return buildParameters(
+        getTestParameters().withCfRuntimes().build(),
+        getKotlinTestParameters()
+            .withAllCompilers()
+            .withTargetVersion(KotlinTargetVersion.JAVA_8)
+            .build());
   }
 
-  public MetadataRewriteJvmStaticTest(TestParameters parameters, KotlinCompiler kotlinc) {
-    // We are testing static methods on interfaces which requires java 8.
-    super(KotlinTargetVersion.JAVA_8, kotlinc);
+  public MetadataRewriteJvmStaticTest(
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepPathTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepPathTest.java
index 056b18b..569d2b7 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepPathTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepPathTest.java
@@ -4,17 +4,15 @@
 
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestShrinkerBuilder;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -26,12 +24,11 @@
 @RunWith(Parameterized.class)
 public class MetadataRewriteKeepPathTest extends KotlinMetadataTestBase {
 
-  @Parameterized.Parameters(name = "{0} target: {1}, kotlinc: {2}, keep: {3}")
+  @Parameterized.Parameters(name = "{0}, {1}, keep: {2}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers(),
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
         BooleanUtils.values());
   }
 
@@ -42,11 +39,8 @@
   private final boolean keepMetadata;
 
   public MetadataRewriteKeepPathTest(
-      TestParameters parameters,
-      KotlinTargetVersion targetVersion,
-      KotlinCompiler kotlinc,
-      boolean keepMetadata) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters, boolean keepMetadata) {
+    super(kotlinParameters);
     this.parameters = parameters;
     this.keepMetadata = keepMetadata;
   }
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepTest.java
index 74159a6..c8dd873 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepTest.java
@@ -4,14 +4,12 @@
 
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
@@ -23,19 +21,17 @@
 @RunWith(Parameterized.class)
 public class MetadataRewriteKeepTest extends KotlinMetadataTestBase {
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   private final TestParameters parameters;
 
-  public MetadataRewriteKeepTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+  public MetadataRewriteKeepTest(TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java
index 0b65ec3..99586b1 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java
@@ -4,12 +4,9 @@
 
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
-
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import java.util.Collection;
@@ -20,19 +17,18 @@
 @RunWith(Parameterized.class)
 public class MetadataRewritePassThroughTest extends KotlinMetadataTestBase {
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   private final TestParameters parameters;
 
   public MetadataRewritePassThroughTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java
index 3d3386c..6d4391f 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java
@@ -4,16 +4,14 @@
 
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static junit.framework.TestCase.assertEquals;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -37,17 +35,16 @@
           getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_LIB), "lib"));
   private final TestParameters parameters;
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public MetadataRewritePrunedObjectsTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
index 1a2cd73..8111567 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin.metadata;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
@@ -11,11 +10,10 @@
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.R8TestRunResult;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.codeinspector.AnnotationSubject;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -31,17 +29,15 @@
   private final TestParameters parameters;
   private static final String FOLDER = "lambdas_jstyle_runnable";
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
-  public MetadataStripTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+  public MetadataStripTest(TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataVersionNumberBumpTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataVersionNumberBumpTest.java
index e23ac8f..a7886b2 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataVersionNumberBumpTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataVersionNumberBumpTest.java
@@ -9,9 +9,9 @@
 import static org.junit.Assert.fail;
 import static org.objectweb.asm.Opcodes.ASM7;
 
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.R8FullTestBuilder;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.graph.DexAnnotationElement;
@@ -25,6 +25,7 @@
 import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
 import java.io.IOException;
 import java.util.Arrays;
+import java.util.List;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
 import org.junit.Test;
@@ -39,12 +40,18 @@
   private final TestParameters parameters;
 
   @Parameters(name = "{0}")
-  public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimesAndApiLevels().build(),
+        getKotlinTestParameters()
+            .withCompiler(getKotlinC_1_3_72())
+            .withTargetVersion(KotlinTargetVersion.JAVA_8)
+            .build());
   }
 
-  public MetadataVersionNumberBumpTest(TestParameters parameters) {
-    super(KotlinTargetVersion.JAVA_8, getKotlinC_1_3_72());
+  public MetadataVersionNumberBumpTest(
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java b/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java
index ac7eb23..ae80118 100644
--- a/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java
@@ -4,16 +4,14 @@
 
 package com.android.tools.r8.kotlin.reflection;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.junit.Assume.assumeTrue;
 
 import com.android.tools.r8.DexIndexedConsumer.ArchiveConsumer;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
 import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.shaking.ProguardKeepAttributes;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.FileUtils;
@@ -29,7 +27,6 @@
 public class KotlinReflectTest extends KotlinTestBase {
 
   private final TestParameters parameters;
-  private final KotlinTargetVersion targetVersion;
   private static final String EXPECTED_OUTPUT = "Hello World!";
   private static final String PKG = KotlinReflectTest.class.getPackage().getName();
   private static final KotlinCompileMemoizer compiledJars =
@@ -40,19 +37,16 @@
               DescriptorUtils.getBinaryNameFromJavaType(PKG),
               "SimpleReflect" + FileUtils.KT_EXTENSION));
 
-  @Parameters(name = "{0}, target: {1}, kotlinc: {2}")
+  @Parameters(name = "{0}, {1}")
   public static List<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
-  public KotlinReflectTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+  public KotlinReflectTest(TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
-    this.targetVersion = targetVersion;
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/sealed/SealedClassTest.java b/src/test/java/com/android/tools/r8/kotlin/sealed/SealedClassTest.java
index 92b6b97..47f9463 100644
--- a/src/test/java/com/android/tools/r8/kotlin/sealed/SealedClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/sealed/SealedClassTest.java
@@ -6,15 +6,13 @@
 
 import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
 import static com.android.tools.r8.ToolHelper.getFilesInTestFolderRelativeToClass;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static org.hamcrest.CoreMatchers.containsString;
 
 import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
 import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import java.io.IOException;
 import java.nio.file.Path;
 import java.util.Collection;
@@ -33,17 +31,15 @@
 
   private final TestParameters parameters;
 
-  @Parameters(name = "{0}")
+  @Parameters(name = "{0}, {1}")
   public static List<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
-  public SealedClassTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+  public SealedClassTest(TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/naming/AbstractR8KotlinNamingTestBase.java b/src/test/java/com/android/tools/r8/naming/AbstractR8KotlinNamingTestBase.java
index ddc2da6..2c3f2bb 100644
--- a/src/test/java/com/android/tools/r8/naming/AbstractR8KotlinNamingTestBase.java
+++ b/src/test/java/com/android/tools/r8/naming/AbstractR8KotlinNamingTestBase.java
@@ -7,10 +7,8 @@
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
 import static org.hamcrest.MatcherAssert.assertThat;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
-import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.FieldSubject;
@@ -21,11 +19,10 @@
   protected final boolean minification;
 
   AbstractR8KotlinNamingTestBase(
-      KotlinTargetVersion kotlinTargetVersion,
-      KotlinCompiler kotlinc,
+      KotlinTestParameters kotlinParameters,
       boolean allowAccessModification,
       boolean minification) {
-    super(kotlinTargetVersion, kotlinc, allowAccessModification);
+    super(kotlinParameters, allowAccessModification);
     this.minification = minification;
   }
 
@@ -41,56 +38,27 @@
     return classSubject;
   }
 
-  protected FieldSubject checkFieldIsRenamed(
-      ClassSubject classSubject, String fieldType, String fieldName) {
-    FieldSubject fieldSubject = checkFieldIsKept(classSubject, fieldType, fieldName);
-    assertThat(fieldSubject, isPresentAndRenamed());
-    return fieldSubject;
-  }
-
   protected FieldSubject checkFieldIsRenamed(ClassSubject classSubject, String fieldName) {
     FieldSubject fieldSubject = checkFieldIsKept(classSubject, fieldName);
     assertThat(fieldSubject, isPresentAndRenamed());
     return fieldSubject;
   }
 
-  protected FieldSubject checkFieldIsNotRenamed(
-      ClassSubject classSubject, String fieldType, String fieldName) {
-    FieldSubject fieldSubject = checkFieldIsKept(classSubject, fieldType, fieldName);
-    assertThat(fieldSubject, isPresentAndNotRenamed());
-    return fieldSubject;
-  }
-
   protected FieldSubject checkFieldIsNotRenamed(ClassSubject classSubject, String fieldName) {
     FieldSubject fieldSubject = checkFieldIsKept(classSubject, fieldName);
     assertThat(fieldSubject, isPresentAndNotRenamed());
     return fieldSubject;
   }
 
-  protected MethodSubject checkMethodIsRenamed(
-      ClassSubject classSubject, MethodSignature methodSignature) {
-    MethodSubject methodSubject = checkMethodIsKept(classSubject, methodSignature);
-    assertThat(methodSubject, isPresentAndRenamed());
-    return methodSubject;
-  }
-
   protected MethodSubject checkMethodIsRenamed(ClassSubject classSubject, String methodName) {
     MethodSubject methodSubject = checkMethodIsKept(classSubject, methodName);
     assertThat(methodSubject, isPresentAndRenamed());
     return methodSubject;
   }
 
-  protected MethodSubject checkMethodIsNotRenamed(
-      ClassSubject classSubject, MethodSignature methodSignature) {
-    MethodSubject methodSubject = checkMethodIsKept(classSubject, methodSignature);
-    assertThat(methodSubject, isPresentAndNotRenamed());
-    return methodSubject;
-  }
-
   protected MethodSubject checkMethodIsNotRenamed(ClassSubject classSubject, String methodName) {
     MethodSubject methodSubject = checkMethodIsKept(classSubject, methodName);
     assertThat(methodSubject, isPresentAndNotRenamed());
     return methodSubject;
   }
-
 }
diff --git a/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java b/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java
index b96ff51..f911ce6 100644
--- a/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java
@@ -3,17 +3,15 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.naming;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
 import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -31,21 +29,17 @@
   private final TestParameters parameters;
   private final boolean minify;
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}, minify: {3}")
+  @Parameterized.Parameters(name = "{0}, {1}, minify: {2}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers(),
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
         BooleanUtils.values());
   }
 
   public EnumMinificationKotlinTest(
-      TestParameters parameters,
-      KotlinTargetVersion targetVersion,
-      KotlinCompiler kotlinc,
-      boolean minify) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters, boolean minify) {
+    super(kotlinParameters);
     this.parameters = parameters;
     this.minify = minify;
   }
diff --git a/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java b/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java
index 7a1b6e4..5d9e78b 100644
--- a/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java
+++ b/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java
@@ -3,17 +3,15 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.naming;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.SingleTestRunResult;
 import com.android.tools.r8.TestCompileResult;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.kotlin.TestKotlinClass;
@@ -39,21 +37,19 @@
 public class KotlinIntrinsicsIdentifierTest extends AbstractR8KotlinNamingTestBase {
   private static final String FOLDER = "intrinsics_identifiers";
 
-  @Parameters(name = "target: {0}, kotlinc: {1}, allowAccessModification: {2}, minification: {3}")
+  @Parameters(name = "{0}, allowAccessModification: {1}, minification: {2}")
   public static Collection<Object[]> data() {
     return buildParameters(
-        KotlinTargetVersion.values(),
-        getKotlinCompilers(),
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
         BooleanUtils.values(),
         BooleanUtils.values());
   }
 
   public KotlinIntrinsicsIdentifierTest(
-      KotlinTargetVersion targetVersion,
-      KotlinCompiler kotlinc,
+      KotlinTestParameters kotlinParameters,
       boolean allowAccessModification,
       boolean minification) {
-    super(targetVersion, kotlinc, allowAccessModification, minification);
+    super(kotlinParameters, allowAccessModification, minification);
   }
 
   private static final KotlinCompileMemoizer compiledJars =
diff --git a/src/test/java/com/android/tools/r8/naming/b139991218/TestRunner.java b/src/test/java/com/android/tools/r8/naming/b139991218/TestRunner.java
index 69f0148..665e410 100644
--- a/src/test/java/com/android/tools/r8/naming/b139991218/TestRunner.java
+++ b/src/test/java/com/android/tools/r8/naming/b139991218/TestRunner.java
@@ -4,8 +4,8 @@
 
 package com.android.tools.r8.naming.b139991218;
 
-import static junit.framework.TestCase.assertTrue;
-import static org.junit.Assert.assertFalse;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
 
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.TestBase;
@@ -14,8 +14,6 @@
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ir.optimize.Inliner.Reason;
 import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
 import com.google.common.collect.ImmutableSet;
 import java.io.IOException;
 import java.nio.file.Paths;
@@ -63,21 +61,25 @@
             options -> {
               options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
               options.enableClassInlining = false;
+
+              // TODO(b/179019716): Add support for merging in presence of annotations.
+              options.horizontalClassMergerOptions()
+                      .skipNoClassesOrMembersWithAnnotationsPolicyForTesting =
+                  true;
             })
         .addDontWarnJetBrainsAnnotations()
+        .addHorizontallyMergedClassesInspector(
+            inspector ->
+                inspector.assertIsCompleteMergeGroup(
+                    "com.android.tools.r8.naming.b139991218.Lambda1",
+                    "com.android.tools.r8.naming.b139991218.Lambda2"))
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutput(StringUtils.lines("11", "12"))
+        .assertSuccessWithOutputLines("11", "12")
         .inspect(
-            inspector -> {
-              // Ensure that we have created a lambda group and that the lambda classes are now
-              // gone.
-              boolean foundLambdaGroup = false;
-              for (FoundClassSubject allClass : inspector.allClasses()) {
-                foundLambdaGroup |= allClass.getOriginalName().contains("LambdaGroup");
-                assertFalse(allClass.getOriginalName().contains("b139991218.Lambda"));
-              }
-              assertTrue(foundLambdaGroup);
-            });
+            inspector ->
+                assertThat(
+                    inspector.clazz("com.android.tools.r8.naming.b139991218.Lambda1"),
+                    isPresent()));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
index 94e1e1a..2479dce 100644
--- a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
@@ -5,7 +5,6 @@
 
 import static com.android.tools.r8.Collectors.toSingle;
 import static com.android.tools.r8.ToolHelper.getFilesInTestFolderRelativeToClass;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.containsLinePositions;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isInlineFrame;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isInlineStack;
@@ -16,11 +15,10 @@
 
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
 import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.naming.retrace.StackTrace;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -44,18 +42,17 @@
 
   private final TestParameters parameters;
 
-  @Parameters(name = "{0}")
+  @Parameters(name = "{0}, {1}")
   public static List<Object[]> data() {
     // TODO(b/141817471): Extend with compilation modes.
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public KotlinInlineFunctionInSameFileRetraceTests(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
index 3827c26..945b732 100644
--- a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
@@ -6,7 +6,6 @@
 
 import static com.android.tools.r8.Collectors.toSingle;
 import static com.android.tools.r8.ToolHelper.getFilesInTestFolderRelativeToClass;
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.containsLinePositions;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isInlineFrame;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isInlineStack;
@@ -17,11 +16,10 @@
 
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
 import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.naming.retrace.StackTrace;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
@@ -45,18 +43,17 @@
   private static final String FILENAME_INLINE_STATIC = "InlineFunction.kt";
   private static final String FILENAME_INLINE_INSTANCE = "InlineFunction.kt";
 
-  @Parameters(name = "{0}")
+  @Parameters(name = "{0}, {1}")
   public static List<Object[]> data() {
     // TODO(b/141817471): Extend with compilation modes.
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers());
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
   }
 
   public KotlinInlineFunctionRetraceTest(
-      TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
     this.parameters = parameters;
   }
 
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinTest.java
index 64ea164..2cc7c96 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinTest.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.rewrite.assertions;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.not;
@@ -15,8 +14,8 @@
 
 import com.android.tools.r8.AssertionsConfiguration;
 import com.android.tools.r8.D8TestBuilder;
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
 import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.R8FullTestBuilder;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ThrowableConsumer;
@@ -63,24 +62,21 @@
   private final boolean useJvmAssertions;
   private final KotlinCompileMemoizer compiledForAssertions;
 
-  @Parameterized.Parameters(
-      name = "{0}, target: {1}, kotlinc: {2}, kotlin-stdlib as library: {3}, -Xassertions=jvm: {4}")
+  @Parameterized.Parameters(name = "{0}, {1}, kotlin-stdlib as library: {2}, -Xassertions=jvm: {3}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers(),
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
         BooleanUtils.values(),
         BooleanUtils.values());
   }
 
   public AssertionConfigurationKotlinTest(
       TestParameters parameters,
-      KotlinTargetVersion targetVersion,
-      KotlinCompiler kotlinc,
+      KotlinTestParameters kotlinParameters,
       boolean kotlinStdlibAsClasspath,
       boolean useJvmAssertions) {
-    super(targetVersion, kotlinc);
+    super(kotlinParameters);
     this.parameters = parameters;
     this.kotlinStdlibAsLibrary = kotlinStdlibAsClasspath;
     this.useJvmAssertions = useJvmAssertions;
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java b/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java
index bc55c55..8eaee28 100644
--- a/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking.annotations;
 
-import static com.android.tools.r8.ToolHelper.getKotlinCompilers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
 import static org.hamcrest.CoreMatchers.containsString;
@@ -13,10 +12,9 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assume.assumeTrue;
 
-import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
 import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.graph.DexAnnotationElement;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.BooleanUtils;
@@ -62,21 +60,17 @@
   private final TestParameters parameters;
   private final boolean minify;
 
-  @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}, minify: {3}")
+  @Parameterized.Parameters(name = "{0}, {1}, minify: {2}")
   public static Collection<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(),
-        KotlinTargetVersion.values(),
-        getKotlinCompilers(),
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
         BooleanUtils.values());
   }
 
   public ReflectiveAnnotationUseTest(
-      TestParameters parameters,
-      KotlinTargetVersion targetVersion,
-      KotlinCompiler kotlinc,
-      boolean minify) {
-    super(targetVersion, kotlinc);
+      TestParameters parameters, KotlinTestParameters kotlinParameters, boolean minify) {
+    super(kotlinParameters);
     this.parameters = parameters;
     this.minify = minify;
   }
diff --git a/src/test/java/com/android/tools/r8/shaking/b134858535/EventPublisherTest.java b/src/test/java/com/android/tools/r8/shaking/b134858535/EventPublisherTest.java
index 6f9c298..5d5a4b9 100644
--- a/src/test/java/com/android/tools/r8/shaking/b134858535/EventPublisherTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/b134858535/EventPublisherTest.java
@@ -51,8 +51,8 @@
         .addKeepClassRules(Interface.class)
         .addKeepMainRule(Main.class)
         .setMinApi(parameters.getApiLevel())
-        .addHorizontallyMergedLambdaClassesInspector(
-            inspector -> inspector.assertClassNotMerged(EventPublisher$b.class))
+        .addHorizontallyMergedClassesInspector(
+            inspector -> inspector.assertClassesNotMerged(EventPublisher$b.class))
         .compile();
   }
 
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
index cb09a17..491bf9d 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.references.ClassReference;
 import java.util.List;
 import java.util.function.Consumer;
+import java.util.function.Predicate;
 import kotlinx.metadata.jvm.KotlinClassMetadata;
 import org.junit.rules.TemporaryFolder;
 
@@ -38,7 +39,7 @@
   }
 
   @Override
-  public MethodSubject uniqueInstanceInitializer() {
+  public MethodSubject uniqueMethodThatMatches(Predicate<FoundMethodSubject> predicate) {
     return new AbsentMethodSubject();
   }
 
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
index cec4e2b..ee96d6b 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
@@ -96,7 +96,11 @@
 
   public abstract MethodSubject method(String returnType, String name, List<String> parameters);
 
-  public abstract MethodSubject uniqueInstanceInitializer();
+  public final MethodSubject uniqueInstanceInitializer() {
+    return uniqueMethodThatMatches(FoundMethodSubject::isInstanceInitializer);
+  }
+
+  public abstract MethodSubject uniqueMethodThatMatches(Predicate<FoundMethodSubject> predicate);
 
   public abstract MethodSubject uniqueMethodWithName(String name);
 
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
index 0bfe560..ed04f35 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
@@ -46,6 +46,7 @@
 import java.util.List;
 import java.util.Set;
 import java.util.function.Consumer;
+import java.util.function.Predicate;
 import kotlinx.metadata.jvm.KotlinClassMetadata;
 import org.junit.rules.TemporaryFolder;
 
@@ -128,9 +129,9 @@
   }
 
   @Override
-  public MethodSubject uniqueInstanceInitializer() {
+  public MethodSubject uniqueMethodThatMatches(Predicate<FoundMethodSubject> predicate) {
     MethodSubject methodSubject = null;
-    for (FoundMethodSubject candidate : allMethods(FoundMethodSubject::isInstanceInitializer)) {
+    for (FoundMethodSubject candidate : allMethods(predicate)) {
       assert methodSubject == null;
       methodSubject = candidate;
     }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
index e90df17..a5c623c 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
@@ -8,16 +8,25 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
+import com.android.tools.r8.TestBase;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.utils.SetUtils;
+import com.android.tools.r8.utils.StringUtils;
 import com.google.common.collect.Sets;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.List;
 import java.util.Set;
 import java.util.function.BiConsumer;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 public class HorizontallyMergedClassesInspector {
 
@@ -59,8 +68,32 @@
 
   public HorizontallyMergedClassesInspector assertMergedInto(Class<?> from, Class<?> target) {
     assertEquals(
-        horizontallyMergedClasses.getMergeTargetOrDefault(toDexType(from, dexItemFactory)),
-        toDexType(target, dexItemFactory));
+        horizontallyMergedClasses.getMergeTargetOrDefault(toDexType(from)), toDexType(target));
+    return this;
+  }
+
+  public HorizontallyMergedClassesInspector assertClassesMerged(Collection<Class<?>> classes) {
+    return assertTypesMerged(classes.stream().map(this::toDexType).collect(Collectors.toList()));
+  }
+
+  public HorizontallyMergedClassesInspector assertClassReferencesMerged(
+      Collection<ClassReference> classReferences) {
+    return assertTypesMerged(
+        classReferences.stream().map(this::toDexType).collect(Collectors.toList()));
+  }
+
+  public HorizontallyMergedClassesInspector assertTypesMerged(Collection<DexType> types) {
+    List<DexType> unmerged = new ArrayList<>();
+    for (DexType type : types) {
+      if (!horizontallyMergedClasses.hasBeenMergedOrIsMergeTarget(type)) {
+        unmerged.add(type);
+      }
+    }
+    assertEquals(
+        "Expected the following classes to be merged: "
+            + StringUtils.join(", ", unmerged, DexType::getTypeName),
+        0,
+        unmerged.size());
     return this;
   }
 
@@ -69,52 +102,91 @@
     return this;
   }
 
-  public HorizontallyMergedClassesInspector assertClassNotMerged(Class<?> clazz) {
-    assertFalse(
-        horizontallyMergedClasses.hasBeenMergedIntoDifferentType(toDexType(clazz, dexItemFactory)));
-    return this;
-  }
-
-  public HorizontallyMergedClassesInspector assertClassesNotMerged(Collection<Class<?>> classes) {
-    for (Class<?> clazz : classes) {
-      assertClassNotMerged(clazz);
-    }
-    return this;
-  }
-
   public HorizontallyMergedClassesInspector assertClassesNotMerged(Class<?>... classes) {
     return assertClassesNotMerged(Arrays.asList(classes));
   }
 
-  public HorizontallyMergedClassesInspector assertClassNotMergedIntoDifferentType(Class<?> clazz) {
-    assertFalse(
-        horizontallyMergedClasses.hasBeenMergedIntoDifferentType(toDexType(clazz, dexItemFactory)));
-    return this;
+  public HorizontallyMergedClassesInspector assertClassesNotMerged(Collection<Class<?>> classes) {
+    return assertTypesNotMerged(classes.stream().map(this::toDexType).collect(Collectors.toList()));
   }
 
-  public HorizontallyMergedClassesInspector assertMerged(Class<?> clazz) {
-    assertTrue(
-        horizontallyMergedClasses.hasBeenMergedOrIsMergeTarget(toDexType(clazz, dexItemFactory)));
-    return this;
+  public HorizontallyMergedClassesInspector assertClassReferencesNotMerged(
+      ClassReference... classReferences) {
+    return assertClassReferencesNotMerged(Arrays.asList(classReferences));
   }
 
-  public HorizontallyMergedClassesInspector assertMerged(Class<?>... classes) {
-    for (Class<?> clazz : classes) {
-      assertMerged(clazz);
+  public HorizontallyMergedClassesInspector assertClassReferencesNotMerged(
+      Collection<ClassReference> classReferences) {
+    return assertTypesNotMerged(
+        classReferences.stream().map(this::toDexType).collect(Collectors.toList()));
+  }
+
+  public HorizontallyMergedClassesInspector assertTypesNotMerged(DexType... types) {
+    return assertTypesNotMerged(Arrays.asList(types));
+  }
+
+  public HorizontallyMergedClassesInspector assertTypesNotMerged(Collection<DexType> types) {
+    for (DexType type : types) {
+      assertTrue(type.isClassType());
+      assertFalse(horizontallyMergedClasses.hasBeenMergedOrIsMergeTarget(type));
     }
     return this;
   }
 
-  public HorizontallyMergedClassesInspector assertMergedIntoDifferentType(Class<?> clazz) {
-    assertTrue(
-        horizontallyMergedClasses.hasBeenMergedIntoDifferentType(toDexType(clazz, dexItemFactory)));
+  public HorizontallyMergedClassesInspector assertIsCompleteMergeGroup(String... typeNames) {
+    return assertIsCompleteMergeGroup(
+        Stream.of(typeNames).map(Reference::classFromTypeName).collect(Collectors.toList()));
+  }
+
+  public HorizontallyMergedClassesInspector assertIsCompleteMergeGroup(
+      Collection<ClassReference> classReferences) {
+    assertFalse(classReferences.isEmpty());
+    List<DexType> types =
+        classReferences.stream().map(this::toDexType).collect(Collectors.toList());
+    DexType uniqueTarget = null;
+    for (DexType type : types) {
+      if (horizontallyMergedClasses.isMergeTarget(type)) {
+        if (uniqueTarget == null) {
+          uniqueTarget = type;
+        } else {
+          fail(
+              "Expected a single merge target, but found "
+                  + type.getTypeName()
+                  + " and "
+                  + uniqueTarget.getTypeName());
+        }
+      }
+    }
+    if (uniqueTarget == null) {
+      for (DexType type : types) {
+        if (horizontallyMergedClasses.hasBeenMergedIntoDifferentType(type)) {
+          fail(
+              "Expected merge target "
+                  + horizontallyMergedClasses.getMergeTargetOrDefault(type).getTypeName()
+                  + " to be in merge group");
+        }
+      }
+      fail("Expected to find a merge target, but none found");
+    }
+    Set<DexType> sources = horizontallyMergedClasses.getSourcesFor(uniqueTarget);
+    assertEquals(
+        "Expected to find "
+            + (classReferences.size() - 1)
+            + " source(s) for merge target "
+            + uniqueTarget.getTypeName()
+            + ", but only found: "
+            + StringUtils.join(", ", sources, DexType::getTypeName),
+        classReferences.size() - 1,
+        sources.size());
+    assertTrue(types.containsAll(sources));
     return this;
   }
 
-  public HorizontallyMergedClassesInspector assertMergedIntoDifferentType(Class<?>... classes) {
-    for (Class<?> clazz : classes) {
-      assertMergedIntoDifferentType(clazz);
-    }
-    return this;
+  private DexType toDexType(Class<?> clazz) {
+    return TestBase.toDexType(clazz, dexItemFactory);
+  }
+
+  private DexType toDexType(ClassReference classReference) {
+    return TestBase.toDexType(classReference, dexItemFactory);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedLambdaClassesInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedLambdaClassesInspector.java
deleted file mode 100644
index 619b415..0000000
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedLambdaClassesInspector.java
+++ /dev/null
@@ -1,53 +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.utils.codeinspector;
-
-import static com.android.tools.r8.TestBase.toDexType;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.classmerging.HorizontallyMergedLambdaClasses;
-import java.util.Set;
-import java.util.function.BiConsumer;
-
-public class HorizontallyMergedLambdaClassesInspector {
-
-  private final DexItemFactory dexItemFactory;
-  private final HorizontallyMergedLambdaClasses horizontallyMergedLambdaClasses;
-
-  public HorizontallyMergedLambdaClassesInspector(
-      DexItemFactory dexItemFactory,
-      HorizontallyMergedLambdaClasses horizontallyMergedLambdaClasses) {
-    this.dexItemFactory = dexItemFactory;
-    this.horizontallyMergedLambdaClasses = horizontallyMergedLambdaClasses;
-  }
-
-  public HorizontallyMergedLambdaClassesInspector assertMerged(Class<?> clazz) {
-    assertTrue(
-        horizontallyMergedLambdaClasses.hasBeenMergedIntoDifferentType(
-            toDexType(clazz, dexItemFactory)));
-    return this;
-  }
-
-  public HorizontallyMergedLambdaClassesInspector assertMerged(Class<?>... classes) {
-    for (Class<?> clazz : classes) {
-      assertMerged(clazz);
-    }
-    return this;
-  }
-
-  public HorizontallyMergedLambdaClassesInspector assertClassNotMerged(Class<?> clazz) {
-    assertFalse(
-        horizontallyMergedLambdaClasses.hasBeenMergedIntoDifferentType(
-            toDexType(clazz, dexItemFactory)));
-    return this;
-  }
-
-  public void forEachMergeGroup(BiConsumer<Set<DexType>, DexType> consumer) {
-    horizontallyMergedLambdaClasses.forEachMergeGroup(consumer);
-  }
-}