Move frame verification logic to CfFrameVerifier

Change-Id: Id6e11b815982c63c9ccaebb661982c959fb6c9a2
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrameVerificationHelper.java b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerificationHelper.java
deleted file mode 100644
index a4131e9..0000000
--- a/src/main/java/com/android/tools/r8/cf/code/CfFrameVerificationHelper.java
+++ /dev/null
@@ -1,226 +0,0 @@
-// Copyright (c) 2020, 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.cf.code;
-
-import com.android.tools.r8.cf.code.CfAssignability.AssignabilityResult;
-import com.android.tools.r8.cf.code.frame.FrameType;
-import com.android.tools.r8.cf.code.frame.PreciseFrameType;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.CfCode;
-import com.android.tools.r8.graph.CfCodeDiagnostics;
-import com.android.tools.r8.graph.CfCodeStackMapValidatingException;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.optimize.interfaces.analysis.CfAnalysisConfig;
-import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
-import com.android.tools.r8.optimize.interfaces.analysis.ConcreteCfFrameState;
-import com.android.tools.r8.utils.TraversalContinuation;
-import com.android.tools.r8.utils.collections.ImmutableDeque;
-import com.google.common.collect.Sets;
-import java.util.ArrayDeque;
-import java.util.Deque;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-public class CfFrameVerificationHelper implements CfAnalysisConfig {
-
-  private final AppView<?> appView;
-  private final CfAssignability assignability;
-  private final CfCode code;
-  private final GraphLens codeLens;
-  private final DexItemFactory factory;
-  private final ProgramMethod method;
-  private final DexMethod previousMethod;
-
-  private final Map<CfLabel, CfFrame> stateMap;
-  private final List<CfTryCatch> tryCatchRanges;
-
-  private final Deque<CfTryCatch> currentCatchRanges = new ArrayDeque<>();
-  private final Set<CfLabel> tryCatchRangeLabels;
-
-  public CfFrameVerificationHelper(
-      AppView<?> appView,
-      CfCode code,
-      GraphLens codeLens,
-      ProgramMethod method,
-      Map<CfLabel, CfFrame> stateMap,
-      List<CfTryCatch> tryCatchRanges) {
-    this.appView = appView;
-    this.assignability = new CfAssignability(appView);
-    this.code = code;
-    this.codeLens = codeLens;
-    this.method = method;
-    this.previousMethod =
-        appView.graphLens().getOriginalMethodSignature(method.getReference(), codeLens);
-    this.stateMap = stateMap;
-    this.tryCatchRanges = tryCatchRanges;
-    this.factory = appView.dexItemFactory();
-    // Compute all labels that marks a start or end to catch ranges.
-    tryCatchRangeLabels = Sets.newIdentityHashSet();
-    for (CfTryCatch tryCatchRange : tryCatchRanges) {
-      tryCatchRangeLabels.add(tryCatchRange.start);
-      tryCatchRangeLabels.add(tryCatchRange.end);
-    }
-  }
-
-  @Override
-  public CfAssignability getAssignability() {
-    return assignability;
-  }
-
-  @Override
-  public DexMethod getCurrentContext() {
-    return previousMethod;
-  }
-
-  @Override
-  public int getMaxLocals() {
-    return code.getMaxLocals();
-  }
-
-  @Override
-  public int getMaxStack() {
-    return code.getMaxStack();
-  }
-
-  @Override
-  public boolean isImmediateSuperClassOfCurrentContext(DexType type) {
-    // If the code is rewritten according to the graph lens, we perform a strict check that the
-    // given type is the same as the current holder's super class.
-    if (codeLens == appView.graphLens()) {
-      return type == method.getHolder().getSuperType();
-    }
-    // Otherwise, we don't know what the super class of the current class was at the point of the
-    // code lens. We return true, which has the consequence that we may accept a constructor call
-    // for an uninitialized-this value where the constructor is not defined in the immediate parent
-    // class.
-    return true;
-  }
-
-  @Override
-  public boolean isStrengthenFramesEnabled() {
-    return false;
-  }
-
-  public void seenLabel(CfLabel label) {
-    if (tryCatchRangeLabels.contains(label)) {
-      for (CfTryCatch tryCatchRange : tryCatchRanges) {
-        if (tryCatchRange.start == label) {
-          currentCatchRanges.add(tryCatchRange);
-        }
-      }
-      currentCatchRanges.removeIf(currentRange -> currentRange.end == label);
-    }
-  }
-
-  public CfCodeDiagnostics checkTryCatchRanges() {
-    for (CfTryCatch tryCatchRange : tryCatchRanges) {
-      CfCodeDiagnostics diagnostics = checkTryCatchRange(tryCatchRange);
-      if (diagnostics != null) {
-        return diagnostics;
-      }
-    }
-    return null;
-  }
-
-  public CfCodeDiagnostics checkTryCatchRange(CfTryCatch tryCatchRange) {
-    // According to the spec:
-    // https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.10.1
-    // saying ` and the handler's target (the initial instruction of the handler code) is type
-    // safe assuming an incoming type state T. The type state T is derived from ExcStackFrame
-    // by replacing the operand stack with a stack whose sole element is the handler's
-    // exception class.
-    for (CfLabel target : tryCatchRange.getTargets()) {
-      CfFrame destinationFrame = stateMap.get(target);
-      if (destinationFrame == null) {
-        return CfCodeStackMapValidatingException.invalidTryCatchRange(
-            method, tryCatchRange, "No frame for target catch range target", appView);
-      }
-      // From the spec: the handler's exception class is assignable to the class Throwable.
-      for (DexType guard : tryCatchRange.guards) {
-        if (!assignability.isAssignable(guard, factory.throwableType)) {
-          return CfCodeStackMapValidatingException.invalidTryCatchRange(
-              method,
-              tryCatchRange,
-              "Could not assign " + guard.getTypeName() + " to java.lang.Throwable",
-              appView);
-        }
-        Deque<PreciseFrameType> sourceStack =
-            ImmutableDeque.of(FrameType.initializedNonNullReference(guard));
-        AssignabilityResult assignabilityResult =
-            assignability.isStackAssignable(sourceStack, destinationFrame.getStack());
-        if (assignabilityResult.isFailed()) {
-          return CfCodeStackMapValidatingException.invalidTryCatchRange(
-              method, tryCatchRange, assignabilityResult.asFailed().getMessage(), appView);
-        }
-      }
-    }
-    return null;
-  }
-
-  public CfFrameState checkExceptionEdges(CfFrameState state) {
-    for (CfTryCatch currentCatchRange : currentCatchRanges) {
-      for (CfLabel target : currentCatchRange.getTargets()) {
-        CfFrame destinationFrame = stateMap.get(target);
-        if (destinationFrame == null) {
-          return CfFrameState.error("No frame for target catch range target");
-        }
-        state = state.checkLocals(this, destinationFrame);
-      }
-    }
-    return state;
-  }
-
-  public CfFrameState checkTarget(CfFrameState state, CfLabel label) {
-    CfFrame destinationFrame = getDestinationFrame(label);
-    return destinationFrame != null
-        ? state.checkLocals(this, destinationFrame).checkStack(this, destinationFrame)
-        : CfFrameState.error("No destination frame");
-  }
-
-  private CfFrame getDestinationFrame(CfLabel label) {
-    return stateMap.get(label);
-  }
-
-  public TraversalContinuation<CfCodeDiagnostics, CfFrameState> computeStateForNextInstruction(
-      CfInstruction instruction, int instructionIndex, CfFrameState state) {
-    if (!instruction.isJump()) {
-      return TraversalContinuation.doContinue(state);
-    }
-    if (instructionIndex == code.getInstructions().size() - 1) {
-      return TraversalContinuation.doContinue(CfFrameState.bottom());
-    }
-    if (instructionIndex == code.getInstructions().size() - 2
-        && code.getInstructions().get(instructionIndex + 1).isLabel()) {
-      return TraversalContinuation.doContinue(CfFrameState.bottom());
-    }
-    if (instruction.asJump().hasFallthrough()) {
-      return TraversalContinuation.doContinue(state);
-    }
-    int nextInstructionIndex = instructionIndex + 1;
-    CfInstruction nextInstruction = code.getInstructions().get(nextInstructionIndex);
-    CfFrame nextFrame = null;
-    if (nextInstruction.isFrame()) {
-      nextFrame = nextInstruction.asFrame();
-    } else if (nextInstruction.isLabel()) {
-      nextFrame = getDestinationFrame(nextInstruction.asLabel());
-    }
-    if (nextFrame != null) {
-      CfFrame currentFrameCopy = nextFrame.mutableCopy();
-      return TraversalContinuation.doContinue(
-          new ConcreteCfFrameState(
-              currentFrameCopy.getMutableLocals(),
-              currentFrameCopy.getMutableStack(),
-              currentFrameCopy.computeStackSize()));
-    }
-    return TraversalContinuation.doBreak(
-        CfCodeStackMapValidatingException.invalidStackMapForInstruction(
-            method, nextInstructionIndex, nextInstruction, "Expected frame instruction", appView));
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifier.java b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifier.java
new file mode 100644
index 0000000..db01f47
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifier.java
@@ -0,0 +1,446 @@
+// Copyright (c) 2020, 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.cf.code;
+
+import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.cf.code.CfAssignability.AssignabilityResult;
+import com.android.tools.r8.cf.code.frame.FrameType;
+import com.android.tools.r8.cf.code.frame.PreciseFrameType;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.CfCodeDiagnostics;
+import com.android.tools.r8.graph.CfCodeStackMapValidatingException;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.optimize.interfaces.analysis.CfAnalysisConfig;
+import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
+import com.android.tools.r8.optimize.interfaces.analysis.ConcreteCfFrameState;
+import com.android.tools.r8.utils.TraversalContinuation;
+import com.android.tools.r8.utils.collections.ImmutableDeque;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+public class CfFrameVerifier {
+
+  private final AppView<?> appView;
+  private final CfCode code;
+  private final CfAnalysisConfig config;
+  private final CfFrameVerifierEventConsumer eventConsumer;
+  private final DexItemFactory factory;
+  private final ProgramMethod method;
+  private final Optional<DexMethod> previousMethod;
+  private final boolean previousMethodIsInstance;
+
+  private final Deque<CfTryCatch> activeCatchHandlers = new ArrayDeque<>();
+  private final Set<CfLabel> tryCatchRangeLabels;
+
+  public CfFrameVerifier(
+      AppView<?> appView,
+      CfCode code,
+      CfAnalysisConfig config,
+      CfFrameVerifierEventConsumer eventConsumer,
+      ProgramMethod method,
+      Optional<DexMethod> previousMethod,
+      boolean previousMethodIsInstance) {
+    this.appView = appView;
+    this.code = code;
+    this.config = config;
+    this.eventConsumer = eventConsumer;
+    this.factory = appView.dexItemFactory();
+    this.method = method;
+    this.previousMethod = previousMethod;
+    this.previousMethodIsInstance = previousMethodIsInstance;
+    this.tryCatchRangeLabels = code.getTryCatchRangeLabels();
+  }
+
+  public static Builder builder(AppView<?> appView, CfCode code, ProgramMethod method) {
+    return new Builder(appView, code, method);
+  }
+
+  public StackMapStatus run() {
+    if (!appView.options().canUseInputStackMaps()
+        || appView.options().testing.disableStackMapVerification) {
+      return StackMapStatus.NOT_PRESENT;
+    }
+
+    DexEncodedMethod definition = method.getDefinition();
+    if (definition.hasClassFileVersion()
+        && definition.getClassFileVersion().isLessThan(CfVersion.V1_7)) {
+      return StackMapStatus.NOT_PRESENT;
+    }
+
+    // Build a map from labels to frames.
+    TraversalContinuation<CfCodeDiagnostics, Map<CfLabel, CfFrame>> labelToFrameMapOrError =
+        buildLabelToFrameMap();
+    if (labelToFrameMapOrError.shouldBreak()) {
+      return fail(labelToFrameMapOrError);
+    }
+    Map<CfLabel, CfFrame> labelToFrameMap = labelToFrameMapOrError.asContinue().getValue();
+
+    // Check try catch ranges.
+    CfCodeDiagnostics diagnostics = checkTryCatchRanges(labelToFrameMap);
+    if (diagnostics != null) {
+      return fail(diagnostics);
+    }
+
+    // Compute initial state.
+    TraversalContinuation<CfCodeDiagnostics, CfFrameState> initialState = computeInitialState();
+    if (initialState.shouldBreak()) {
+      return fail(initialState);
+    }
+
+    // Linear scan over instructions.
+    CfFrameState state = initialState.asContinue().getValue();
+    for (int i = 0; i < code.getInstructions().size(); i++) {
+      CfInstruction instruction = code.getInstruction(i);
+      assert !state.isError();
+      // Check the exceptional edge prior to evaluating the instruction. The local state is stable
+      // at this point as store operations are not throwing and the current stack does not
+      // affect the exceptional transfer (the exception edge is always a singleton stack).
+      if (instruction.canThrow()) {
+        assert !instruction.isStore();
+        state = checkExceptionEdges(state, labelToFrameMap);
+      }
+      if (instruction.isLabel()) {
+        updateActiveCatchHandlers(instruction.asLabel());
+      }
+      state = instruction.evaluate(state, appView, config);
+      if (instruction.isJumpWithNormalTarget()) {
+        CfInstruction fallthroughInstruction =
+            (i + 1) < code.getInstructions().size() ? code.getInstruction(i + 1) : null;
+        TraversalContinuation<CfCodeDiagnostics, CfFrameState> traversalContinuation =
+            instruction.traverseNormalTargets(
+                (target, currentState) -> {
+                  if (target != fallthroughInstruction) {
+                    assert target.isLabel();
+                    currentState = checkTarget(currentState, target.asLabel(), labelToFrameMap);
+                  }
+                  return TraversalContinuation.doContinue(currentState);
+                },
+                fallthroughInstruction,
+                state);
+        state = traversalContinuation.asContinue().getValue();
+      }
+      TraversalContinuation<CfCodeDiagnostics, CfFrameState> traversalContinuation =
+          computeStateForNextInstruction(instruction, i, state, labelToFrameMap);
+      if (traversalContinuation.isContinue()) {
+        state = traversalContinuation.asContinue().getValue();
+      } else {
+        return fail(traversalContinuation);
+      }
+      if (state.isError()) {
+        return fail(
+            CfCodeStackMapValidatingException.invalidStackMapForInstruction(
+                method, i, instruction, state.asError().getMessage(), appView));
+      }
+    }
+    return StackMapStatus.VALID;
+  }
+
+  private TraversalContinuation<CfCodeDiagnostics, Map<CfLabel, CfFrame>> buildLabelToFrameMap() {
+    Map<CfLabel, CfFrame> labelToFrameMap = new IdentityHashMap<>();
+    List<CfLabel> labels = new ArrayList<>();
+    boolean requireStackMapFrame = !code.getTryCatchRanges().isEmpty();
+    for (CfInstruction instruction : code.getInstructions()) {
+      if (instruction.isFrame()) {
+        CfFrame frame = instruction.asFrame();
+        if (!labels.isEmpty()) {
+          for (CfLabel label : labels) {
+            if (labelToFrameMap.containsKey(label)) {
+              return TraversalContinuation.doBreak(
+                  CfCodeStackMapValidatingException.multipleFramesForLabel(method, appView));
+            }
+            labelToFrameMap.put(label, frame);
+          }
+        } else if (instruction != code.getInstruction(0)) {
+          // From b/168212806, it is possible that the first instruction is a frame.
+          return TraversalContinuation.doBreak(
+              CfCodeStackMapValidatingException.unexpectedStackMapFrame(method, appView));
+        }
+      }
+      // We are trying to map a frame to a label, but we can have positions in between, so skip
+      // those.
+      if (instruction.isPosition()) {
+        continue;
+      } else if (instruction.isLabel()) {
+        labels.add(instruction.asLabel());
+      } else {
+        labels.clear();
+      }
+      if (!requireStackMapFrame) {
+        requireStackMapFrame = instruction.isJump() && !isFinalAndExitInstruction(instruction);
+      }
+    }
+    // If there are no frames but we have seen a jump instruction, we cannot verify the stack map.
+    if (requireStackMapFrame && labelToFrameMap.isEmpty()) {
+      return TraversalContinuation.doBreak(
+          CfCodeStackMapValidatingException.noFramesForMethodWithJumps(method, appView));
+    }
+    return TraversalContinuation.doContinue(labelToFrameMap);
+  }
+
+  private StackMapStatus fail(TraversalContinuation<CfCodeDiagnostics, ?> traversalContinuation) {
+    assert traversalContinuation.shouldBreak();
+    return fail(traversalContinuation.asBreak().getValue());
+  }
+
+  private StackMapStatus fail(CfCodeDiagnostics diagnostics) {
+    eventConsumer.acceptError(diagnostics);
+    return StackMapStatus.INVALID;
+  }
+
+  private void updateActiveCatchHandlers(CfLabel label) {
+    if (tryCatchRangeLabels.contains(label)) {
+      for (CfTryCatch tryCatchRange : code.getTryCatchRanges()) {
+        if (tryCatchRange.start == label) {
+          activeCatchHandlers.add(tryCatchRange);
+        }
+      }
+      activeCatchHandlers.removeIf(currentRange -> currentRange.end == label);
+    }
+  }
+
+  private CfCodeDiagnostics checkTryCatchRanges(Map<CfLabel, CfFrame> labelToFrameMap) {
+    for (CfTryCatch tryCatchRange : code.getTryCatchRanges()) {
+      CfCodeDiagnostics diagnostics = checkTryCatchRange(tryCatchRange, labelToFrameMap);
+      if (diagnostics != null) {
+        return diagnostics;
+      }
+    }
+    return null;
+  }
+
+  private CfCodeDiagnostics checkTryCatchRange(
+      CfTryCatch tryCatchRange, Map<CfLabel, CfFrame> labelToFrameMap) {
+    // According to the spec:
+    // https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.10.1
+    // saying ` and the handler's target (the initial instruction of the handler code) is type
+    // safe assuming an incoming type state T. The type state T is derived from ExcStackFrame
+    // by replacing the operand stack with a stack whose sole element is the handler's
+    // exception class.
+    for (CfLabel target : tryCatchRange.getTargets()) {
+      CfFrame destinationFrame = labelToFrameMap.get(target);
+      if (destinationFrame == null) {
+        return CfCodeStackMapValidatingException.invalidTryCatchRange(
+            method, tryCatchRange, "No frame for target catch range target", appView);
+      }
+      // From the spec: the handler's exception class is assignable to the class Throwable.
+      for (DexType guard : tryCatchRange.guards) {
+        if (!config.getAssignability().isAssignable(guard, factory.throwableType)) {
+          return CfCodeStackMapValidatingException.invalidTryCatchRange(
+              method,
+              tryCatchRange,
+              "Could not assign " + guard.getTypeName() + " to java.lang.Throwable",
+              appView);
+        }
+        Deque<PreciseFrameType> sourceStack =
+            ImmutableDeque.of(FrameType.initializedNonNullReference(guard));
+        AssignabilityResult assignabilityResult =
+            config.getAssignability().isStackAssignable(sourceStack, destinationFrame.getStack());
+        if (assignabilityResult.isFailed()) {
+          return CfCodeStackMapValidatingException.invalidTryCatchRange(
+              method, tryCatchRange, assignabilityResult.asFailed().getMessage(), appView);
+        }
+      }
+    }
+    return null;
+  }
+
+  private CfFrameState checkExceptionEdges(
+      CfFrameState state, Map<CfLabel, CfFrame> labelToFrameMap) {
+    for (CfTryCatch currentCatchRange : activeCatchHandlers) {
+      for (CfLabel target : currentCatchRange.getTargets()) {
+        CfFrame destinationFrame = labelToFrameMap.get(target);
+        if (destinationFrame == null) {
+          return CfFrameState.error("No frame for target catch range target");
+        }
+        state = state.checkLocals(config, destinationFrame);
+      }
+    }
+    return state;
+  }
+
+  private CfFrameState checkTarget(
+      CfFrameState state, CfLabel label, Map<CfLabel, CfFrame> labelToFrameMap) {
+    CfFrame destinationFrame = labelToFrameMap.get(label);
+    return destinationFrame != null
+        ? state.checkLocals(config, destinationFrame).checkStack(config, destinationFrame)
+        : CfFrameState.error("No destination frame");
+  }
+
+  private TraversalContinuation<CfCodeDiagnostics, CfFrameState> computeInitialState() {
+    DexItemFactory dexItemFactory = appView.dexItemFactory();
+    CfFrameState state = new ConcreteCfFrameState();
+    int localIndex = 0;
+    DexMethod context = previousMethod.orElse(method.getReference());
+    if (method.getDefinition().isInstance() || previousMethodIsInstance) {
+      state =
+          state.storeLocal(
+              localIndex,
+              context.isInstanceInitializer(dexItemFactory)
+                      || context.mustBeInlinedIntoInstanceInitializer(appView)
+                      || context.isHorizontallyMergedInstanceInitializer(dexItemFactory)
+                  ? FrameType.uninitializedThis()
+                  : FrameType.initializedNonNullReference(context.getHolderType()),
+              config);
+      localIndex++;
+    }
+    for (DexType parameter : context.getParameters()) {
+      state = state.storeLocal(localIndex, FrameType.initialized(parameter), config);
+      localIndex += parameter.getRequiredRegisters();
+    }
+    if (state.isError()) {
+      return TraversalContinuation.doBreak(
+          CfCodeStackMapValidatingException.invalidStackMapForInstruction(
+              method, 0, code.getInstruction(0), state.asError().getMessage(), appView));
+    }
+    return TraversalContinuation.doContinue(state);
+  }
+
+  private TraversalContinuation<CfCodeDiagnostics, CfFrameState> computeStateForNextInstruction(
+      CfInstruction instruction,
+      int instructionIndex,
+      CfFrameState state,
+      Map<CfLabel, CfFrame> labelToFrameMap) {
+    if (!instruction.isJump()) {
+      return TraversalContinuation.doContinue(state);
+    }
+    if (instructionIndex == code.getInstructions().size() - 1) {
+      return TraversalContinuation.doContinue(CfFrameState.bottom());
+    }
+    if (instructionIndex == code.getInstructions().size() - 2
+        && code.getInstruction(instructionIndex + 1).isLabel()) {
+      return TraversalContinuation.doContinue(CfFrameState.bottom());
+    }
+    if (instruction.asJump().hasFallthrough()) {
+      return TraversalContinuation.doContinue(state);
+    }
+    int nextInstructionIndex = instructionIndex + 1;
+    CfInstruction nextInstruction = code.getInstruction(nextInstructionIndex);
+    CfFrame nextFrame = null;
+    if (nextInstruction.isFrame()) {
+      nextFrame = nextInstruction.asFrame();
+    } else if (nextInstruction.isLabel()) {
+      nextFrame = labelToFrameMap.get(nextInstruction.asLabel());
+    }
+    if (nextFrame != null) {
+      CfFrame currentFrameCopy = nextFrame.mutableCopy();
+      return TraversalContinuation.doContinue(
+          new ConcreteCfFrameState(
+              currentFrameCopy.getMutableLocals(),
+              currentFrameCopy.getMutableStack(),
+              currentFrameCopy.computeStackSize()));
+    }
+    return TraversalContinuation.doBreak(
+        CfCodeStackMapValidatingException.invalidStackMapForInstruction(
+            method, nextInstructionIndex, nextInstruction, "Expected frame instruction", appView));
+  }
+
+  private boolean isFinalAndExitInstruction(CfInstruction instruction) {
+    boolean isReturnOrThrow = instruction.isThrow() || instruction.isReturn();
+    if (!isReturnOrThrow) {
+      return false;
+    }
+    for (int i = code.getInstructions().size() - 1; i >= 0; i--) {
+      CfInstruction instr = code.getInstruction(i);
+      if (instr == instruction) {
+        return true;
+      }
+      if (instr.isPosition() || instr.isLabel()) {
+        continue;
+      }
+      return false;
+    }
+    throw new Unreachable("Instruction " + instruction + " should be in instructions");
+  }
+
+  public enum StackMapStatus {
+    NOT_VERIFIED,
+    NOT_PRESENT,
+    INVALID,
+    VALID;
+
+    public boolean isValidOrNotPresent() {
+      return this == VALID || this == NOT_PRESENT;
+    }
+
+    public boolean isInvalidOrNotPresent() {
+      return this == INVALID || this == NOT_PRESENT;
+    }
+  }
+
+  public static class Builder {
+
+    private final AppView<?> appView;
+    private final CfCode code;
+    private final ProgramMethod method;
+
+    private CfAnalysisConfig config;
+    private CfFrameVerifierEventConsumer eventConsumer;
+    private Optional<DexMethod> previousMethod = Optional.empty();
+    private boolean previousMethodIsInstance;
+
+    Builder(AppView<?> appView, CfCode code, ProgramMethod method) {
+      this.appView = appView;
+      this.code = code;
+      this.method = method;
+    }
+
+    public Builder setCodeLens(GraphLens codeLens) {
+      if (codeLens != appView.graphLens()) {
+        this.previousMethod =
+            Optional.of(
+                appView.graphLens().getOriginalMethodSignature(method.getReference(), codeLens));
+        this.previousMethodIsInstance =
+            method.getDefinition().isInstance()
+                || appView
+                    .graphLens()
+                    .lookupPrototypeChangesForMethodDefinition(method.getReference(), codeLens)
+                    .getArgumentInfoCollection()
+                    .isConvertedToStaticMethod();
+      }
+      return this;
+    }
+
+    public Builder setConfig(CfAnalysisConfig config) {
+      this.config = config;
+      return this;
+    }
+
+    public Builder setEventConsumer(CfFrameVerifierEventConsumer eventConsumer) {
+      this.eventConsumer = eventConsumer;
+      return this;
+    }
+
+    public CfFrameVerifier build() {
+      assert eventConsumer != null;
+      return new CfFrameVerifier(
+          appView,
+          code,
+          buildConfig(),
+          eventConsumer,
+          method,
+          previousMethod,
+          previousMethodIsInstance);
+    }
+
+    private CfAnalysisConfig buildConfig() {
+      return config != null
+          ? config
+          : new CfFrameVerifierDefaultAnalysisConfig(appView, code, method, previousMethod);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifierDefaultAnalysisConfig.java b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifierDefaultAnalysisConfig.java
new file mode 100644
index 0000000..82ea7ef
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifierDefaultAnalysisConfig.java
@@ -0,0 +1,68 @@
+// 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.cf.code;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.optimize.interfaces.analysis.CfAnalysisConfig;
+import java.util.Optional;
+
+public class CfFrameVerifierDefaultAnalysisConfig implements CfAnalysisConfig {
+
+  private final CfAssignability assignability;
+  private final CfCode code;
+  private final ProgramMethod method;
+  private final Optional<DexMethod> previousMethod;
+
+  CfFrameVerifierDefaultAnalysisConfig(
+      AppView<?> appView, CfCode code, ProgramMethod method, Optional<DexMethod> previousMethod) {
+    this.assignability = new CfAssignability(appView);
+    this.code = code;
+    this.method = method;
+    this.previousMethod = previousMethod;
+  }
+
+  @Override
+  public CfAssignability getAssignability() {
+    return assignability;
+  }
+
+  @Override
+  public DexMethod getCurrentContext() {
+    return previousMethod.orElse(method.getReference());
+  }
+
+  @Override
+  public int getMaxLocals() {
+    return code.getMaxLocals();
+  }
+
+  @Override
+  public int getMaxStack() {
+    return code.getMaxStack();
+  }
+
+  @Override
+  public boolean isImmediateSuperClassOfCurrentContext(DexType type) {
+    // If the code is rewritten according to the graph lens, we perform a strict check that the
+    // given type is the same as the current holder's super class.
+    if (!previousMethod.isPresent()) {
+      return type == method.getHolder().getSuperType();
+    }
+    // Otherwise, we don't know what the super class of the current class was at the point of the
+    // code lens. We return true, which has the consequence that we may accept a constructor call
+    // for an uninitialized-this value where the constructor is not defined in the immediate parent
+    // class.
+    return true;
+  }
+
+  @Override
+  public boolean isStrengthenFramesEnabled() {
+    return false;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifierEventConsumer.java b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifierEventConsumer.java
new file mode 100644
index 0000000..a149a5f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifierEventConsumer.java
@@ -0,0 +1,12 @@
+// 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.cf.code;
+
+import com.android.tools.r8.graph.CfCodeDiagnostics;
+
+public interface CfFrameVerifierEventConsumer {
+
+  void acceptError(CfCodeDiagnostics diagnostics);
+}
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index ec420af..0d4f9c9 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -9,7 +9,9 @@
 import com.android.tools.r8.cf.CfPrinter;
 import com.android.tools.r8.cf.CfVersion;
 import com.android.tools.r8.cf.code.CfFrame;
-import com.android.tools.r8.cf.code.CfFrameVerificationHelper;
+import com.android.tools.r8.cf.code.CfFrameVerifier;
+import com.android.tools.r8.cf.code.CfFrameVerifier.StackMapStatus;
+import com.android.tools.r8.cf.code.CfFrameVerifierEventConsumer;
 import com.android.tools.r8.cf.code.CfIinc;
 import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.cf.code.CfLabel;
@@ -17,7 +19,6 @@
 import com.android.tools.r8.cf.code.CfPosition;
 import com.android.tools.r8.cf.code.CfReturnVoid;
 import com.android.tools.r8.cf.code.CfTryCatch;
-import com.android.tools.r8.cf.code.frame.FrameType;
 import com.android.tools.r8.dex.code.CfOrDexInstruction;
 import com.android.tools.r8.dex.code.DexBase5Format;
 import com.android.tools.r8.errors.InvalidDebugInfoException;
@@ -39,47 +40,29 @@
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.optimize.interfaces.analysis.CfFrameState;
-import com.android.tools.r8.optimize.interfaces.analysis.ConcreteCfFrameState;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.TraversalContinuation;
 import com.android.tools.r8.utils.structural.CompareToVisitor;
 import com.android.tools.r8.utils.structural.HashingVisitor;
 import com.android.tools.r8.utils.structural.StructuralItem;
 import com.android.tools.r8.utils.structural.StructuralMapping;
 import com.google.common.base.Strings;
+import com.google.common.collect.Sets;
 import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
 import java.util.ArrayList;
 import java.util.BitSet;
 import java.util.Collections;
-import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.ListIterator;
-import java.util.Map;
+import java.util.Set;
 import org.objectweb.asm.Label;
 import org.objectweb.asm.MethodVisitor;
 
 public class CfCode extends Code implements CfWritableCode, StructuralItem<CfCode> {
 
-  public enum StackMapStatus {
-    NOT_VERIFIED,
-    NOT_PRESENT,
-    INVALID,
-    VALID;
-
-    public boolean isValid() {
-      return this == VALID || this == NOT_PRESENT;
-    }
-
-    public boolean isInvalidOrNotPresent() {
-      return this == INVALID || this == NOT_PRESENT;
-    }
-  }
-
   public static class LocalVariableInfo {
 
     private final int index;
@@ -149,7 +132,7 @@
   private List<CfInstruction> instructions;
   private final List<CfTryCatch> tryCatchRanges;
   private final List<LocalVariableInfo> localVariables;
-  private StackMapStatus stackMapStatus = StackMapStatus.NOT_VERIFIED;
+  private StackMapStatus stackMapStatus = CfFrameVerifier.StackMapStatus.NOT_VERIFIED;
   private final com.android.tools.r8.position.Position diagnosticPosition;
   private final BytecodeMetadata<CfInstruction> metadata;
 
@@ -261,7 +244,7 @@
   }
 
   public StackMapStatus getStackMapStatus() {
-    assert stackMapStatus != StackMapStatus.NOT_VERIFIED;
+    assert stackMapStatus != CfFrameVerifier.StackMapStatus.NOT_VERIFIED;
     return stackMapStatus;
   }
 
@@ -281,6 +264,15 @@
     return tryCatchRanges;
   }
 
+  public Set<CfLabel> getTryCatchRangeLabels() {
+    Set<CfLabel> tryCatchRangeLabels = Sets.newIdentityHashSet();
+    for (CfTryCatch tryCatchRange : getTryCatchRanges()) {
+      tryCatchRangeLabels.add(tryCatchRange.start);
+      tryCatchRangeLabels.add(tryCatchRange.end);
+    }
+    return tryCatchRangeLabels;
+  }
+
   public CfInstruction getInstruction(int index) {
     return instructions.get(index);
   }
@@ -415,7 +407,8 @@
       LensCodeRewriterUtils rewriter,
       MethodVisitor visitor) {
     GraphLens graphLens = appView.graphLens();
-    assert verifyFrames(method, appView).isValid() : "Could not validate stack map frames";
+    assert verifyFrames(method, appView).isValidOrNotPresent()
+        : "Could not validate stack map frames";
     DexItemFactory dexItemFactory = appView.dexItemFactory();
     InitClassLens initClassLens = appView.initClassLens();
     InternalOptions options = appView.options();
@@ -553,7 +546,7 @@
 
   private void verifyFramesOrRemove(ProgramMethod method, AppView<?> appView, GraphLens codeLens) {
     stackMapStatus = verifyFrames(method, appView, codeLens);
-    if (!stackMapStatus.isValid()) {
+    if (!stackMapStatus.isValidOrNotPresent()) {
       ArrayList<CfInstruction> copy = new ArrayList<>(instructions);
       copy.removeIf(CfInstruction::isFrame);
       setInstructions(copy);
@@ -896,184 +889,23 @@
   }
 
   public StackMapStatus verifyFrames(ProgramMethod method, AppView<?> appView, GraphLens codeLens) {
-    GraphLens graphLens = appView.graphLens();
-    DexEncodedMethod definition = method.getDefinition();
-    if (!appView.options().canUseInputStackMaps()
-        || appView.options().testing.disableStackMapVerification) {
-      return StackMapStatus.NOT_PRESENT;
-    }
-    if (definition.hasClassFileVersion()
-        && definition.getClassFileVersion().isLessThan(CfVersion.V1_7)) {
-      return StackMapStatus.NOT_PRESENT;
-    }
+    CfFrameVerifierEventConsumer eventConsumer =
+        new CfFrameVerifierEventConsumer() {
 
-    RewrittenPrototypeDescription protoChanges =
-        graphLens.lookupPrototypeChangesForMethodDefinition(method.getReference(), codeLens);
-
-    DexMethod previousMethodSignature =
-        graphLens.getOriginalMethodSignature(method.getReference(), codeLens);
-    boolean previousMethodSignatureIsInstance =
-        method.getDefinition().isInstance()
-            || protoChanges.getArgumentInfoCollection().isConvertedToStaticMethod();
-
-    // Build a map from labels to frames.
-    Map<CfLabel, CfFrame> stateMap = new IdentityHashMap<>();
-    List<CfLabel> labels = new ArrayList<>();
-    boolean requireStackMapFrame = !tryCatchRanges.isEmpty();
-    for (CfInstruction instruction : instructions) {
-      if (instruction.isFrame()) {
-        CfFrame frame = instruction.asFrame();
-        if (!labels.isEmpty()) {
-          for (CfLabel label : labels) {
-            if (stateMap.containsKey(label)) {
-              return reportStackMapError(
-                  CfCodeStackMapValidatingException.multipleFramesForLabel(method, appView),
-                  appView);
-            }
-            stateMap.put(label, frame);
+          @Override
+          public void acceptError(CfCodeDiagnostics diagnostics) {
+            // Stack maps was required from version V1_6 (50), but the JVM gave a grace-period and
+            // only started enforcing stack maps from 51 in JVM 8. As a consequence, we have
+            // different android libraries that has V1_7 code but has no stack maps. To not fail on
+            // compilations we only report a warning.
+            appView.options().reporter.warning(diagnostics);
           }
-        } else if (instruction != instructions.get(0)) {
-          // From b/168212806, it is possible that the first instruction is a frame.
-          return reportStackMapError(
-              CfCodeStackMapValidatingException.unexpectedStackMapFrame(method, appView), appView);
-        }
-      }
-      // We are trying to map a frame to a label, but we can have positions in between, so skip
-      // those.
-      if (instruction.isPosition()) {
-        continue;
-      } else if (instruction.isLabel()) {
-        labels.add(instruction.asLabel());
-      } else {
-        labels.clear();
-      }
-      if (!requireStackMapFrame) {
-        requireStackMapFrame = instruction.isJump() && !finalAndExitInstruction(instruction);
-      }
-    }
-    // If there are no frames but we have seen a jump instruction, we cannot verify the stack map.
-    if (requireStackMapFrame && stateMap.isEmpty()) {
-      return reportStackMapError(
-          CfCodeStackMapValidatingException.noFramesForMethodWithJumps(method, appView), appView);
-    }
-    CfFrameVerificationHelper helper =
-        new CfFrameVerificationHelper(appView, this, codeLens, method, stateMap, tryCatchRanges);
-    CfCodeDiagnostics diagnostics = helper.checkTryCatchRanges();
-    if (diagnostics != null) {
-      return reportStackMapError(diagnostics, appView);
-    }
-    TraversalContinuation<CfCodeDiagnostics, CfFrameState> initialState =
-        computeInitialState(
-            appView, helper, method, previousMethodSignature, previousMethodSignatureIsInstance);
-    if (initialState.shouldBreak()) {
-      return reportStackMapError(initialState.asBreak().getValue(), appView);
-    }
-    CfFrameState state = initialState.asContinue().getValue();
-    for (int i = 0; i < instructions.size(); i++) {
-      CfInstruction instruction = instructions.get(i);
-      assert !state.isError();
-      // Check the exceptional edge prior to evaluating the instruction. The local state is stable
-      // at this point as store operations are not throwing and the current stack does not
-      // affect the exceptional transfer (the exception edge is always a singleton stack).
-      if (instruction.canThrow()) {
-        assert !instruction.isStore();
-        state = helper.checkExceptionEdges(state);
-      }
-      if (instruction.isLabel()) {
-        helper.seenLabel(instruction.asLabel());
-      }
-      state = instruction.evaluate(state, appView, helper);
-      if (instruction.isJumpWithNormalTarget()) {
-        CfInstruction fallthroughInstruction =
-            (i + 1) < instructions.size() ? instructions.get(i + 1) : null;
-        TraversalContinuation<CfCodeDiagnostics, CfFrameState> traversalContinuation =
-            instruction.traverseNormalTargets(
-                (target, currentState) -> {
-                  if (target != fallthroughInstruction) {
-                    assert target.isLabel();
-                    currentState = helper.checkTarget(currentState, target.asLabel());
-                  }
-                  return TraversalContinuation.doContinue(currentState);
-                },
-                fallthroughInstruction,
-                state);
-        state = traversalContinuation.asContinue().getValue();
-      }
-      TraversalContinuation<CfCodeDiagnostics, CfFrameState> traversalContinuation =
-          helper.computeStateForNextInstruction(instruction, i, state);
-      if (traversalContinuation.isContinue()) {
-        state = traversalContinuation.asContinue().getValue();
-      } else {
-        return reportStackMapError(traversalContinuation.asBreak().getValue(), appView);
-      }
-      if (state.isError()) {
-        return reportStackMapError(
-            CfCodeStackMapValidatingException.invalidStackMapForInstruction(
-                method, i, instruction, state.asError().getMessage(), appView),
-            appView);
-      }
-    }
-    return StackMapStatus.VALID;
-  }
-
-  private StackMapStatus reportStackMapError(CfCodeDiagnostics diagnostics, AppView<?> appView) {
-    // Stack maps was required from version V1_6 (50), but the JVM gave a grace-period and only
-    // started enforcing stack maps from 51 in JVM 8. As a consequence, we have different android
-    // libraries that has V1_7 code but has no stack maps. To not fail on compilations we only
-    // report a warning.
-    appView.options().reporter.warning(diagnostics);
-    return StackMapStatus.INVALID;
-  }
-
-  private boolean finalAndExitInstruction(CfInstruction instruction) {
-    boolean isReturnOrThrow = instruction.isThrow() || instruction.isReturn();
-    if (!isReturnOrThrow) {
-      return false;
-    }
-    for (int i = instructions.size() - 1; i >= 0; i--) {
-      CfInstruction instr = instructions.get(i);
-      if (instr == instruction) {
-        return true;
-      }
-      if (instr.isPosition() || instr.isLabel()) {
-        continue;
-      }
-      return false;
-    }
-    throw new Unreachable("Instruction " + instruction + " should be in instructions");
-  }
-
-  private TraversalContinuation<CfCodeDiagnostics, CfFrameState> computeInitialState(
-      AppView<?> appView,
-      CfFrameVerificationHelper helper,
-      ProgramMethod method,
-      DexMethod previousMethodSignature,
-      boolean previousMethodSignatureIsInstance) {
-    DexItemFactory dexItemFactory = appView.dexItemFactory();
-    CfFrameState state = new ConcreteCfFrameState();
-    int localIndex = 0;
-    if (previousMethodSignatureIsInstance) {
-      state =
-          state.storeLocal(
-              localIndex,
-              previousMethodSignature.isInstanceInitializer(dexItemFactory)
-                      || previousMethodSignature.mustBeInlinedIntoInstanceInitializer(appView)
-                      || previousMethodSignature.isHorizontallyMergedInstanceInitializer(
-                          dexItemFactory)
-                  ? FrameType.uninitializedThis()
-                  : FrameType.initializedNonNullReference(previousMethodSignature.getHolderType()),
-              helper);
-      localIndex++;
-    }
-    for (DexType parameter : previousMethodSignature.getParameters()) {
-      state = state.storeLocal(localIndex, FrameType.initialized(parameter), helper);
-      localIndex += parameter.getRequiredRegisters();
-    }
-    if (state.isError()) {
-      return TraversalContinuation.doBreak(
-          CfCodeStackMapValidatingException.invalidStackMapForInstruction(
-              method, 0, instructions.get(0), state.asError().getMessage(), appView));
-    }
-    return TraversalContinuation.doContinue(state);
+        };
+    CfFrameVerifier helper =
+        CfFrameVerifier.builder(appView, this, method)
+            .setCodeLens(codeLens)
+            .setEventConsumer(eventConsumer)
+            .build();
+    return helper.run();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index e6228f7..cb3bf73 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -193,7 +193,7 @@
     DexBuilder.removeRedundantDebugPositions(code);
     CfCode code = buildCfCode();
     assert verifyInvokeInterface(code, appView);
-    assert code.verifyFrames(method, appView, appView.graphLens()).isValid();
+    assert code.verifyFrames(method, appView, appView.graphLens()).isValidOrNotPresent();
     return code;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
index 8a18a09..bffd1fe 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
@@ -6,6 +6,7 @@
 import static it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMaps.emptyMap;
 
 import com.android.tools.r8.cf.code.CfFrame;
+import com.android.tools.r8.cf.code.CfFrameVerifier;
 import com.android.tools.r8.cf.code.CfGoto;
 import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.cf.code.CfLabel;
@@ -21,7 +22,6 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.CfCode.LocalVariableInfo;
-import com.android.tools.r8.graph.CfCode.StackMapStatus;
 import com.android.tools.r8.graph.CfCodeDiagnostics;
 import com.android.tools.r8.graph.DebugLocalInfo;
 import com.android.tools.r8.graph.DebugLocalInfo.PrintLevel;
@@ -693,7 +693,7 @@
   @Override
   public DexType getPhiTypeForBlock(
       int register, int blockOffset, ValueTypeConstraint constraint, RegisterReadType readType) {
-    assert code.getStackMapStatus() != StackMapStatus.NOT_VERIFIED;
+    assert code.getStackMapStatus() != CfFrameVerifier.StackMapStatus.NOT_VERIFIED;
     if (code.getStackMapStatus().isInvalidOrNotPresent()) {
       return null;
     }
@@ -881,7 +881,7 @@
 
   @Override
   public boolean hasValidTypesFromStackMap() {
-    return code.getStackMapStatus() == StackMapStatus.VALID;
+    return code.getStackMapStatus() == CfFrameVerifier.StackMapStatus.VALID;
   }
 
   @Override