Enable minimizeSyntheticNames in TwrCloseResourceDuplicationProfileRewritingTest

This implements robust detection of synthetics in the output in a way that is not based on the naming.

Change-Id: I81184477e20c4545940eb279909885e9e4a89258
diff --git a/src/main/java/com/android/tools/r8/ir/code/NumberGenerator.java b/src/main/java/com/android/tools/r8/ir/code/NumberGenerator.java
index e1ecde4..ddaf5de 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NumberGenerator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NumberGenerator.java
@@ -10,6 +10,10 @@
 
   private int nextValueNumber = 0;
 
+  public int peekPrevious() {
+    return nextValueNumber - 1;
+  }
+
   public int next() {
     return nextValueNumber++;
   }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
index e0a0ee9..def2cc5 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -38,6 +38,7 @@
 import com.android.tools.r8.utils.IterableUtils;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.OptionalBool;
+import com.android.tools.r8.utils.QuadConsumer;
 import com.android.tools.r8.utils.SetUtils;
 import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeHashMap;
 import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeMap;
@@ -650,14 +651,13 @@
             // Two equivalence groups in same context type must be distinct otherwise the assignment
             // of the synthetic name will be non-deterministic between the two.
             assert i == 0 || checkGroupsAreDistinct(groups.get(i - 1), group, comparator);
-            SyntheticKind kind = group.getRepresentative().getKind();
             DexType representativeType =
                 intermediate
                         && synthetics.isSyntheticInput(
                             group.getRepresentative().getHolder().asProgramClass())
                     ? group.getRepresentative().getHolder().getType()
                     : createExternalType(
-                        kind,
+                        group,
                         externalSyntheticTypePrefix,
                         generators,
                         appView,
@@ -871,15 +871,17 @@
   }
 
   private DexType createExternalType(
-      SyntheticKind kind,
+      EquivalenceGroup<?> group,
       String externalSyntheticTypePrefix,
       Map<String, NumberGenerator> generators,
       AppView<?> appView,
       Predicate<DexType> reserved) {
     DexItemFactory factory = appView.dexItemFactory();
+    SyntheticKind kind = group.getRepresentative().getKind();
+    InternalOptions options = appView.options();
     if (kind.isFixedSuffixSynthetic()) {
       return SyntheticNaming.createExternalType(
-          kind, externalSyntheticTypePrefix, "", factory, appView.options());
+          kind, externalSyntheticTypePrefix, "", factory, options);
     }
     NumberGenerator generator =
         generators.computeIfAbsent(externalSyntheticTypePrefix, k -> new NumberGenerator());
@@ -903,6 +905,17 @@
         externalType = null;
       }
     } while (externalType == null);
+    QuadConsumer<SyntheticKind, Integer, DexType, DexType> syntheticItemsConsumer =
+        options.getTestingOptions().syntheticItemsConsumer;
+    if (syntheticItemsConsumer != null) {
+      for (SyntheticDefinition<?, ?, ?> member : group.getRepresentativeAndMembers()) {
+        syntheticItemsConsumer.accept(
+            kind,
+            generator.peekPrevious(),
+            member.getContext().getSynthesizingContextType(),
+            externalType);
+      }
+    }
     return externalType;
   }
 
@@ -1012,6 +1025,10 @@
       this.pinned = pinned;
     }
 
+    public Iterable<T> getRepresentativeAndMembers() {
+      return IterableUtils.append(members, representative);
+    }
+
     public boolean isPinned(AppView<?> appView) {
       if (pinned.isTrue()) {
         return true;
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 64eabc6..017d4a1 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -107,6 +107,7 @@
 import com.android.tools.r8.shaking.ProguardConfiguration;
 import com.android.tools.r8.shaking.ProguardConfigurationRule;
 import com.android.tools.r8.synthesis.SyntheticItemsOptions;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
 import com.android.tools.r8.threading.ThreadingModule;
 import com.android.tools.r8.tracereferences.TraceReferencesOptions;
 import com.android.tools.r8.utils.IROrdering.IdentityIROrdering;
@@ -2331,6 +2332,8 @@
     public BiConsumer<DexItemFactory, RepackagingLens> repackagingLensConsumer =
         ConsumerUtils.emptyBiConsumer();
 
+    public QuadConsumer<SyntheticKind, Integer, DexType, DexType> syntheticItemsConsumer = null;
+
     public BiConsumer<DexItemFactory, EnumDataMap> unboxedEnumsConsumer =
         ConsumerUtils.emptyBiConsumer();
 
diff --git a/src/test/examplesJava9/twr/twrcloseresourceduplication/TwrCloseResourceDuplicationProfileRewritingTest.java b/src/test/examplesJava9/twr/twrcloseresourceduplication/TwrCloseResourceDuplicationProfileRewritingTest.java
index 76f002f..dd68df9 100644
--- a/src/test/examplesJava9/twr/twrcloseresourceduplication/TwrCloseResourceDuplicationProfileRewritingTest.java
+++ b/src/test/examplesJava9/twr/twrcloseresourceduplication/TwrCloseResourceDuplicationProfileRewritingTest.java
@@ -17,7 +17,9 @@
 import com.android.tools.r8.profile.art.utils.ArtProfileInspector;
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.references.TypeReference;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.Box;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.InternalOptions.InlinerOptions;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -59,6 +61,7 @@
   @Test
   public void testR8ProfileRewriting() throws Exception {
     parameters.assumeR8TestParameters();
+    Box<SyntheticItemsTestUtils> syntheticItems = new Box<>();
     testForR8(parameters.getBackend())
         .addProgramClassFileData(TwrCloseResourceDuplicationTest.getProgramInputs())
         .addKeepMainRule(MAIN)
@@ -67,9 +70,7 @@
         .addOptionsModification(InlinerOptions::disableInlining)
         .addOptionsModification(
             options -> {
-              // Explicitly disable minimal synthetic names to enable robust detection of
-              // synthetics.
-              options.desugarSpecificOptions().minimizeSyntheticNames = false;
+              options.desugarSpecificOptions().minimizeSyntheticNames = true;
               options.testing.enableSyntheticSharing = false;
             })
         .applyIf(
@@ -83,10 +84,14 @@
                             options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces()),
             testBuilder ->
                 testBuilder.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST)))
