Fix StackOverflowError from NoClassInitializerCycles

Bug: b/236992167
Bug: b/230504175
Change-Id: I8c7a6a855bf0288b42909b7ac6e49019149b72f5
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassInitializerCycles.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassInitializerCycles.java
index fa9bbde..7cb0c68 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassInitializerCycles.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassInitializerCycles.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.horizontalclassmerging.policies;
 
 import static com.android.tools.r8.graph.DexClassAndMethod.asProgramMethodOrNull;
+import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
 import static com.android.tools.r8.ir.desugar.LambdaDescriptor.isLambdaMetafactoryMethod;
 import static com.android.tools.r8.utils.MapUtils.ignoreKey;
 
@@ -26,6 +27,7 @@
 import com.android.tools.r8.utils.InternalOptions.HorizontalClassMergerOptions;
 import com.android.tools.r8.utils.SetUtils;
 import com.android.tools.r8.utils.TraversalContinuation;
+import com.android.tools.r8.utils.WorkList;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Sets;
@@ -321,10 +323,6 @@
       worklist.clear();
     }
 
-    boolean markClassInitializerAsSeen(DexProgramClass clazz) {
-      return seenClassInitializers.add(clazz);
-    }
-
     boolean enqueueMethod(ProgramMethod method) {
       if (seenMethods.add(method)) {
         worklist.addLast(method);
@@ -434,44 +432,38 @@
         return appView.appInfo().isSubtype(getContext().getHolder(), clazz);
       }
 
-      private void triggerClassInitializer(DexType type) {
-        DexProgramClass clazz = type.asProgramClass(appView);
-        if (clazz != null) {
-          triggerClassInitializer(clazz);
-        }
-      }
-
-      private void triggerClassInitializer(DexProgramClass clazz) {
-        if (!markClassInitializerAsSeen(clazz)) {
-          return;
-        }
-
-        if (groupMembers.contains(clazz)) {
-          if (hasSingleTracingRoot(clazz)) {
-            // We found an execution path from the class initializer of the given class back to its
-            // own class initializer. Therefore this class is not eligible for merging.
-            fail();
-          } else {
-            // Record that this class initializer is reachable from the tracing roots.
-            recordClassInitializerReachableFromTracingRoots(clazz);
+      private void triggerClassInitializer(DexProgramClass root) {
+        WorkList<DexProgramClass> worklist = WorkList.newWorkList(seenClassInitializers);
+        worklist.addIfNotSeen(root);
+        while (worklist.hasNext()) {
+          DexProgramClass clazz = worklist.next();
+          if (groupMembers.contains(clazz)) {
+            if (hasSingleTracingRoot(clazz)) {
+              // We found an execution path from the class initializer of the given class back to
+              // its own class initializer. Therefore this class is not eligible for merging.
+              fail();
+            } else {
+              // Record that this class initializer is reachable from the tracing roots.
+              recordClassInitializerReachableFromTracingRoots(clazz);
+            }
           }
-        }
 
-        ProgramMethod classInitializer = clazz.getProgramClassInitializer();
-        if (classInitializer != null) {
-          if (!enqueueMethod(classInitializer)) {
+          ProgramMethod classInitializer = clazz.getProgramClassInitializer();
+          if (classInitializer != null && !enqueueMethod(classInitializer)) {
             // This class initializer is already seen in the current context, thus all of the parent
             // class initializers are also seen in the current context.
             return;
           }
-        }
 
-        triggerClassInitializer(clazz.getSuperType());
+          DexProgramClass superClass =
+              asProgramClassOrNull(appView.definitionFor(clazz.getSuperType()));
+          if (superClass != null) {
+            worklist.addIfNotSeen(superClass);
+          }
 
-        MergeGroup other = allGroups.get(clazz);
-        if (other != null && other != group) {
-          for (DexProgramClass member : other) {
-            triggerClassInitializer(member);
+          MergeGroup other = allGroups.get(clazz);
+          if (other != null && other != group) {
+            worklist.addIfNotSeen(other);
           }
         }
       }