Fix non-determinism in adding synthetics to profile

Bug: b/298344609
Change-Id: I6c9736fc683cf591dcf3f49021f66bb75ba5a92a
diff --git a/src/main/java/com/android/tools/r8/GlobalSyntheticsGenerator.java b/src/main/java/com/android/tools/r8/GlobalSyntheticsGenerator.java
index be74e08..395a877 100644
--- a/src/main/java/com/android/tools/r8/GlobalSyntheticsGenerator.java
+++ b/src/main/java/com/android/tools/r8/GlobalSyntheticsGenerator.java
@@ -175,7 +175,9 @@
     RecordDesugaring.ensureRecordClassHelper(
         appView,
         synthesizingContext,
-        recordTagClass -> recordTagClass.programMethods().forEach(methodsToProcess::add));
+        recordTagClass -> recordTagClass.programMethods().forEach(methodsToProcess::add),
+        null,
+        null);
 
     VarHandleDesugaringEventConsumer varHandleEventConsumer =
         new VarHandleDesugaringEventConsumer() {
diff --git a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
index 9aa3575..d4f9278 100644
--- a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
+++ b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
@@ -204,40 +204,41 @@
     if (contexts == null) {
       throw new Unreachable("Attempt to create a global synthetic with no contexts");
     }
-    DexProgramClass mockClass =
-        appView
-            .appInfo()
-            .getSyntheticItems()
-            .ensureGlobalClass(
-                () -> new MissingGlobalSyntheticsConsumerDiagnostic("API stubbing"),
-                kinds -> kinds.API_MODEL_STUB,
-                libraryClass.getType(),
-                contexts,
-                appView,
-                classBuilder -> {
-                  classBuilder
-                      .setSuperType(libraryClass.getSuperType())
-                      .setInterfaces(Arrays.asList(libraryClass.getInterfaces().values))
-                      // Add throwing static initializer
-                      .addMethod(
-                          methodBuilder ->
-                              methodBuilder
-                                  .setName(factory.classConstructorMethodName)
-                                  .setProto(factory.createProto(factory.voidType))
-                                  .setAccessFlags(MethodAccessFlags.createForClassInitializer())
-                                  .setCode(method -> throwExceptionCode));
-                  if (libraryClass.isInterface()) {
-                    classBuilder.setInterface();
-                  }
-                  if (!libraryClass.isFinal()) {
-                    classBuilder.unsetFinal();
-                  }
-                },
-                clazz -> eventConsumer.acceptMockedLibraryClass(clazz, libraryClass));
-    if (!eventConsumer.isEmpty()) {
-      for (DexProgramClass context : contexts) {
-        eventConsumer.acceptMockedLibraryClassContext(mockClass, libraryClass, context);
-      }
-    }
+    appView
+        .appInfo()
+        .getSyntheticItems()
+        .ensureGlobalClass(
+            () -> new MissingGlobalSyntheticsConsumerDiagnostic("API stubbing"),
+            kinds -> kinds.API_MODEL_STUB,
+            libraryClass.getType(),
+            contexts,
+            appView,
+            classBuilder -> {
+              classBuilder
+                  .setSuperType(libraryClass.getSuperType())
+                  .setInterfaces(Arrays.asList(libraryClass.getInterfaces().values))
+                  // Add throwing static initializer
+                  .addMethod(
+                      methodBuilder ->
+                          methodBuilder
+                              .setName(factory.classConstructorMethodName)
+                              .setProto(factory.createProto(factory.voidType))
+                              .setAccessFlags(MethodAccessFlags.createForClassInitializer())
+                              .setCode(method -> throwExceptionCode));
+              if (libraryClass.isInterface()) {
+                classBuilder.setInterface();
+              }
+              if (!libraryClass.isFinal()) {
+                classBuilder.unsetFinal();
+              }
+            },
+            clazz -> eventConsumer.acceptMockedLibraryClass(clazz, libraryClass),
+            clazz -> {
+              if (!eventConsumer.isEmpty()) {
+                for (DexProgramClass context : contexts) {
+                  eventConsumer.acceptMockedLibraryClassContext(clazz, libraryClass, context);
+                }
+              }
+            });
   }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java b/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java
index 208c8ad..a7bbaae 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java
@@ -20,6 +20,11 @@
   }
 
   @Override