+        .collectSyntheticItems()
         .noHorizontalClassMergingOfSynthetics()
         .setMinApi(parameters)
         .compile()
-        .inspectResidualArtProfile(this::inspectR8)
+        .inspectSyntheticItems(syntheticItems::set)
+        .inspectResidualArtProfile(
+            (profileInspector, inspector) ->
+                inspectR8(profileInspector, inspector, syntheticItems.get()))
         .run(parameters.getRuntime(), MAIN, getZipFile())
         .assertSuccessWithOutput(TwrCloseResourceDuplicationTest.EXPECTED);
   }
@@ -127,19 +132,29 @@
   }
 
   private void inspectD8(ArtProfileInspector profileInspector, CodeInspector inspector) {
-    inspect(profileInspector, inspector, hasTwrCloseResourceSupport(true), false);
+    inspect(
+        profileInspector,
+        inspector,
+        hasTwrCloseResourceSupport(true),
+        getDefaultSyntheticItemsTestUtils());
   }
 
-  private void inspectR8(ArtProfileInspector profileInspector, CodeInspector inspector) {
+  private void inspectR8(
+      ArtProfileInspector profileInspector,
+      CodeInspector inspector,
+      SyntheticItemsTestUtils syntheticItems) {
     inspect(
-        profileInspector, inspector, hasTwrCloseResourceSupport(parameters.isDexRuntime()), true);
+        profileInspector,
+        inspector,
+        hasTwrCloseResourceSupport(parameters.isDexRuntime()),
+        syntheticItems);
   }
 
   private void inspect(
       ArtProfileInspector profileInspector,
       CodeInspector inspector,
       boolean hasTwrCloseResourceSupport,
-      boolean isR8) {
+      SyntheticItemsTestUtils syntheticItems) {
     int expectedClassCount = 3;
     if (!hasTwrCloseResourceSupport) {
       expectedClassCount += 8;
@@ -191,46 +206,41 @@
     // There is 1 backport, 2 synthetic API outlines, and 3 twr classes for both Foo and Bar.
     for (String clazz : ImmutableList.of(FOO, BAR)) {
       ClassSubject syntheticApiOutlineClassSubject0 =
-          inspector.clazz(
-              getDefaultSyntheticItemsTestUtils()
-                  .syntheticApiOutlineClass(Reference.classFromTypeName(clazz), 0));
+          inspector.syntheticClass(
+              syntheticItems.syntheticApiOutlineClass(Reference.classFromTypeName(clazz), 0));
       assertThat(syntheticApiOutlineClassSubject0, isPresentIf(hasTwrCloseResourceApiOutlines()));
 
       ClassSubject syntheticApiOutlineClassSubject1 =
-          inspector.clazz(
-              getDefaultSyntheticItemsTestUtils()
-                  .syntheticApiOutlineClass(Reference.classFromTypeName(clazz), 1));
+          inspector.syntheticClass(
+              syntheticItems.syntheticApiOutlineClass(Reference.classFromTypeName(clazz), 1));
       assertThat(syntheticApiOutlineClassSubject1, isPresentIf(hasTwrCloseResourceApiOutlines()));
 
       int initialSyntheticId = hasTwrCloseResourceApiOutlines() ? 2 : 0;
 
       ClassSubject syntheticBackportClassSubject =
-          inspector.clazz(
-              getDefaultSyntheticItemsTestUtils()
-                  .syntheticBackportClass(Reference.classFromTypeName(clazz), initialSyntheticId));
+          inspector.syntheticClass(
+              syntheticItems.syntheticBackportClass(
+                  Reference.classFromTypeName(clazz), initialSyntheticId));
       assertThat(syntheticBackportClassSubject, notIf(isPresent(), hasTwrCloseResourceSupport));
 
       ClassSubject syntheticTwrCloseResourceClassSubject3 =
-          inspector.clazz(
-              getDefaultSyntheticItemsTestUtils()
-                  .syntheticTwrCloseResourceClass(
-                      Reference.classFromTypeName(clazz), initialSyntheticId + 1));
+          inspector.syntheticClass(
+              syntheticItems.syntheticTwrCloseResourceClass(
+                  Reference.classFromTypeName(clazz), initialSyntheticId + 1));
       assertThat(
           syntheticTwrCloseResourceClassSubject3, notIf(isPresent(), hasTwrCloseResourceSupport));
 
       ClassSubject syntheticTwrCloseResourceClassSubject4 =
-          inspector.clazz(
-              getDefaultSyntheticItemsTestUtils()
-                  .syntheticTwrCloseResourceClass(
-                      Reference.classFromTypeName(clazz), initialSyntheticId + 2));
+          inspector.syntheticClass(
+              syntheticItems.syntheticTwrCloseResourceClass(
+                  Reference.classFromTypeName(clazz), initialSyntheticId + 2));
       assertThat(
           syntheticTwrCloseResourceClassSubject4, notIf(isPresent(), hasTwrCloseResourceSupport));
 
       ClassSubject syntheticTwrCloseResourceClassSubject5 =
-          inspector.clazz(
-              getDefaultSyntheticItemsTestUtils()
-                  .syntheticTwrCloseResourceClass(
-                      Reference.classFromTypeName(clazz), initialSyntheticId + 3));
+          inspector.syntheticClass(
+              syntheticItems.syntheticTwrCloseResourceClass(
+                  Reference.classFromTypeName(clazz), initialSyntheticId + 3));
       assertThat(
           syntheticTwrCloseResourceClassSubject5, notIf(isPresent(), hasTwrCloseResourceSupport));
 
@@ -261,9 +271,10 @@
     profileInspector.applyIf(
         options.shouldDesugarAutoCloseable(),
         i ->
-            i.assertContainsClassRules(getCloseDispatcherSyntheticClasses(inspector, isR8))
+            i.assertContainsClassRules(
+                    getCloseDispatcherSyntheticClasses(inspector, syntheticItems))
                 .assertContainsMethodRules(
-                    Arrays.stream(getCloseDispatcherSyntheticClasses(inspector, isR8))
+                    Arrays.stream(getCloseDispatcherSyntheticClasses(inspector, syntheticItems))
                         .map(ClassSubject::uniqueMethod)
                         .toArray(MethodSubject[]::new)));
 
@@ -271,44 +282,36 @@
   }
 
   private static ClassSubject[] getCloseDispatcherSyntheticClasses(
-      CodeInspector inspector, boolean isR8) {
+      CodeInspector inspector, SyntheticItemsTestUtils syntheticItems) {
     return new ClassSubject[] {
-      inspector.clazz(
-          getDefaultSyntheticItemsTestUtils()
-              .syntheticAutoCloseableDispatcherClass(Reference.classFromTypeName(FOO), 0)),
-      inspector.clazz(
-          getDefaultSyntheticItemsTestUtils()
-              .syntheticAutoCloseableDispatcherClass(Reference.classFromTypeName(FOO), 1)),
-      inspector.clazz(
-          getDefaultSyntheticItemsTestUtils()
-              .syntheticAutoCloseableDispatcherClass(Reference.classFromTypeName(BAR), 0)),
-      inspector.clazz(
-          getDefaultSyntheticItemsTestUtils()
-              .syntheticAutoCloseableDispatcherClass(Reference.classFromTypeName(BAR), 1)),
-      inspector.clazz(
-          getDefaultSyntheticItemsTestUtils()
-              .syntheticAutoCloseableForwarderClass(Reference.classFromTypeName(FOO), 2)),
-      inspector.clazz(
-          getDefaultSyntheticItemsTestUtils()
-              .syntheticAutoCloseableForwarderClass(Reference.classFromTypeName(FOO), 3)),
-      inspector.clazz(
-          getDefaultSyntheticItemsTestUtils()
-              .syntheticAutoCloseableForwarderClass(Reference.classFromTypeName(BAR), 2)),
-      inspector.clazz(
-          getDefaultSyntheticItemsTestUtils()
-              .syntheticAutoCloseableForwarderClass(Reference.classFromTypeName(BAR), 3)),
-      inspector.clazz(
-          getDefaultSyntheticItemsTestUtils()
-              .syntheticThrowIAEClass(Reference.classFromTypeName(FOO), 4)),
-      inspector.clazz(
-          getDefaultSyntheticItemsTestUtils()
-              .syntheticThrowIAEClass(Reference.classFromTypeName(FOO), 5)),
-      inspector.clazz(
-          getDefaultSyntheticItemsTestUtils()
-              .syntheticThrowIAEClass(Reference.classFromTypeName(BAR), 4)),
-      inspector.clazz(
-          getDefaultSyntheticItemsTestUtils()
-              .syntheticThrowIAEClass(Reference.classFromTypeName(BAR), 5))
+      inspector.syntheticClass(
+          syntheticItems.syntheticAutoCloseableDispatcherClass(
+              Reference.classFromTypeName(FOO), 0)),
+      inspector.syntheticClass(
+          syntheticItems.syntheticAutoCloseableDispatcherClass(
+              Reference.classFromTypeName(FOO), 1)),
+      inspector.syntheticClass(
+          syntheticItems.syntheticAutoCloseableDispatcherClass(
+              Reference.classFromTypeName(BAR), 0)),
+      inspector.syntheticClass(
+          syntheticItems.syntheticAutoCloseableDispatcherClass(
+              Reference.classFromTypeName(BAR), 1)),
+      inspector.syntheticClass(
+          syntheticItems.syntheticAutoCloseableForwarderClass(Reference.classFromTypeName(FOO), 2)),
+      inspector.syntheticClass(
+          syntheticItems.syntheticAutoCloseableForwarderClass(Reference.classFromTypeName(FOO), 3)),
+      inspector.syntheticClass(
+          syntheticItems.syntheticAutoCloseableForwarderClass(Reference.classFromTypeName(BAR), 2)),
+      inspector.syntheticClass(
+          syntheticItems.syntheticAutoCloseableForwarderClass(Reference.classFromTypeName(BAR), 3)),
+      inspector.syntheticClass(
+          syntheticItems.syntheticThrowIAEClass(Reference.classFromTypeName(FOO), 4)),
+      inspector.syntheticClass(
+          syntheticItems.syntheticThrowIAEClass(Reference.classFromTypeName(FOO), 5)),
+      inspector.syntheticClass(
+          syntheticItems.syntheticThrowIAEClass(Reference.classFromTypeName(BAR), 4)),
+      inspector.syntheticClass(
+          syntheticItems.syntheticThrowIAEClass(Reference.classFromTypeName(BAR), 5))
     };
   }
 }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/b149971007/B149971007.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/b149971007/B149971007.java
index 5b38c12..c261571 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/b149971007/B149971007.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/b149971007/B149971007.java
@@ -5,19 +5,19 @@
 package com.android.tools.r8.ir.optimize.outliner.b149971007;
 
 import static com.android.tools.r8.synthesis.SyntheticItemsTestUtils.getMinimalSyntheticItemsTestUtils;
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
 import static junit.framework.TestCase.assertEquals;
-import static junit.framework.TestCase.assertFalse;
 import static junit.framework.TestCase.assertTrue;
+import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import com.android.tools.r8.R8TestCompileResult;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.dexsplitter.SplitterTestBase;
-import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
@@ -30,21 +30,20 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
 public class B149971007 extends SplitterTestBase {
 
-  private final TestParameters parameters;
+  @Parameter(0)
+  public TestParameters parameters;
 
-  @Parameterized.Parameters(name = "{0}")
+  @Parameters(name = "{0}")
   public static TestParametersCollection data() {
     return getTestParameters().withDexRuntimes().withAllApiLevels().build();
   }
 
-  public B149971007(TestParameters parameters) {
-    this.parameters = parameters;
-  }
-
   private boolean invokesOutline(MethodSubject method, String outlineClassName) {
     assertThat(method, isPresent());
     for (InstructionSubject instruction : method.asFoundMethodSubject().instructions()) {
@@ -150,6 +149,7 @@
             .addProgramClasses(TestClass.class, FeatureAPI.class)
             .addKeepClassAndMembersRules(TestClass.class)
             .addKeepClassAndMembersRules(FeatureClass.class)
+            .collectSyntheticItems()
             .setMinApi(parameters)
             .addFeatureSplit(
                 builder -> simpleSplitProvider(builder, featureCode, temp, FeatureClass.class))
@@ -161,30 +161,25 @@
             .compile()
             .inspect(this::checkNoOutlineFromFeature);
 
+    MethodSubject outlineMethod =
+        compileResult
+            .inspector()
+            .clazz(compileResult.getSyntheticItems().syntheticOutlineClass(TestClass.class, 1))
+            .uniqueMethod();
+    assertThat(outlineMethod, isPresent());
+
     // Check that parts of method1, ..., method4 in FeatureClass was not outlined.
     CodeInspector featureInspector = new CodeInspector(featureCode);
     ClassSubject featureClass = featureInspector.clazz(FeatureClass.class);
-
-    // Note, this code does not really check a valid property now as the name of the outline is not
-    // known.
     assertThat(featureClass, isPresent());
-    String outlineClassName =
-        ClassNameMapper.mapperFromString(compileResult.getProguardMap())
-            .getObfuscatedToOriginalMapping()
-            .inverse
-            .get(
-                getMinimalSyntheticItemsTestUtils()
-                    .syntheticOutlineClass(TestClass.class, 0)
-                    .getTypeName());
-
-    assertFalse(
-        invokesOutline(featureClass.uniqueMethodWithOriginalName("method1"), outlineClassName));
-    assertFalse(
-        invokesOutline(featureClass.uniqueMethodWithOriginalName("method2"), outlineClassName));
-    assertFalse(
-        invokesOutline(featureClass.uniqueMethodWithOriginalName("method3"), outlineClassName));
-    assertFalse(
-        invokesOutline(featureClass.uniqueMethodWithOriginalName("method4"), outlineClassName));
+    assertThat(
+        featureClass.uniqueMethodWithOriginalName("method1"), not(invokesMethod(outlineMethod)));
+    assertThat(
+        featureClass.uniqueMethodWithOriginalName("method2"), not(invokesMethod(outlineMethod)));
+    assertThat(
+        featureClass.uniqueMethodWithOriginalName("method3"), not(invokesMethod(outlineMethod)));
+    assertThat(
+        featureClass.uniqueMethodWithOriginalName("method4"), not(invokesMethod(outlineMethod)));
 
     // Run the code without the feature code present.
     compileResult.run(parameters.getRuntime(), TestClass.class).assertSuccessWithOutput("12");
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 7d1fabd..52d0ba8 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -99,7 +99,7 @@
                                     "class_inliner_lambda_j_style.MainKt$$ExternalSyntheticLambda7")
                                 .assertIsCompleteMergeGroup(
                                     "class_inliner_lambda_j_style.MainKt$$ExternalSyntheticLambda2",
-                                    "class_inliner_lambda_j_style.MainKt$$ExternalSyntheticThrowBlockOutline0");
+                                    "class_inliner_lambda_j_style.MainKt$$ExternalSyntheticBUOutline0");
                           }
                           inspector.assertNoOtherClassesMerged();
                         })
