Package org.eclipse.jdt.internal.compiler.ast

Source Code of org.eclipse.jdt.internal.compiler.ast.ForStatement

/*******************************************************************************
* Copyright (c) 2000, 2011 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*     IBM Corporation - initial API and implementation
*     Stephan Herrmann - Contribution for bug 319201 - [null] no warning when unboxing SingleNameReference causes NPE
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;

import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.codegen.*;
import org.eclipse.jdt.internal.compiler.flow.*;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.lookup.*;

public class ForStatement extends Statement {

  public Statement[] initializations;
  public Expression condition;
  public Statement[] increments;
  public Statement action;

  //when there is no local declaration, there is no need of a new scope
  //scope is positioned either to a new scope, or to the "upper"scope (see resolveType)
  public BlockScope scope;

  private BranchLabel breakLabel, continueLabel;

  // for local variables table attributes
  int preCondInitStateIndex = -1;
  int preIncrementsInitStateIndex = -1;
  int condIfTrueInitStateIndex = -1;
  int mergedInitStateIndex = -1;

  public ForStatement(
    Statement[] initializations,
    Expression condition,
    Statement[] increments,
    Statement action,
    boolean neededScope,
    int s,
    int e) {

    this.sourceStart = s;
    this.sourceEnd = e;
    this.initializations = initializations;
    this.condition = condition;
    this.increments = increments;
    this.action = action;
    // remember useful empty statement
    if (action instanceof EmptyStatement) action.bits |= ASTNode.IsUsefulEmptyStatement;
    if (neededScope) {
      this.bits |= ASTNode.NeededScope;
    }
  }

  public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
    this.breakLabel = new BranchLabel();
    this.continueLabel = new BranchLabel();
    int initialComplaintLevel = (flowInfo.reachMode() & FlowInfo.UNREACHABLE) != 0 ? Statement.COMPLAINED_FAKE_REACHABLE : Statement.NOT_COMPLAINED;

    // process the initializations
    if (this.initializations != null) {
      for (int i = 0, count = this.initializations.length; i < count; i++) {
        flowInfo = this.initializations[i].analyseCode(this.scope, flowContext, flowInfo);
      }
    }
    this.preCondInitStateIndex =
      currentScope.methodScope().recordInitializationStates(flowInfo);

    Constant cst = this.condition == null ? null : this.condition.constant;
    boolean isConditionTrue = cst == null || (cst != Constant.NotAConstant && cst.booleanValue() == true);
    boolean isConditionFalse = cst != null && (cst != Constant.NotAConstant && cst.booleanValue() == false);

    cst = this.condition == null ? null : this.condition.optimizedBooleanConstant();
    boolean isConditionOptimizedTrue = cst == null ||  (cst != Constant.NotAConstant && cst.booleanValue() == true);
    boolean isConditionOptimizedFalse = cst != null && (cst != Constant.NotAConstant && cst.booleanValue() == false);
   
    // process the condition
    LoopingFlowContext condLoopContext = null;
    FlowInfo condInfo = flowInfo.nullInfoLessUnconditionalCopy();
    if (this.condition != null) {
      if (!isConditionTrue) {
        condInfo =
          this.condition.analyseCode(
            this.scope,
            (condLoopContext =
              new LoopingFlowContext(flowContext, flowInfo, this, null,
                null, this.scope)),
            condInfo);
        if ((this.condition.implicitConversion & TypeIds.UNBOXING) != 0) {
          this.condition.checkNPE(currentScope, flowContext, flowInfo);
        }
      }
    }

    // process the action
    LoopingFlowContext loopingContext;
    UnconditionalFlowInfo actionInfo;
    if (this.action == null
      || (this.action.isEmptyBlock() && currentScope.compilerOptions().complianceLevel <= ClassFileConstants.JDK1_3)) {
      if (condLoopContext != null)
        condLoopContext.complainOnDeferredFinalChecks(this.scope, condInfo);
      if (isConditionTrue) {
        if (condLoopContext != null) {
          condLoopContext.complainOnDeferredNullChecks(currentScope,
            condInfo);
        }
        return FlowInfo.DEAD_END;
      } else {
        if (isConditionFalse){
          this.continueLabel = null; // for(;false;p());
        }
        actionInfo = condInfo.initsWhenTrue().unconditionalCopy();
        loopingContext =
          new LoopingFlowContext(flowContext, flowInfo, this,
            this.breakLabel, this.continueLabel, this.scope);
      }
    }
    else {
      loopingContext =
        new LoopingFlowContext(flowContext, flowInfo, this, this.breakLabel,
          this.continueLabel, this.scope);
      FlowInfo initsWhenTrue = condInfo.initsWhenTrue();
      this.condIfTrueInitStateIndex =
        currentScope.methodScope().recordInitializationStates(initsWhenTrue);

        if (isConditionFalse) {
          actionInfo = FlowInfo.DEAD_END;
        } else {
          actionInfo = initsWhenTrue.unconditionalCopy();
          if (isConditionOptimizedFalse){
            actionInfo.setReachMode(FlowInfo.UNREACHABLE_OR_DEAD);
          }
        }
      if (this.action.complainIfUnreachable(actionInfo, this.scope, initialComplaintLevel) < Statement.COMPLAINED_UNREACHABLE) {
        actionInfo = this.action.analyseCode(this.scope, loopingContext, actionInfo).unconditionalInits();
      }

      // code generation can be optimized when no need to continue in the loop
      if ((actionInfo.tagBits &
          loopingContext.initsOnContinue.tagBits &
          FlowInfo.UNREACHABLE_OR_DEAD) != 0) {
        this.continueLabel = null;
      }
      else {
        if (condLoopContext != null) {
          condLoopContext.complainOnDeferredFinalChecks(this.scope,
              condInfo);
        }
        actionInfo = actionInfo.mergedWith(loopingContext.initsOnContinue);
        loopingContext.complainOnDeferredFinalChecks(this.scope,
            actionInfo);
      }
    }
    // for increments
    FlowInfo exitBranch = flowInfo.copy();
    // recover null inits from before condition analysis
    LoopingFlowContext incrementContext = null;
    if (this.continueLabel != null) {
      if (this.increments != null) {
        incrementContext =
          new LoopingFlowContext(flowContext, flowInfo, this, null,
            null, this.scope);
        FlowInfo incrementInfo = actionInfo;
        this.preIncrementsInitStateIndex =
          currentScope.methodScope().recordInitializationStates(incrementInfo);
        for (int i = 0, count = this.increments.length; i < count; i++) {
          incrementInfo = this.increments[i].
            analyseCode(this.scope, incrementContext, incrementInfo);
        }
        incrementContext.complainOnDeferredFinalChecks(this.scope,
            actionInfo = incrementInfo.unconditionalInits());
      }
      exitBranch.addPotentialInitializationsFrom(actionInfo).
        addInitializationsFrom(condInfo.initsWhenFalse());
    } else {
      exitBranch.addInitializationsFrom(condInfo.initsWhenFalse());
      if (this.increments != null) {
        if (initialComplaintLevel == Statement.NOT_COMPLAINED) {
          currentScope.problemReporter().fakeReachable(this.increments[0]);
        }
      }
    }
    // nulls checks
    if (condLoopContext != null) {
      condLoopContext.complainOnDeferredNullChecks(currentScope,
        actionInfo);
    }
    loopingContext.complainOnDeferredNullChecks(currentScope,
      actionInfo);
    if (incrementContext != null) {
      incrementContext.complainOnDeferredNullChecks(currentScope,
        actionInfo);
    }
    if (loopingContext.hasEscapingExceptions()) { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=321926
      FlowInfo loopbackFlowInfo = flowInfo.copy();
      if (this.continueLabel != null) {  // we do get to the bottom
        loopbackFlowInfo.mergedWith(actionInfo.unconditionalCopy());
      }
      loopingContext.simulateThrowAfterLoopBack(loopbackFlowInfo);
    }
    //end of loop
    FlowInfo mergedInfo = FlowInfo.mergedOptimizedBranches(
        (loopingContext.initsOnBreak.tagBits &
          FlowInfo.UNREACHABLE) != 0 ?
          loopingContext.initsOnBreak :
          flowInfo.addInitializationsFrom(loopingContext.initsOnBreak), // recover upstream null info
        isConditionOptimizedTrue,
        exitBranch,
        isConditionOptimizedFalse,
        !isConditionTrue /*for(;;){}while(true); unreachable(); */);
    this.mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo);
    return mergedInfo;
  }

  /**
   * For statement code generation
   *
   * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
   * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
   */
  public void generateCode(BlockScope currentScope, CodeStream codeStream) {

    if ((this.bits & IsReachable) == 0) {
      return;
    }
    int pc = codeStream.position;

    // generate the initializations
    if (this.initializations != null) {
      for (int i = 0, max = this.initializations.length; i < max; i++) {
        this.initializations[i].generateCode(this.scope, codeStream);
      }
    }
    Constant cst = this.condition == null ? null : this.condition.optimizedBooleanConstant();
    boolean isConditionOptimizedFalse = cst != null && (cst != Constant.NotAConstant && cst.booleanValue() == false);
    if (isConditionOptimizedFalse) {
      this.condition.generateCode(this.scope, codeStream, false);
      // May loose some local variable initializations : affecting the local variable attributes
      if ((this.bits & ASTNode.NeededScope) != 0) {
        codeStream.exitUserScope(this.scope);
      }
      if (this.mergedInitStateIndex != -1) {
        codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.mergedInitStateIndex);
        codeStream.addDefinitelyAssignedVariables(currentScope, this.mergedInitStateIndex);
      }
      codeStream.recordPositionsFrom(pc, this.sourceStart);
      return;
    }

    // label management
    BranchLabel actionLabel = new BranchLabel(codeStream);
    actionLabel.tagBits |= BranchLabel.USED;
    BranchLabel conditionLabel = new BranchLabel(codeStream);
    this.breakLabel.initialize(codeStream);
    if (this.continueLabel == null) {
      conditionLabel.place();
      if ((this.condition != null) && (this.condition.constant == Constant.NotAConstant)) {
        this.condition.generateOptimizedBoolean(this.scope, codeStream, null, this.breakLabel, true);
      }
    } else {
      this.continueLabel.initialize(codeStream);
      // jump over the actionBlock
      if ((this.condition != null)
        && (this.condition.constant == Constant.NotAConstant)
        && !((this.action == null || this.action.isEmptyBlock()) && (this.increments == null))) {
        conditionLabel.tagBits |= BranchLabel.USED;
        int jumpPC = codeStream.position;
        codeStream.goto_(conditionLabel);
        codeStream.recordPositionsFrom(jumpPC, this.condition.sourceStart);
      }
    }

    // generate the loop action
    if (this.action != null) {
      // Required to fix 1PR0XVS: LFRE:WINNT - Compiler: variable table for method appears incorrect
      if (this.condIfTrueInitStateIndex != -1) {
        // insert all locals initialized inside the condition into the action generated prior to the condition
        codeStream.addDefinitelyAssignedVariables(
          currentScope,
          this.condIfTrueInitStateIndex);
      }
      actionLabel.place();
      this.action.generateCode(this.scope, codeStream);
    } else {
      actionLabel.place();
    }
    if (this.preIncrementsInitStateIndex != -1) {
      codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.preIncrementsInitStateIndex);
      codeStream.addDefinitelyAssignedVariables(currentScope, this.preIncrementsInitStateIndex);
    }
    // continuation point
    if (this.continueLabel != null) {
      this.continueLabel.place();
      // generate the increments for next iteration
      if (this.increments != null) {
        for (int i = 0, max = this.increments.length; i < max; i++) {
          this.increments[i].generateCode(this.scope, codeStream);
        }
      }
      // May loose some local variable initializations : affecting the local variable attributes
      if (this.preCondInitStateIndex != -1) {
        codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.preCondInitStateIndex);
      }
      // generate the condition
      conditionLabel.place();
      if ((this.condition != null) && (this.condition.constant == Constant.NotAConstant)) {
        this.condition.generateOptimizedBoolean(this.scope, codeStream, actionLabel, null, true);
      } else {
        codeStream.goto_(actionLabel);
      }

    } else {
      // May loose some local variable initializations : affecting the local variable attributes
      if (this.preCondInitStateIndex != -1) {
        codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.preCondInitStateIndex);
      }
    }


    // May loose some local variable initializations : affecting the local variable attributes
    if ((this.bits & ASTNode.NeededScope) != 0) {
      codeStream.exitUserScope(this.scope);
    }
    if (this.mergedInitStateIndex != -1) {
      codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.mergedInitStateIndex);
      codeStream.addDefinitelyAssignedVariables(currentScope, this.mergedInitStateIndex);
    }
    this.breakLabel.place();
    codeStream.recordPositionsFrom(pc, this.sourceStart);
  }

  public StringBuffer printStatement(int tab, StringBuffer output) {

    printIndent(tab, output).append("for ("); //$NON-NLS-1$
    //inits
    if (this.initializations != null) {
      for (int i = 0; i < this.initializations.length; i++) {
        //nice only with expressions
        if (i > 0) output.append(", "); //$NON-NLS-1$
        this.initializations[i].print(0, output);
      }
    }
    output.append("; "); //$NON-NLS-1$
    //cond
    if (this.condition != null) this.condition.printExpression(0, output);
    output.append("; "); //$NON-NLS-1$
    //updates
    if (this.increments != null) {
      for (int i = 0; i < this.increments.length; i++) {
        if (i > 0) output.append(", "); //$NON-NLS-1$
        this.increments[i].print(0, output);
      }
    }
    output.append(") "); //$NON-NLS-1$
    //block
    if (this.action == null)
      output.append(';');
    else {
      output.append('\n');
      this.action.printStatement(tab + 1, output);
    }
    return output;
  }

  public void resolve(BlockScope upperScope) {

    // use the scope that will hold the init declarations
    this.scope = (this.bits & ASTNode.NeededScope) != 0 ? new BlockScope(upperScope) : upperScope;
    if (this.initializations != null)
      for (int i = 0, length = this.initializations.length; i < length; i++)
        this.initializations[i].resolve(this.scope);
    if (this.condition != null) {
      TypeBinding type = this.condition.resolveTypeExpecting(this.scope, TypeBinding.BOOLEAN);
      this.condition.computeConversion(this.scope, type, type);
    }
    if (this.increments != null)
      for (int i = 0, length = this.increments.length; i < length; i++)
        this.increments[i].resolve(this.scope);
    if (this.action != null)
      this.action.resolve(this.scope);
  }

  public void traverse(
    ASTVisitor visitor,
    BlockScope blockScope) {

    if (visitor.visit(this, blockScope)) {
      if (this.initializations != null) {
        int initializationsLength = this.initializations.length;
        for (int i = 0; i < initializationsLength; i++)
          this.initializations[i].traverse(visitor, this.scope);
      }

      if (this.condition != null)
        this.condition.traverse(visitor, this.scope);

      if (this.increments != null) {
        int incrementsLength = this.increments.length;
        for (int i = 0; i < incrementsLength; i++)
          this.increments[i].traverse(visitor, this.scope);
      }

      if (this.action != null)
        this.action.traverse(visitor, this.scope);
    }
    visitor.endVisit(this, blockScope);
  }
}
TOP

Related Classes of org.eclipse.jdt.internal.compiler.ast.ForStatement

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.