@Override
  public final @NotNull RunnerResult interpret(@NotNull List<? extends DfaInstructionState> startingStates) {
    int endOffset = myFlow.getInstructionCount();
    DfaInstructionState lastInstructionState = null;
    myNestedClosures.clear();
    myWasForciblyMerged = false;
    myValueFactory.setContext(myPsiAnchor);

    final StateQueue queue = new StateQueue();
    for (DfaInstructionState state : startingStates) {
      queue.offer(state);
    }

    MultiMap<Instruction, DfaMemoryState> processedStates = MultiMap.createSet();
    MultiMap<Instruction, DfaMemoryState> incomingStates = MultiMap.createSet();
    try {
      Set<Instruction> joinInstructions = getJoinInstructions();
      int[] loopNumber = myFlow.getLoopNumbers();

      int stateLimit = Registry.intValue("ide.dfa.state.limit");
      int count = 0;
      while (!queue.isEmpty()) {
        List<DfaInstructionState> states = queue.getNextInstructionStates(joinInstructions);
        if (states.size() > getComplexityLimit()) {
          LOG.trace("Too complex because too many different possible states");
          return RunnerResult.TOO_COMPLEX;
        }
        assert !states.isEmpty();
        Instruction instruction = states.get(0).getInstruction();
        beforeInstruction(instruction);
        for (DfaInstructionState instructionState : states) {
          lastInstructionState = instructionState;
          if (count++ > stateLimit) {
            LOG.trace("Too complex data flow: too many instruction states processed");
            return RunnerResult.TOO_COMPLEX;
          }
          ProgressManager.checkCanceled();

          if (LOG.isTraceEnabled()) {
            LOG.trace(instructionState.toString());
          }

          if (!instruction.isLinear()) {
            Collection<DfaMemoryState> processed = processedStates.get(instruction);
            if (containsState(processed, instructionState)) {
              continue;
            }
            if (processed.size() > getComplexityLimit() / 6) {
              instructionState = mergeBackBranches(instructionState, processed);
              if (containsState(processed, instructionState)) {
                continue;
              }
            }
            if (processed.size() > getComplexityLimit()) {
              LOG.trace("Too complex because too many different possible states");
              return RunnerResult.TOO_COMPLEX;
            }
            if (loopNumber[instruction.getIndex()] != 0) {
              processedStates.putValue(instruction, instructionState.getMemoryState().createCopy());
            }
          }

          DfaInstructionState[] after = acceptInstruction(instructionState);
          if (LOG.isDebugEnabled() && instruction instanceof ControlTransferInstruction && after.length == 0) {
            DfaMemoryState memoryState = instructionState.getMemoryState();
            if (!memoryState.isEmptyStack()) {
              // can pop safely as this memory state is unnecessary anymore (after is empty)
              DfaValue topValue = memoryState.pop();
              if (!(topValue instanceof DfaControlTransferValue || myPsiAnchor instanceof PsiCodeFragment && memoryState.isEmptyStack())) {
                // push back so error report includes this entry
                memoryState.push(topValue);
                reportDfaProblem(instructionState, new RuntimeException("Stack is corrupted"));
              }
            }
          }
          for (DfaInstructionState state : after) {
            Instruction nextInstruction = state.getInstruction();
            if (nextInstruction.getIndex() >= endOffset) {
              continue;
            }
            handleStepOutOfLoop(instruction, nextInstruction, loopNumber, processedStates, incomingStates, states, after, queue);
            if (!nextInstruction.isLinear()) {
              if (containsState(processedStates.get(nextInstruction), state) ||
                  containsState(incomingStates.get(nextInstruction), state)) {
                continue;
              }
              if (loopNumber[nextInstruction.getIndex()] != 0) {
                incomingStates.putValue(nextInstruction, state.getMemoryState().createCopy());
              }
            }
            if (nextInstruction.getIndex() < instruction.getIndex() &&
                (!(instruction instanceof GotoInstruction) || ((GotoInstruction)instruction).shouldWidenBackBranch())) {
              state.getMemoryState().widen();
            }
            queue.offer(state);
          }
        }
        afterInstruction(instruction);
        if (myCancelled) {
          return RunnerResult.CANCELLED;
        }
      }

      myWasForciblyMerged |= queue.wasForciblyMerged();
      return RunnerResult.OK;
    }
    catch (ProcessCanceledException ex) {
      throw ex;
    }
    catch (RuntimeException | AssertionError e) {
      reportDfaProblem(lastInstructionState, e);
      return RunnerResult.ABORTED;
    }
  }
@Override
  public final @NotNull RunnerResult interpret(@NotNull List<? extends DfaInstructionState> startingStates)