diff --git a/src/test/testbase/java/com/android/tools/r8/R8TestCompileResultBase.java b/src/test/testbase/java/com/android/tools/r8/R8TestCompileResultBase.java
index 083a743..e22b978 100644
--- a/src/test/testbase/java/com/android/tools/r8/R8TestCompileResultBase.java
+++ b/src/test/testbase/java/com/android/tools/r8/R8TestCompileResultBase.java
@@ -27,6 +27,7 @@
 import com.android.tools.r8.profile.art.utils.ArtProfileInspector;
 import com.android.tools.r8.shaking.CollectingGraphConsumer;
 import com.android.tools.r8.shaking.ProguardConfigurationRule;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.InternalOptions;
@@ -214,6 +215,16 @@
     return self();
   }
 
+  public SyntheticItemsTestUtils getSyntheticItems() {
+    return state.getSyntheticItems();
+  }
+
+  public <E extends Throwable> CR inspectSyntheticItems(
+      ThrowingConsumer<SyntheticItemsTestUtils, E> consumer) throws E {
+    consumer.accept(state.getSyntheticItems());
+    return self();
+  }
+
   public <E extends Throwable> CR assertResourceFile(String name, boolean present)
       throws IOException {
     assertNotNull(resourceShrinkerOutput);
diff --git a/src/test/testbase/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/testbase/java/com/android/tools/r8/TestCompilerBuilder.java
index 49007f8..5806896 100644
--- a/src/test/testbase/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/testbase/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorEventConsumer;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference;
 import com.android.tools.r8.profile.art.model.ExternalArtProfile;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
 import com.android.tools.r8.testing.AndroidBuildVersion;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
@@ -115,6 +116,7 @@
   private PrintStream oldStderr = null;
   protected OutputMode outputMode = OutputMode.DexIndexed;
   private boolean isBenchmarkRunner = false;
+  private SyntheticItemsTestUtils.Builder syntheticItemsBuilder;
 
   private Optional<Integer> isAndroidBuildVersionAdded = null;
 
@@ -296,6 +298,13 @@
     return self();
   }
 
