Add process and run to worklist

Change-Id: I7cca65fd683bf4dfd537d7188a171819270d15a0
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 8bbfcfd..c42f8c6 100644
--- a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
+++ b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
@@ -139,24 +139,25 @@
     if (!type.isClassType() || isJavaType(type)) {
       return;
     }
-    WorkList<DexType> workList = WorkList.newIdentityWorkList(type, seenTypes);
-    while (workList.hasNext()) {
-      DexClass clazz = appView.definitionFor(workList.next());
-      if (clazz == null || !clazz.isLibraryClass()) {
-        continue;
-      }
-      ComputedApiLevel androidApiLevel =
-          apiLevelCompute.computeApiLevelForLibraryReference(
-              clazz.type, ComputedApiLevel.unknown());
-      if (androidApiLevel.isGreaterThan(appView.computedMinApiLevel())
-          && androidApiLevel.isKnownApiLevel()) {
-        workList.addIfNotSeen(clazz.allImmediateSupertypes());
-        libraryClassesToMock.add(clazz.asLibraryClass());
-        referencingContexts
-            .computeIfAbsent(clazz.asLibraryClass(), ignoreKey(Sets::newConcurrentHashSet))
-            .add(context);
-      }
-    }
+    WorkList.newIdentityWorkList(type, seenTypes)
+        .process(
+            (classType, workList) -> {
+              DexClass clazz = appView.definitionFor(classType);
+              if (clazz == null || !clazz.isLibraryClass()) {
+                return;
+              }
+              ComputedApiLevel androidApiLevel =
+                  apiLevelCompute.computeApiLevelForLibraryReference(
+                      clazz.type, ComputedApiLevel.unknown());
+              if (androidApiLevel.isGreaterThan(appView.computedMinApiLevel())
+                  && androidApiLevel.isKnownApiLevel()) {
+                workList.addIfNotSeen(clazz.allImmediateSupertypes());
+                libraryClassesToMock.add(clazz.asLibraryClass());
+                referencingContexts
+                    .computeIfAbsent(clazz.asLibraryClass(), ignoreKey(Sets::newConcurrentHashSet))
+                    .add(context);
+              }
+            });
   }
 
   private boolean isJavaType(DexType type) {
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureContextBuilder.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureContextBuilder.java
index 3492c06..b761eb99b 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignatureContextBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureContextBuilder.java
@@ -182,20 +182,26 @@
 
   public static GenericSignatureContextBuilder createForSingleClass(
       AppView<?> appView, DexProgramClass clazz) {
-    WorkList<DexProgramClass> workList = WorkList.newIdentityWorkList(clazz);
-    while (workList.hasNext()) {
-      DexProgramClass current = workList.next();
-      DexClass outer = null;
-      if (current.getEnclosingMethodAttribute() != null) {
-        outer = appView.definitionFor(current.getEnclosingMethodAttribute().getEnclosingType());
-      } else if (current.getInnerClassAttributeForThisClass() != null) {
-        outer = appView.definitionFor(current.getInnerClassAttributeForThisClass().getOuter());
-      }
-      if (outer != null && outer.isProgramClass()) {
-        workList.addIfNotSeen(outer.asProgramClass());
-      }
-    }
-    return create(appView, workList.getSeenSet());
+    return create(
+        appView,
+        WorkList.newIdentityWorkList(clazz)
+            .process(
+                (current, workList) -> {
+                  DexClass outer = null;
+                  if (current.getEnclosingMethodAttribute() != null) {
+                    outer =
+                        appView.definitionFor(
+                            current.getEnclosingMethodAttribute().getEnclosingType());
+                  } else if (current.getInnerClassAttributeForThisClass() != null) {
+                    outer =
+                        appView.definitionFor(
+                            current.getInnerClassAttributeForThisClass().getOuter());
+                  }
+                  if (outer != null && outer.isProgramClass()) {
+                    workList.addIfNotSeen(outer.asProgramClass());
+                  }
+                })
+            .getSeenSet());
   }
 
   public TypeParameterContext computeTypeParameterContext(
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
index 1aa8117..98eab7f 100644
--- a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
@@ -217,26 +217,27 @@
       }
     }
 
