public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
    this.breakLabel = new BranchLabel();
    this.continueLabel = new BranchLabel();
    LoopingFlowContext loopingContext =
        new LoopingFlowContext(
            flowContext, flowInfo, this, this.breakLabel, this.continueLabel, currentScope);

    Constant cst = this.condition.constant;
    boolean isConditionTrue = cst != Constant.NotAConstant && cst.booleanValue() == true;
    cst = this.condition.optimizedBooleanConstant();
    boolean isConditionOptimizedTrue = cst != Constant.NotAConstant && cst.booleanValue() == true;
    boolean isConditionOptimizedFalse = cst != Constant.NotAConstant && cst.booleanValue() == false;

    int previousMode = flowInfo.reachMode();

    UnconditionalFlowInfo actionInfo = flowInfo.nullInfoLessUnconditionalCopy();
    // we need to collect the contribution to nulls of the coming paths through the
    // loop, be they falling through normally or branched to break, continue labels
    // or catch blocks
    if ((this.action != null) && !this.action.isEmptyBlock()) {
      actionInfo =
          this.action.analyseCode(currentScope, loopingContext, actionInfo).unconditionalInits();

      // code generation can be optimized when no need to continue in the loop
      if ((actionInfo.tagBits & loopingContext.initsOnContinue.tagBits & FlowInfo.UNREACHABLE)
          != 0) {
        this.continueLabel = null;
      }
    }
    /* Reset reach mode, to address following scenario.
     *   final blank;
     *   do { if (true) break; else blank = 0; } while(false);
     *   blank = 1; // may be initialized already
     */
    actionInfo.setReachMode(previousMode);

    LoopingFlowContext condLoopContext;
    FlowInfo condInfo =
        this.condition.analyseCode(
            currentScope,
            (condLoopContext =
                new LoopingFlowContext(flowContext, flowInfo, this, null, null, currentScope)),
            (this.action == null
                    ? actionInfo
                    : (actionInfo.mergedWith(loopingContext.initsOnContinue)))
                .copy());
    this.preConditionInitStateIndex =
        currentScope.methodScope().recordInitializationStates(actionInfo);
    if (!isConditionOptimizedFalse && this.continueLabel != null) {
      loopingContext.complainOnDeferredFinalChecks(currentScope, condInfo);
      condLoopContext.complainOnDeferredFinalChecks(currentScope, condInfo);
      loopingContext.complainOnDeferredNullChecks(
          currentScope,
          flowInfo
              .unconditionalCopy()
              .addPotentialNullInfoFrom(condInfo.initsWhenTrue().unconditionalInits()));
      condLoopContext.complainOnDeferredNullChecks(
          currentScope,
          actionInfo.addPotentialNullInfoFrom(condInfo.initsWhenTrue().unconditionalInits()));
    }

    // end of loop
    FlowInfo mergedInfo =
        FlowInfo.mergedOptimizedBranches(
            (loopingContext.initsOnBreak.tagBits & FlowInfo.UNREACHABLE) != 0
                ? loopingContext.initsOnBreak
                : flowInfo.unconditionalCopy().addInitializationsFrom(loopingContext.initsOnBreak),
            // recover upstream null info
            isConditionOptimizedTrue,
            (condInfo.tagBits & FlowInfo.UNREACHABLE) == 0
                ? flowInfo.addInitializationsFrom(condInfo.initsWhenFalse())
                : condInfo,
            // recover null inits from before condition analysis
            false, // never consider opt false case for DO loop, since break can always occur
                   // (47776)
            !isConditionTrue /*do{}while(true); unreachable(); */);
    this.mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo);
    return mergedInfo;
  }