+  public T collectSyntheticItems() {
+    assert syntheticItemsBuilder == null;
+    this.syntheticItemsBuilder = new SyntheticItemsTestUtils.Builder();
+    return addOptionsModification(
+        options -> options.testing.syntheticItemsConsumer = syntheticItemsBuilder::add);
+  }
+
   public CR benchmarkCompile(BenchmarkResults results) throws CompilationFailedException {
     if (System.getProperty("com.android.tools.r8.printtimes") != null) {
       allowStdoutMessages();
@@ -433,6 +442,9 @@
         getState().setStderr(stderr.toString());
       }
       System.setErr(oldStderr);
+      if (syntheticItemsBuilder != null) {
+        getState().setSyntheticItems(syntheticItemsBuilder);
+      }
     }
   }
 
diff --git a/src/test/testbase/java/com/android/tools/r8/TestState.java b/src/test/testbase/java/com/android/tools/r8/TestState.java
index 77d4a0b..0cd7d32 100644
--- a/src/test/testbase/java/com/android/tools/r8/TestState.java
+++ b/src/test/testbase/java/com/android/tools/r8/TestState.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
 import java.io.IOException;
 import java.io.UncheckedIOException;
 import java.nio.file.Path;
@@ -20,6 +21,8 @@
   private String stdout;
   private String stderr;
 