+  default DexProgramClass asClass() {
+    return null;
+  }
+
+  @Override
   default ProgramField asField() {
     return null;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java
index 5cd5230..c8a0668 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java
@@ -416,17 +416,13 @@
 
   private void ensureRecordClass(
       RecordInstructionDesugaringEventConsumer eventConsumer, ProgramMethod context) {
-    DexProgramClass recordTagClass =
-        internalEnsureRecordClass(eventConsumer, ImmutableList.of(context));
-    eventConsumer.acceptRecordClassContext(recordTagClass, context);
+    internalEnsureRecordClass(eventConsumer, null, eventConsumer, ImmutableList.of(context));
   }
 
   private void ensureRecordClass(
       RecordClassSynthesizerDesugaringEventConsumer eventConsumer,
       Collection<DexProgramClass> recordClasses) {
-    DexProgramClass recordTagClass = internalEnsureRecordClass(eventConsumer, recordClasses);
-    recordClasses.forEach(
-        recordClass -> eventConsumer.acceptRecordClassContext(recordTagClass, recordClass));
+    internalEnsureRecordClass(eventConsumer, eventConsumer, null, recordClasses);
   }
 
   /**
@@ -439,16 +435,25 @@
    */
   private DexProgramClass internalEnsureRecordClass(
       RecordDesugaringEventConsumer eventConsumer,
+      RecordClassSynthesizerDesugaringEventConsumer recordClassSynthesizerDesugaringEventConsumer,
+      RecordInstructionDesugaringEventConsumer recordInstructionDesugaringEventConsumer,
       Collection<? extends ProgramDefinition> contexts) {
     DexItemFactory factory = appView.dexItemFactory();
     checkRecordTagNotPresent(factory);
-    return ensureRecordClassHelper(appView, contexts, eventConsumer);
+    return ensureRecordClassHelper(
+        appView,
+        contexts,
+        eventConsumer,
+        recordClassSynthesizerDesugaringEventConsumer,
+        recordInstructionDesugaringEventConsumer);
   }
 
   public static DexProgramClass ensureRecordClassHelper(
       AppView<?> appView,
       Collection<? extends ProgramDefinition> contexts,
-      RecordDesugaringEventConsumer eventConsumer) {
+      RecordDesugaringEventConsumer eventConsumer,
+      RecordClassSynthesizerDesugaringEventConsumer recordClassSynthesizerDesugaringEventConsumer,
+      RecordInstructionDesugaringEventConsumer recordInstructionDesugaringEventConsumer) {
     return appView
         .getSyntheticItems()
         .ensureGlobalClass(
@@ -461,7 +466,21 @@
               DexEncodedMethod init = synthesizeRecordInitMethod(appView);
               builder.setAbstract().setDirectMethods(ImmutableList.of(init));
             },
-            eventConsumer::acceptRecordClass);
+            eventConsumer::acceptRecordClass,
+            clazz -> {
+              if (recordClassSynthesizerDesugaringEventConsumer != null) {
+                for (ProgramDefinition context : contexts) {
+                  recordClassSynthesizerDesugaringEventConsumer.acceptRecordClassContext(
+                      clazz, context.asClass());
+                }
+              }
+              if (recordInstructionDesugaringEventConsumer != null) {
+                for (ProgramDefinition context : contexts) {
+                  recordInstructionDesugaringEventConsumer.acceptRecordClassContext(
+                      clazz, context.asMethod());
+                }
+              }
+            });
   }
 
   private void checkRecordTagNotPresent(DexItemFactory factory) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaring.java