-    while (worklist.hasNext()) {
-      DexClass clazz = worklist.next();
-      if (clazz.isProgramClass()) {
-        DexProgramClass programClass = clazz.asProgramClass();
-        if (isInstantiatedDirectly(programClass)
-            || isInterfaceWithUnknownSubtypeHierarchy(programClass)) {
-          if (onClass.apply(programClass).shouldBreak()) {
-            return TraversalContinuation.doBreak();
+    return worklist.run(
+        clazz -> {
+          if (clazz.isProgramClass()) {
+            DexProgramClass programClass = clazz.asProgramClass();
+            if (isInstantiatedDirectly(programClass)
+                || isInterfaceWithUnknownSubtypeHierarchy(programClass)) {
+              if (onClass.apply(programClass).shouldBreak()) {
+                return TraversalContinuation.doBreak();
+              }
+            }
           }
-        }
-      }
-      worklist.addIfNotSeen(instantiatedHierarchy.getOrDefault(clazz.type, Collections.emptySet()));
-      for (LambdaDescriptor lambda :
-          instantiatedLambdas.getOrDefault(clazz.type, Collections.emptyList())) {
-        if (onLambda.apply(lambda).shouldBreak()) {
-          return TraversalContinuation.doBreak();
-        }
-      }
-    }
-    return TraversalContinuation.doContinue();
+          worklist.addIfNotSeen(
+              instantiatedHierarchy.getOrDefault(clazz.type, Collections.emptySet()));
+          for (LambdaDescriptor lambda :
+              instantiatedLambdas.getOrDefault(clazz.type, Collections.emptyList())) {
+            if (onLambda.apply(lambda).shouldBreak()) {
+              return TraversalContinuation.doBreak();
+            }
+          }
+          return TraversalContinuation.doContinue();
+        });
   }
 
   public Set<DexType> getInstantiatedLambdaInterfaces() {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoConstructorCollisions.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoConstructorCollisions.java
index e77b31a..6e94908 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoConstructorCollisions.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoConstructorCollisions.java
@@ -79,33 +79,33 @@
     // Find the set of types that must not be merged, because they could lead to a constructor
     // collision.
     Set<DexType> collisionResolution = Sets.newIdentityHashSet();
-    WorkList<DexProgramClass> workList = WorkList.newIdentityWorkList(appView.appInfo().classes());
-    while (workList.hasNext()) {
-      // Iterate over all the instance initializers of the current class. If the current class is in
-      // a merge group, we must include all constructors of the entire merge group.
-      DexProgramClass current = workList.next();
-      Iterable<DexProgramClass> group =
-          groupsByType.containsKey(current.getType())
-              ? groupsByType.get(current.getType())
-              : IterableUtils.singleton(current);
-      Set<DexMethod> seen = Sets.newIdentityHashSet();
-      for (DexProgramClass clazz : group) {
-        for (DexEncodedMethod method :
-            clazz.directMethods(DexEncodedMethod::isInstanceInitializer)) {
-          // Rewrite the constructor reference using the current merge groups.
-          DexMethod newReference = rewriteReference(method.getReference(), groupsByType);
-          if (!seen.add(newReference)) {
-            // Found a collision. Block all referenced types from being merged.
-            for (DexType type : method.getProto().getBaseTypes(dexItemFactory)) {
-              if (type.isClassType() && groupsByType.containsKey(type)) {
-                collisionResolution.add(type);
+    // Iterate over all the instance initializers of the current class. If the current class is in
+    // a merge group, we must include all constructors of the entire merge group.
+    WorkList.newIdentityWorkList(appView.appInfo().classes())
+        .process(
+            (current, workList) -> {
+              Iterable<DexProgramClass> group =
+                  groupsByType.containsKey(current.getType())
+                      ? groupsByType.get(current.getType())
+                      : IterableUtils.singleton(current);
+              Set<DexMethod> seen = Sets.newIdentityHashSet();
+              for (DexProgramClass clazz : group) {
+                for (DexEncodedMethod method :
+                    clazz.directMethods(DexEncodedMethod::isInstanceInitializer)) {
+                  // Rewrite the constructor reference using the current merge groups.
+                  DexMethod newReference = rewriteReference(method.getReference(), groupsByType);
+                  if (!seen.add(newReference)) {
+                    // Found a collision. Block all referenced types from being merged.
+                    for (DexType type : method.getProto().getBaseTypes(dexItemFactory)) {
+                      if (type.isClassType() && groupsByType.containsKey(type)) {
+                        collisionResolution.add(type);
+                      }
+                    }
+                  }
+                }
               }
-            }
-          }
-        }
-      }
-      workList.markAsSeen(group);
-    }
+              workList.markAsSeen(group);
+            });
     return collisionResolution;
   }
 
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyDirectlyConnectedOrUnrelatedInterfaces.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyDirectlyConnectedOrUnrelatedInterfaces.java
index fd24d1f..a64402a 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyDirectlyConnectedOrUnrelatedInterfaces.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyDirectlyConnectedOrUnrelatedInterfaces.java
@@ -133,21 +133,21 @@
     WorkList<DexProgramClass> workList = WorkList.newWorkList(new LinkedHashSet<>());
     // Intentionally not marking `clazz` as seen, since we only want the strict sub/super types.
     workList.addIgnoringSeenSet(clazz);
-    while (workList.hasNext()) {
-      DexProgramClass interfaceDefinition = workList.next();
-      MergeGroup group = committed.get(interfaceDefinition);
-      if (group != null) {
-        workList.addIfNotSeen(group);
-      }
-      for (DexType immediateSubOrSuperInterfaceType :
-          immediateSubOrSuperInterfacesProvider.apply(interfaceDefinition)) {
-        DexProgramClass immediateSubOrSuperInterface =
-            asProgramClassOrNull(appView.definitionFor(immediateSubOrSuperInterfaceType));
-        if (immediateSubOrSuperInterface != null) {
-          workList.addIfNotSeen(immediateSubOrSuperInterface);
-        }
-      }
-    }
+    workList.process(
+        interfaceDefinition -> {
+          MergeGroup group = committed.get(interfaceDefinition);
+          if (group != null) {
+            workList.addIfNotSeen(group);
+          }
+          for (DexType immediateSubOrSuperInterfaceType :
+              immediateSubOrSuperInterfacesProvider.apply(interfaceDefinition)) {
+            DexProgramClass immediateSubOrSuperInterface =
+                asProgramClassOrNull(appView.definitionFor(immediateSubOrSuperInterfaceType));
+            if (immediateSubOrSuperInterface != null) {
+              workList.addIfNotSeen(immediateSubOrSuperInterface);
+            }
+          }
+        });
     assert !workList.isSeen(clazz);
     return workList.getMutableSeenSet();
   }
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java b/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
index 808913a..1a2d82b 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
@@ -258,20 +258,22 @@
     if (originalClass.isLibraryClass()) {
       return originalClass;
     }