+  private SyntheticItemsTestUtils syntheticItems;
+
   public TestState(TemporaryFolder temp) {
     this(temp, new TestDiagnosticMessagesImpl());
   }
@@ -81,6 +84,14 @@
     this.stderr = stderr;
   }
 
+  public SyntheticItemsTestUtils getSyntheticItems() {
+    return syntheticItems;
+  }
+
+  void setSyntheticItems(SyntheticItemsTestUtils syntheticItems) {
+    this.syntheticItems = syntheticItems;
+  }
+
   void setDiagnosticsLevelModifier(
       BiFunction<DiagnosticsLevel, Diagnostic, DiagnosticsLevel> modifier) {
     messages.setDiagnosticsLevelModifier(modifier);
diff --git a/src/test/testbase/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java b/src/test/testbase/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
index bcb3ce2..605183e 100644
--- a/src/test/testbase/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
+++ b/src/test/testbase/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
@@ -11,9 +11,12 @@
 import static com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring.NEST_ACCESS_STATIC_METHOD_NAME_PREFIX;
 import static com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring.NEST_ACCESS_STATIC_PUT_FIELD_NAME_PREFIX;
 import static com.android.tools.r8.synthesis.SyntheticNaming.EXTERNAL_SYNTHETIC_CLASS_SEPARATOR;
+import static com.android.tools.r8.utils.MapUtils.ignoreKey;
 import static org.hamcrest.CoreMatchers.containsString;
 
+import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.desugar.invokespecial.InvokeSpecialToSelfDesugaring;
 import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringForTesting;
 import com.android.tools.r8.references.ClassReference;
@@ -25,9 +28,14 @@
 import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.google.common.collect.ImmutableList;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMaps;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
 import org.hamcrest.Matcher;
 
 // TODO(b/454846973): Instantiate this based on an output from D8/R8 so that this is completely
@@ -130,25 +138,19 @@
     return syntheticBottomUpOutlineClass(Reference.classFromClass(clazz), id);
   }
 
