Instantiate existing dataflow analysis framework for cf code

Change-Id: I7a0a4bbb4a083bfc175fa587fc7194dd88a84b71
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfGoto.java b/src/main/java/com/android/tools/r8/cf/code/CfGoto.java
index fa3a8a5..07f135e 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfGoto.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfGoto.java
@@ -19,7 +19,9 @@
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.TraversalContinuation;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import java.util.function.BiFunction;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 
@@ -63,6 +65,14 @@
   }
 
   @Override
+  public <BT, CT> TraversalContinuation<BT, CT> traverseNormalTargets(
+      BiFunction<? super CfInstruction, ? super CT, TraversalContinuation<BT, CT>> fn,
+      CfInstruction fallthroughInstruction,
+      CT initialValue) {
+    return fn.apply(target, initialValue);
+  }
+
+  @Override
   public void write(
       AppView<?> appView,
       ProgramMethod context,
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfIf.java b/src/main/java/com/android/tools/r8/cf/code/CfIf.java
index 833ac44..d1f2eb1 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfIf.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfIf.java
@@ -23,7 +23,9 @@
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.TraversalContinuation;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import java.util.function.BiFunction;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 
@@ -66,6 +68,16 @@
     return target;
   }
 
+  @Override
+  public <BT, CT> TraversalContinuation<BT, CT> traverseNormalTargets(
+      BiFunction<? super CfInstruction, ? super CT, TraversalContinuation<BT, CT>> fn,
+      CfInstruction fallthroughInstruction,
+      CT initialValue) {
+    return fn.apply(target, initialValue)
+        .ifContinueThen(
+            continuation -> fn.apply(fallthroughInstruction, continuation.getValueOrDefault(null)));
+  }
+
   public int getOpcode() {
     switch (kind) {
       case EQ:
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java b/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java
index c8129a6..37b1c2b 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfIfCmp.java
@@ -24,7 +24,9 @@
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.TraversalContinuation;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import java.util.function.BiFunction;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 
@@ -67,6 +69,16 @@
     return target;
   }
 
+  @Override
+  public <BT, CT> TraversalContinuation<BT, CT> traverseNormalTargets(
+      BiFunction<? super CfInstruction, ? super CT, TraversalContinuation<BT, CT>> fn,
+      CfInstruction fallthroughInstruction,
+      CT initialValue) {
+    return fn.apply(target, initialValue)
+        .ifContinueThen(
+            continuation -> fn.apply(fallthroughInstruction, continuation.getValueOrDefault(null)));
+  }
+
   public int getOpcode() {
     switch (kind) {
       case EQ:
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
index 40b9737..58c8280 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
@@ -25,8 +25,11 @@
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.TraversalContinuation;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
 import java.util.ListIterator;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
 import org.objectweb.asm.MethodVisitor;
 
 public abstract class CfInstruction implements CfOrDexInstruction {
@@ -106,6 +109,26 @@
     return null;
   }
 
+  public final void forEachNormalTarget(
+      Consumer<? super CfInstruction> consumer, CfInstruction fallthroughInstruction) {
+    traverseNormalTargets(
+        (target, ignore) -> {
+          consumer.accept(target);
+          return TraversalContinuation.doContinue();
+        },
+        fallthroughInstruction,
+        null);
+  }
+
+  public <BT, CT> TraversalContinuation<BT, CT> traverseNormalTargets(
+      BiFunction<? super CfInstruction, ? super CT, TraversalContinuation<BT, CT>> fn,
+      CfInstruction fallthroughInstruction,
+      CT initialValue) {
+    // The method is overridden in each jump instruction.
+    assert !isJump();
+    return fn.apply(fallthroughInstruction, initialValue);
+  }
+
   @Override
   public CfInstruction asCfInstruction() {
     return this;
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfReturn.java b/src/main/java/com/android/tools/r8/cf/code/CfReturn.java
index d06db87..134e4d2 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfReturn.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfReturn.java
@@ -22,7 +22,9 @@
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.TraversalContinuation;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import java.util.function.BiFunction;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 
@@ -72,6 +74,14 @@
   }
 
   @Override
+  public <BT, CT> TraversalContinuation<BT, CT> traverseNormalTargets(
+      BiFunction<? super CfInstruction, ? super CT, TraversalContinuation<BT, CT>> fn,
+      CfInstruction fallthroughInstruction,
+      CT initialValue) {
+    return TraversalContinuation.doContinue(initialValue);
+  }
+
+  @Override
   public boolean isJump() {
     return true;
   }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java b/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java
index d492b04..56eab9b 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfReturnVoid.java
@@ -19,13 +19,23 @@
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.TraversalContinuation;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import java.util.function.BiFunction;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 
 public class CfReturnVoid extends CfInstruction {
 
   @Override
+  public <BT, CT> TraversalContinuation<BT, CT> traverseNormalTargets(
+      BiFunction<? super CfInstruction, ? super CT, TraversalContinuation<BT, CT>> fn,
+      CfInstruction fallthroughInstruction,
+      CT initialValue) {
+    return TraversalContinuation.doContinue(initialValue);
+  }
+
+  @Override
   public boolean isJump() {
     return true;
   }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java b/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java
index a86bcd1..7e2564c 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java
@@ -21,9 +21,12 @@
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.TraversalContinuation;
+import com.android.tools.r8.utils.TraversalUtils;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
 import it.unimi.dsi.fastutil.ints.IntArrayList;
 import java.util.List;
+import java.util.function.BiFunction;
 import org.objectweb.asm.Label;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
@@ -47,6 +50,16 @@
   }
 
   @Override
+  public <BT, CT> TraversalContinuation<BT, CT> traverseNormalTargets(
+      BiFunction<? super CfInstruction, ? super CT, TraversalContinuation<BT, CT>> fn,
+      CfInstruction fallthroughInstruction,
+      CT initialValue) {
+    return TraversalUtils.traverseIterable(targets, fn, initialValue)
+        .ifContinueThen(
+            continuation -> fn.apply(defaultTarget, continuation.getValueOrDefault(null)));
+  }
+
+  @Override
   public int getCompareToId() {
     return kind == Kind.LOOKUP ? Opcodes.LOOKUPSWITCH : Opcodes.TABLESWITCH;
   }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfThrow.java b/src/main/java/com/android/tools/r8/cf/code/CfThrow.java
index 6e5eb78..6498e4f 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfThrow.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfThrow.java
@@ -20,13 +20,23 @@
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.TraversalContinuation;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
+import java.util.function.BiFunction;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 
 public class CfThrow extends CfInstruction {
 
   @Override
+  public <BT, CT> TraversalContinuation<BT, CT> traverseNormalTargets(
+      BiFunction<? super CfInstruction, ? super CT, TraversalContinuation<BT, CT>> fn,
+      CfInstruction fallthroughInstruction,
+      CT initialValue) {
+    return TraversalContinuation.doContinue(initialValue);
+  }
+
+  @Override
   public boolean isJump() {
     return true;
   }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfTryCatch.java b/src/main/java/com/android/tools/r8/cf/code/CfTryCatch.java
index 2f821be..22aac9a 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfTryCatch.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfTryCatch.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.utils.structural.CompareToVisitor;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.function.Consumer;
 
 public class CfTryCatch {
   public final CfLabel start;
@@ -28,6 +29,22 @@
     assert verifyAllNonNull(guards);
   }
 
+  public void forEachTarget(Consumer<CfLabel> consumer) {
+    targets.forEach(consumer);
+  }
+
+  public CfLabel getStart() {
+    return start;
+  }
+
+  public CfLabel getEnd() {
+    return end;
+  }
+
+  public List<CfLabel> getTargets() {
+    return targets;
+  }
+
   private static boolean verifyAllNonNull(List<DexType> types) {
     for (DexType type : types) {
       assert type != null;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/cf/CfBlock.java b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/cf/CfBlock.java
new file mode 100644
index 0000000..b86915c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/cf/CfBlock.java
@@ -0,0 +1,97 @@
+// Copyright (c) 2022, 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.analysis.framework.intraprocedural.cf;
+
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.utils.SetUtils;
+import java.util.ArrayList;
+import java.util.List;
+
+/** A basic block for {@link com.android.tools.r8.graph.CfCode}. */
+public class CfBlock {
+
+  // The CfCode instruction index of the block's first instruction.
+  int firstInstructionIndex = -1;
+
+  // The CfCode instruction index of the block's last instruction.
+  int lastInstructionIndex = -1;
+
+  // The predecessors of the block. These are stored explicitly (unlike the successors) since they
+  // cannot efficiently be computed from the block.
+  final List<CfBlock> predecessors = new ArrayList<>();
+
+  // The exceptional predecessors of the block.
+  final List<CfBlock> exceptionalPredecessors = new ArrayList<>();
+
+  // The exceptional successors of the block (i.e., the catch handlers of the block).
+  final List<CfBlock> exceptionalSuccessors = new ArrayList<>();
+
+  public CfInstruction getFallthroughInstruction(CfCode code) {
+    int fallthroughInstructionIndex = getLastInstructionIndex() + 1;
+    return fallthroughInstructionIndex < code.getInstructions().size()
+        ? code.getInstructions().get(fallthroughInstructionIndex)
+        : null;
+  }
+
+  public int getFirstInstructionIndex() {
+    return firstInstructionIndex;
+  }
+
+  public CfInstruction getLastInstruction(CfCode code) {
+    return code.getInstructions().get(lastInstructionIndex);
+  }
+
+  public int getLastInstructionIndex() {
+    return lastInstructionIndex;
+  }
+
+  public List<CfBlock> getPredecessors() {
+    return predecessors;
+  }
+
+  // TODO(b/214496607): This currently only encodes the graph, but we likely need to include the
+  //  guard types here.
+  public List<CfBlock> getExceptionalPredecessors() {
+    return exceptionalPredecessors;
+  }
+
+  // TODO(b/214496607): This currently only encodes the graph, but we likely need to include the
+  //  guard types here.
+  public List<CfBlock> getExceptionalSuccessors() {
+    return exceptionalSuccessors;
+  }
+
+  // A mutable interface for block construction.
+  static class MutableCfBlock extends CfBlock {
+
+    void addPredecessor(CfBlock block) {
+      predecessors.add(block);
+    }
+
+    void addExceptionalPredecessor(CfBlock block) {
+      exceptionalPredecessors.add(block);
+    }
+
+    void addExceptionalSuccessor(CfBlock block) {
+      exceptionalSuccessors.add(block);
+    }
+
+    void setFirstInstructionIndex(int firstInstructionIndex) {
+      this.firstInstructionIndex = firstInstructionIndex;
+    }
+
+    void setLastInstructionIndex(int lastInstructionIndex) {
+      this.lastInstructionIndex = lastInstructionIndex;
+    }
+
+    boolean validate() {
+      assert 0 <= firstInstructionIndex;
+      assert firstInstructionIndex <= lastInstructionIndex;
+      assert SetUtils.newIdentityHashSet(predecessors).size() == predecessors.size();
+      return true;
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/cf/CfControlFlowGraph.java b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/cf/CfControlFlowGraph.java
new file mode 100644
index 0000000..878d32d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/cf/CfControlFlowGraph.java
@@ -0,0 +1,110 @@
+// Copyright (c) 2022, 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.analysis.framework.intraprocedural.cf;
+
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.ir.analysis.framework.intraprocedural.ControlFlowGraph;
+import com.android.tools.r8.utils.TraversalContinuation;
+import com.android.tools.r8.utils.TraversalUtils;
+import java.util.Map;
+import java.util.function.BiFunction;
+
+/**
+ * The following provides a control flow graph for a piece of {@link CfCode}.
+ *
+ * <p>In the {@link CfControlFlowGraph}, each instruction that is the target of a jump (including
+ * fallthrough targets following jumps) starts a new basic block. The first instruction in {@link
+ * CfCode} also starts a new block.
+ *
+ * <p>Each block is identified by the first instruction of the block.
+ */
+public class CfControlFlowGraph implements ControlFlowGraph<CfBlock, CfInstruction> {
+
+  // Mapping from block entry instructions to cf blocks.
+  private final Map<CfInstruction, ? extends CfBlock> blocks;
+  private final CfCode code;
+
+  private CfControlFlowGraph(Map<CfInstruction, ? extends CfBlock> blocks, CfCode code) {
+    this.blocks = blocks;
+    this.code = code;
+  }
+
+  private static Builder builder() {
+    return new Builder();
+  }
+
+  public static CfControlFlowGraph create(CfCode code) {
+    return builder().build(code);
+  }
+
+  private CfBlock getBlock(CfInstruction blockEntry) {
+    assert blocks.containsKey(blockEntry);
+    return blocks.get(blockEntry);
+  }
+
+  @Override
+  public <BT, CT> TraversalContinuation<BT, CT> traverseNormalPredecessors(
+      CfBlock block,
+      BiFunction<? super CfBlock, ? super CT, TraversalContinuation<BT, CT>> fn,
+      CT initialValue) {
+    return TraversalUtils.traverseIterable(block.getPredecessors(), fn, initialValue);
+  }
+
+  @Override
+  public <BT, CT> TraversalContinuation<BT, CT> traverseExceptionalPredecessors(
+      CfBlock block,
+      BiFunction<? super CfBlock, ? super CT, TraversalContinuation<BT, CT>> fn,
+      CT initialValue) {
+    return TraversalUtils.traverseIterable(block.getExceptionalPredecessors(), fn, initialValue);
+  }
+
+  @Override
+  public <BT, CT> TraversalContinuation<BT, CT> traverseNormalSuccessors(
+      CfBlock block,
+      BiFunction<? super CfBlock, ? super CT, TraversalContinuation<BT, CT>> fn,
+      CT initialValue) {
+    CfInstruction blockExit = block.getLastInstruction(code);
+    CfInstruction fallthroughInstruction = block.getFallthroughInstruction(code);
+    return blockExit.traverseNormalTargets(
+        (target, value) -> fn.apply(getBlock(target), value), fallthroughInstruction, initialValue);
+  }
+
+  @Override
+  public <BT, CT> TraversalContinuation<BT, CT> traverseExceptionalSuccessors(
+      CfBlock block,
+      BiFunction<? super CfBlock, ? super CT, TraversalContinuation<BT, CT>> fn,
+      CT initialValue) {
+    return TraversalUtils.traverseIterable(block.getExceptionalSuccessors(), fn, initialValue);
+  }
+
+  @Override
+  public <BT, CT> TraversalContinuation<BT, CT> traverseInstructions(
+      CfBlock block,
+      BiFunction<CfInstruction, CT, TraversalContinuation<BT, CT>> fn,
+      CT initialValue) {
+    TraversalContinuation<BT, CT> traversalContinuation =
+        TraversalContinuation.doContinue(initialValue);
+    for (int instructionIndex = block.getFirstInstructionIndex();
+        instructionIndex <= block.getLastInstructionIndex();
+        instructionIndex++) {
+      CfInstruction instruction = code.getInstructions().get(instructionIndex);
+      traversalContinuation = fn.apply(instruction, traversalContinuation.asContinue().getValue());
+      if (traversalContinuation.shouldBreak()) {
+        break;
+      }
+    }
+    return traversalContinuation;
+  }
+
+  private static class Builder {
+
+    CfControlFlowGraph build(CfCode code) {
+      // TODO(b/214496607): Implement cfg construction.
+      throw new Unimplemented();
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/cf/CfIntraproceduralDataflowAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/cf/CfIntraproceduralDataflowAnalysis.java
new file mode 100644
index 0000000..b4ca696
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/framework/intraprocedural/cf/CfIntraproceduralDataflowAnalysis.java
@@ -0,0 +1,21 @@
+// Copyright (c) 2022, 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.analysis.framework.intraprocedural.cf;
+
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.ir.analysis.framework.intraprocedural.AbstractState;
+import com.android.tools.r8.ir.analysis.framework.intraprocedural.AbstractTransferFunction;
+import com.android.tools.r8.ir.analysis.framework.intraprocedural.IntraProceduralDataflowAnalysisBase;
+
+public class CfIntraproceduralDataflowAnalysis<StateType extends AbstractState<StateType>>
+    extends IntraProceduralDataflowAnalysisBase<CfBlock, CfInstruction, StateType> {
+
+  public CfIntraproceduralDataflowAnalysis(
+      StateType bottom,
+      CfControlFlowGraph cfg,
+      AbstractTransferFunction<CfBlock, CfInstruction, StateType> transfer) {
+    super(bottom, cfg, transfer);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/TraversalUtils.java b/src/main/java/com/android/tools/r8/utils/TraversalUtils.java
index c24769a..25e1be0 100644
--- a/src/main/java/com/android/tools/r8/utils/TraversalUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/TraversalUtils.java
@@ -6,6 +6,7 @@
 
 import static com.android.tools.r8.utils.FunctionUtils.ignoreArgument;
 
+import java.util.function.BiFunction;
 import java.util.function.Consumer;
 import java.util.function.Function;
 
@@ -37,4 +38,20 @@
         ignoreArgument(() -> TraversalContinuation.breakIf(counter.incrementAndGet() > value)));
     return counter.get() > value;
   }
+
+  public static <S, BT, CT> TraversalContinuation<BT, CT> traverseIterable(
+      Iterable<S> iterable,
+      BiFunction<? super S, ? super CT, TraversalContinuation<BT, CT>> fn,
+      CT initialValue) {
+    TraversalContinuation<BT, CT> traversalContinuation =
+        TraversalContinuation.doContinue(initialValue);
+    for (S element : iterable) {
+      traversalContinuation =
+          fn.apply(element, traversalContinuation.asContinue().getValueOrDefault(null));
+      if (traversalContinuation.isBreak()) {
+        break;
+      }
+    }
+    return traversalContinuation;
+  }
 }