-    WorkList<DexClass> workList = WorkList.newIdentityWorkList(originalClass);
-    while (workList.hasNext()) {
-      DexClass clazz = workList.next();
-      if (clazz.isLibraryClass()) {
-        return clazz;
-      } else if (clazz.lookupMember(reference) != null) {
-        return clazz;
-      } else if (clazz.getSuperType() != null) {
-        appInfo
-            .contextIndependentDefinitionForWithResolutionResult(clazz.getSuperType())
-            .forEachClassResolutionResult(workList::addIfNotSeen);
-      }
-    }
-    return null;
+    return WorkList.newIdentityWorkList(originalClass)
+        .run(
+            (clazz, workList) -> {
+              if (clazz.isLibraryClass()) {
+                return TraversalContinuation.doBreak(clazz);
+              } else if (clazz.lookupMember(reference) != null) {
+                return TraversalContinuation.doBreak(clazz);
+              } else if (clazz.getSuperType() != null) {
+                appInfo
+                    .contextIndependentDefinitionForWithResolutionResult(clazz.getSuperType())
+                    .forEachClassResolutionResult(workList::addIfNotSeen);
+              }
+              return TraversalContinuation.doContinue();
+            })
+        .asBreakOrDefault(null)
+        .getValue();
   }
 
   private static Set<DexClass> findAllFirstLibraryInterfacesOrProgramClassWithDefinition(
diff --git a/src/main/java/com/android/tools/r8/utils/TraversalContinuation.java b/src/main/java/com/android/tools/r8/utils/TraversalContinuation.java
index c33d020..f2b96f3 100644
--- a/src/main/java/com/android/tools/r8/utils/TraversalContinuation.java
+++ b/src/main/java/com/android/tools/r8/utils/TraversalContinuation.java
@@ -18,6 +18,11 @@
     return null;
   }
 
+  public Break<TB, TC> asBreakOrDefault(TB defaultValue) {
+    Break<TB, TC> breakValue = asBreak();
+    return breakValue == null ? doBreak(defaultValue) : breakValue;
+  }
+
   public boolean isContinue() {
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/utils/WorkList.java b/src/main/java/com/android/tools/r8/utils/WorkList.java
index 79c2ed7..5aa700a 100644
--- a/src/main/java/com/android/tools/r8/utils/WorkList.java
+++ b/src/main/java/com/android/tools/r8/utils/WorkList.java
@@ -10,6 +10,10 @@
 import java.util.Deque;
 import java.util.HashSet;
 import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Function;
 
 public class WorkList<T> {
 
@@ -100,6 +104,32 @@
     return false;
   }
 
+  public WorkList<T> process(Consumer<T> consumer) {
+    return process((item, ignored) -> consumer.accept(item));
+  }
+
+  public WorkList<T> process(BiConsumer<T, WorkList<T>> consumer) {
+    while (hasNext()) {
+      consumer.accept(next(), this);
+    }
+    return this;
+  }
+
+  public <TB, TC> TraversalContinuation<TB, TC> run(Function<T, TraversalContinuation<TB, TC>> fn) {
+    return run((item, ignored) -> fn.apply(item));
+  }
+
+  public <TB, TC> TraversalContinuation<TB, TC> run(
+      BiFunction<T, WorkList<T>, TraversalContinuation<TB, TC>> fn) {
+    while (hasNext()) {
+      TraversalContinuation<TB, TC> result = fn.apply(next(), this);
+      if (result.shouldBreak()) {
+        return result;
+      }
+    }
+    return TraversalContinuation.doContinue();
+  }
+
   public void addFirstIgnoringSeenSet(T item) {
     workingList.addFirst(item);
   }