-  public ClassReference syntheticBottomUpOutlineClass(ClassReference clazz, int id) {
-    return syntheticClass(clazz, naming.BOTTOM_UP_OUTLINE, id);
-  }
+  public abstract ClassReference syntheticBottomUpOutlineClass(ClassReference clazz, int id);
 
   public final ClassReference syntheticOutlineClass(Class<?> clazz, int id) {
     return syntheticOutlineClass(Reference.classFromClass(clazz), id);
   }
 
-  public ClassReference syntheticOutlineClass(ClassReference clazz, int id) {
-    return syntheticClass(clazz, naming.OUTLINE, id);
-  }
+  public abstract ClassReference syntheticOutlineClass(ClassReference clazz, int id);
 
   public final ClassReference syntheticLambdaClass(Class<?> clazz, int id) {
     return syntheticLambdaClass(Reference.classFromClass(clazz), id);
   }
 
-  public ClassReference syntheticLambdaClass(ClassReference clazz, int id) {
-    return syntheticClass(clazz, naming.LAMBDA, id);
-  }
+  public abstract ClassReference syntheticLambdaClass(ClassReference clazz, int id);
 
   public static ClassReference syntheticApiConversionClass(Class<?> clazz, int id) {
     return syntheticClass(clazz, naming.API_CONVERSION, id);
@@ -158,23 +160,15 @@
     return syntheticApiOutlineClass(Reference.classFromClass(clazz), id);
   }
 
-  public ClassReference syntheticApiOutlineClass(ClassReference classReference, int id) {
-    return syntheticClass(classReference, naming.API_MODEL_OUTLINE, id);
-  }
+  public abstract ClassReference syntheticApiOutlineClass(ClassReference classReference, int id);
 
-  public String syntheticApiOutlineClassPrefix(Class<?> clazz) {
-    return clazz.getTypeName()
-        + EXTERNAL_SYNTHETIC_CLASS_SEPARATOR
-        + naming.API_MODEL_OUTLINE.getDescriptor();
-  }
+  public abstract String syntheticApiOutlineClassPrefix(Class<?> clazz);
 
   public final ClassReference syntheticBackportClass(Class<?> clazz, int id) {
     return syntheticBackportClass(Reference.classFromClass(clazz), id);
   }
 