index 1e5bf89..a5f2b75 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaring.java
@@ -221,22 +221,23 @@
       Collection<? extends ProgramDefinition> contexts) {
     assert contexts.stream()
         .allMatch(context -> context.getContextType() != appView.dexItemFactory().varHandleType);
-    DexProgramClass clazz =
-        appView
-            .getSyntheticItems()
-            .ensureGlobalClass(
-                () -> new MissingGlobalSyntheticsConsumerDiagnostic("VarHandle desugaring"),
-                kinds -> kinds.VAR_HANDLE,
-                appView.dexItemFactory().varHandleType,
-                contexts,
-                appView,
-                builder ->
-                    VarHandleDesugaringMethods.generateDesugarVarHandleClass(
-                        builder, appView.dexItemFactory()),
-                eventConsumer::acceptVarHandleDesugaringClass);
-    for (ProgramDefinition context : contexts) {
-      eventConsumer.acceptVarHandleDesugaringClassContext(clazz, context);
-    }
+    appView
+        .getSyntheticItems()
+        .ensureGlobalClass(
+            () -> new MissingGlobalSyntheticsConsumerDiagnostic("VarHandle desugaring"),
+            kinds -> kinds.VAR_HANDLE,
+            appView.dexItemFactory().varHandleType,
+            contexts,
+            appView,
+            builder ->
+                VarHandleDesugaringMethods.generateDesugarVarHandleClass(
+                    builder, appView.dexItemFactory()),
+            eventConsumer::acceptVarHandleDesugaringClass,
+            clazz -> {
+              for (ProgramDefinition context : contexts) {
+                eventConsumer.acceptVarHandleDesugaringClassContext(clazz, context);
+              }
+            });
   }
 
   private void ensureVarHandleClass(
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index e7dc070..5446ed9 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -666,6 +666,7 @@
   private DexProgramClass internalEnsureFixedProgramClass(
       SyntheticKind kind,
       Consumer<SyntheticProgramClassBuilder> classConsumer,
+      Consumer<DexProgramClass> onReferencingContextConsumer,
       Consumer<DexProgramClass> onCreationConsumer,
       SynthesizingContext outerContext,
       AppView<?> appView) {
@@ -675,6 +676,7 @@
     // Fast path is that the synthetic is already present. If so it must be a program class.
     DexProgramClass clazz = internalLookupProgramClass(type, kind, appView);
     if (clazz != null) {
+      onReferencingContextConsumer.accept(clazz);
       return clazz;
     }
     // Slow path creates the class using the context to make it thread safe.
@@ -696,6 +698,7 @@
               type,
               contextToType,
               appView);
+      onReferencingContextConsumer.accept(clazz);
       onCreationConsumer.accept(clazz);
       return clazz;
     }
@@ -805,8 +808,10 @@
       Consumer<DexProgramClass> onCreationConsumer) {
     SyntheticKind kind = kindSelector.select(naming);
     assert kind.isFixedSuffixSynthetic();
+    Consumer<DexProgramClass> onReferencingContextConsumer = ConsumerUtils.emptyConsumer();
     SynthesizingContext outerContext = internalGetOuterContext(context, appView);
-    return internalEnsureFixedProgramClass(kind, fn, onCreationConsumer, outerContext, appView);
+    return internalEnsureFixedProgramClass(
+        kind, fn, onCreationConsumer, onReferencingContextConsumer, outerContext, appView);
   }
 
   public ProgramMethod ensureFixedClassMethod(
@@ -1008,6 +1013,26 @@
       AppView<?> appView,
       Consumer<SyntheticProgramClassBuilder> fn,
       Consumer<DexProgramClass> onCreationConsumer) {
+    return ensureGlobalClass(
+        diagnosticSupplier,
+        kindSelector,
+        globalType,
+        contexts,
+        appView,
+        fn,
+        onCreationConsumer,
+        ConsumerUtils.emptyConsumer());
+  }
+
+  public DexProgramClass ensureGlobalClass(
+      Supplier<MissingGlobalSyntheticsConsumerDiagnostic> diagnosticSupplier,
+      SyntheticKindSelector kindSelector,
+      DexType globalType,
+      Collection<? extends ProgramDefinition> contexts,
+      AppView<?> appView,
+      Consumer<SyntheticProgramClassBuilder> fn,
+      Consumer<DexProgramClass> onCreationConsumer,
+      Consumer<DexProgramClass> onReferencingContextConsumer) {
     SyntheticKind kind = kindSelector.select(naming);
     assert kind.isGlobal();
     assert !contexts.isEmpty();
@@ -1017,7 +1042,8 @@
     // A global type is its own context.
     SynthesizingContext outerContext = SynthesizingContext.fromType(globalType);
     DexProgramClass globalSynthetic =
-        internalEnsureFixedProgramClass(kind, fn, onCreationConsumer, outerContext, appView);
+        internalEnsureFixedProgramClass(
+            kind, fn, onReferencingContextConsumer, onCreationConsumer, outerContext, appView);
     Consumer<DexProgramClass> globalSyntheticCreatedCallback =
         appView.options().testing.globalSyntheticCreatedCallback;
     if (globalSyntheticCreatedCallback != null) {