-  public ClassReference syntheticBackportClass(ClassReference classReference, int id) {
-    return syntheticClass(classReference, naming.BACKPORT, id);
-  }
+  public abstract ClassReference syntheticBackportClass(ClassReference classReference, int id);
 
   public static ClassReference syntheticBackportWithForwardingClass(Class<?> clazz, int id) {
     return syntheticClass(clazz, naming.BACKPORT_WITH_FORWARDING, id);
@@ -193,23 +187,15 @@
     return syntheticClass(reference, naming.RECORD_HELPER, id);
   }
 
-  public ClassReference syntheticTwrCloseResourceClass(ClassReference reference, int id) {
-    return syntheticClass(reference, naming.TWR_CLOSE_RESOURCE, id);
-  }
+  public abstract ClassReference syntheticTwrCloseResourceClass(ClassReference reference, int id);
 
-  public ClassReference syntheticAutoCloseableDispatcherClass(
-      ClassReference classReference, int id) {
-    return syntheticClass(classReference, naming.AUTOCLOSEABLE_DISPATCHER, id);
-  }
+  public abstract ClassReference syntheticAutoCloseableDispatcherClass(
+      ClassReference classReference, int id);
 
-  public ClassReference syntheticAutoCloseableForwarderClass(
-      ClassReference classReference, int id) {
-    return syntheticClass(classReference, naming.AUTOCLOSEABLE_FORWARDER, id);
-  }
+  public abstract ClassReference syntheticAutoCloseableForwarderClass(
+      ClassReference classReference, int id);
 
-  public ClassReference syntheticThrowIAEClass(ClassReference classReference, int id) {
-    return syntheticClass(classReference, naming.THROW_IAE, id);
-  }
+  public abstract ClassReference syntheticThrowIAEClass(ClassReference classReference, int id);
 
   public final MethodReference syntheticLambdaMethod(Class<?> clazz, int id, Method method) {
     ClassReference syntheticHolder = syntheticLambdaClass(clazz, id);
@@ -396,7 +382,62 @@
     return SyntheticNaming.isSynthetic(method.getHolderClass(), Phase.INTERNAL, naming.THROW_NSME);
   }
 
-  private static class DefaultSyntheticItemsTestUtils extends SyntheticItemsTestUtils {}
+  private static class DefaultSyntheticItemsTestUtils extends SyntheticItemsTestUtils {
+
+    @Override
+    public ClassReference syntheticApiOutlineClass(ClassReference classReference, int id) {
+      return syntheticClass(classReference, naming.API_MODEL_OUTLINE, id);
+    }
+
+    @Override
+    public String syntheticApiOutlineClassPrefix(Class<?> clazz) {
+      return clazz.getTypeName()
+          + EXTERNAL_SYNTHETIC_CLASS_SEPARATOR
+          + naming.API_MODEL_OUTLINE.getDescriptor();
+    }
+
+    @Override
+    public ClassReference syntheticAutoCloseableDispatcherClass(
+        ClassReference classReference, int id) {
+      return syntheticClass(classReference, naming.AUTOCLOSEABLE_DISPATCHER, id);
+    }
+
+    @Override
+    public ClassReference syntheticAutoCloseableForwarderClass(
+        ClassReference classReference, int id) {
+      return syntheticClass(classReference, naming.AUTOCLOSEABLE_FORWARDER, id);
+    }
+
+    @Override
+    public ClassReference syntheticBackportClass(ClassReference classReference, int id) {
+      return syntheticClass(classReference, naming.BACKPORT, id);
+    }
+
+    @Override
+    public ClassReference syntheticBottomUpOutlineClass(ClassReference clazz, int id) {
+      return syntheticClass(clazz, naming.BOTTOM_UP_OUTLINE, id);
+    }
+
+    @Override
+    public ClassReference syntheticLambdaClass(ClassReference clazz, int id) {
+      return syntheticClass(clazz, naming.LAMBDA, id);
+    }
+
+    @Override
+    public ClassReference syntheticOutlineClass(ClassReference clazz, int id) {
+      return syntheticClass(clazz, naming.OUTLINE, id);
+    }
+
+    @Override
+    public ClassReference syntheticThrowIAEClass(ClassReference classReference, int id) {
+      return syntheticClass(classReference, naming.THROW_IAE, id);
+    }
+
+    @Override
+    public ClassReference syntheticTwrCloseResourceClass(ClassReference reference, int id) {
+      return syntheticClass(reference, naming.TWR_CLOSE_RESOURCE, id);
+    }
+  }
 
   private static class MinimalSyntheticItemsTestUtils extends SyntheticItemsTestUtils {
 
@@ -428,6 +469,11 @@
     }
 
     @Override
+    public ClassReference syntheticBottomUpOutlineClass(ClassReference classReference, int id) {
+      return syntheticClassWithMinimalName(classReference, id);
+    }
+
+    @Override
     public ClassReference syntheticLambdaClass(ClassReference classReference, int id) {
       return syntheticClassWithMinimalName(classReference, id);
     }
@@ -438,11 +484,6 @@
     }
 
     @Override
-    public ClassReference syntheticBottomUpOutlineClass(ClassReference classReference, int id) {
-      return syntheticClassWithMinimalName(classReference, id);
-    }
-
-    @Override
     public ClassReference syntheticThrowIAEClass(ClassReference classReference, int id) {
       return syntheticClassWithMinimalName(classReference, id);
     }
@@ -452,4 +493,78 @@
       return syntheticClassWithMinimalName(classReference, id);
     }
   }
+
+  public static class Builder extends SyntheticItemsTestUtils {
+
+    private final Map<ClassReference, Map<SyntheticKind, Int2ReferenceMap<ClassReference>>>
+        synthetics = new HashMap<>();
+
+    public void add(SyntheticKind kind, int id, DexType syntheticContext, DexType externalType) {
+      Map<SyntheticKind, Int2ReferenceMap<ClassReference>> syntheticsForContextMap =
+          synthetics.computeIfAbsent(syntheticContext.asClassReference(), ignoreKey(HashMap::new));
+      Int2ReferenceMap<ClassReference> idToExternalTypeMap =
+          syntheticsForContextMap.computeIfAbsent(kind, ignoreKey(Int2ReferenceOpenHashMap::new));
+      ClassReference previousValue = idToExternalTypeMap.put(id, externalType.asClassReference());
+      assert previousValue == null;
+    }
+
+    private ClassReference lookup(ClassReference classReference, int id, SyntheticKind kind) {
+      return synthetics
+          .getOrDefault(classReference, Collections.emptyMap())
+          .getOrDefault(kind, Int2ReferenceMaps.emptyMap())
+          .get(id);
+    }
+
+    @Override
+    public ClassReference syntheticApiOutlineClass(ClassReference classReference, int id) {
+      return lookup(classReference, id, naming.API_MODEL_OUTLINE);
+    }
+
+    @Override
+    public String syntheticApiOutlineClassPrefix(Class<?> clazz) {
+      throw new Unimplemented();
+    }
+
+    @Override
+    public ClassReference syntheticAutoCloseableDispatcherClass(
+        ClassReference classReference, int id) {
+      return lookup(classReference, id, naming.AUTOCLOSEABLE_DISPATCHER);
+    }
+
+    @Override
+    public ClassReference syntheticAutoCloseableForwarderClass(
+        ClassReference classReference, int id) {
+      return lookup(classReference, id, naming.AUTOCLOSEABLE_FORWARDER);
+    }
+
+    @Override
+    public ClassReference syntheticBackportClass(ClassReference classReference, int id) {
+      return lookup(classReference, id, naming.BACKPORT);
+    }
+
+    @Override
+    public ClassReference syntheticBottomUpOutlineClass(ClassReference classReference, int id) {
+      return lookup(classReference, id, naming.BOTTOM_UP_OUTLINE);
+    }
+
+    @Override
+    public ClassReference syntheticLambdaClass(ClassReference classReference, int id) {
+      return lookup(classReference, id, naming.LAMBDA);
+    }
+
+    @Override
+    public ClassReference syntheticOutlineClass(ClassReference classReference, int id) {
+      return lookup(classReference, id, naming.OUTLINE);
+    }
+
+    @Override
+    public ClassReference syntheticThrowIAEClass(ClassReference classReference, int id) {
+      return lookup(classReference, id, naming.THROW_IAE);
+    }
+
+    @Override
+    public ClassReference syntheticTwrCloseResourceClass(ClassReference classReference, int id) {
+      return lookup(classReference, id, naming.TWR_CLOSE_RESOURCE);
+    }
+  }
 }
diff --git a/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java b/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
index 629ad82..3d337bb 100644
--- a/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
+++ b/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
@@ -20,6 +20,10 @@
 
 public class AbsentClassSubject extends ClassSubject {
 
+  public AbsentClassSubject() {
+    super(null, null);
+  }
+
   public AbsentClassSubject(CodeInspector codeInspector, ClassReference reference) {
     super(codeInspector, reference);
   }
diff --git a/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index 521664a..bdb96d0 100644
--- a/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -416,6 +416,10 @@
     return clazz(SyntheticItemsTestUtils.syntheticCompanionClass(clazz));
   }
 
+  public ClassSubject syntheticClass(ClassReference classReference) {
+    return classReference != null ? clazz(classReference) : new AbsentClassSubject();
+  }
+
   public void forAllClasses(Consumer<FoundClassSubject> inspection) {
     forAll(
         application.classes(),