org.eclipse.jdt.internal.compiler.ast.DoStatement.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.jdt.internal.compiler.ast.DoStatement.java

Source

/*******************************************************************************
 * Copyright (c) 2000, 2015 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     Stephan Herrmann - Contributions for
 *                        bug 319201 - [null] no warning when unboxing SingleNameReference causes NPE
 *                        bug 345305 - [compiler][null] Compiler misidentifies a case of "variable can only be null"
 *                        bug 403147 - [compiler][null] FUP of bug 400761: consolidate interaction between unboxing, NPE, and deferred checking
 *                        Bug 415790 - [compiler][resource]Incorrect potential resource leak warning in for loop with close in try/catch
 *******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;

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

public class DoStatement extends Statement {

    public Expression condition;
    public Statement action;

    private BranchLabel breakLabel, continueLabel;

    // for local variables table attributes
    int mergedInitStateIndex = -1;
    int preConditionInitStateIndex = -1;

    public DoStatement(Expression condition, Statement action, int sourceStart, int sourceEnd) {

        this.sourceStart = sourceStart;
        this.sourceEnd = sourceEnd;
        this.condition = condition;
        this.action = action;
        // remember useful empty statement
        if (action instanceof EmptyStatement)
            action.bits |= ASTNode.IsUsefulEmptyStatement;
    }

    @Override
    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, false);

        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();

        FlowInfo initsOnCondition = flowInfo;
        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_OR_DEAD) != 0) {
                this.continueLabel = null;
            }
            if ((this.condition.implicitConversion & TypeIds.UNBOXING) != 0) {
                initsOnCondition = flowInfo.unconditionalInits()
                        .addInitializationsFrom(actionInfo.mergedWith(loopingContext.initsOnContinue));
            }
        }
        this.condition.checkNPEbyUnboxing(currentScope, flowContext, initsOnCondition);
        /* 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,
                        true)),
                (this.action == null ? actionInfo : (actionInfo.mergedWith(loopingContext.initsOnContinue)))
                        .copy());
        /* https://bugs.eclipse.org/bugs/show_bug.cgi?id=367023, we reach the condition at the bottom via two arcs, 
           one by free fall and another by continuing... Merge initializations propagated through the two pathways,
           cf, while and for loops.
        */
        this.preConditionInitStateIndex = currentScope.methodScope()
                .recordInitializationStates(actionInfo.mergedWith(loopingContext.initsOnContinue));
        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()));
        } else {
            loopingContext.complainOnDeferredNullChecks(currentScope, flowInfo.unconditionalCopy()
                    .addPotentialNullInfoFrom(condInfo.initsWhenTrue().unconditionalInits()), false);
            condLoopContext.complainOnDeferredNullChecks(currentScope,
                    actionInfo.addPotentialNullInfoFrom(condInfo.initsWhenTrue().unconditionalInits()), false);
        }
        if (loopingContext.hasEscapingExceptions()) { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=321926
            FlowInfo loopbackFlowInfo = flowInfo.copy();
            // loopback | (loopback + action + condition):
            loopbackFlowInfo = loopbackFlowInfo.mergedWith(loopbackFlowInfo.unconditionalCopy()
                    .addNullInfoFrom(condInfo.initsWhenTrue()).unconditionalInits());
            loopingContext.simulateThrowAfterLoopBack(loopbackFlowInfo);
        }
        // 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.copy().addInitializationsFrom(condInfo.initsWhenFalse()) // https://bugs.eclipse.org/bugs/show_bug.cgi?id=380927
                        : 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;
    }

    /**
     * Do statement code generation
     *
     */
    @Override
    public void generateCode(BlockScope currentScope, CodeStream codeStream) {
        if ((this.bits & ASTNode.IsReachable) == 0) {
            return;
        }
        int pc = codeStream.position;

        // labels management
        BranchLabel actionLabel = new BranchLabel(codeStream);
        if (this.action != null)
            actionLabel.tagBits |= BranchLabel.USED;
        actionLabel.place();
        this.breakLabel.initialize(codeStream);
        boolean hasContinueLabel = this.continueLabel != null;
        if (hasContinueLabel) {
            this.continueLabel.initialize(codeStream);
        }

        // generate action
        if (this.action != null) {
            this.action.generateCode(currentScope, codeStream);
        }
        // continue label (135602)
        if (hasContinueLabel) {
            this.continueLabel.place();
            // May loose some local variable initializations : affecting the local variable attributes
            if (this.preConditionInitStateIndex != -1) {
                codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.preConditionInitStateIndex);
                codeStream.addDefinitelyAssignedVariables(currentScope, this.preConditionInitStateIndex);
            }
            // generate condition
            Constant cst = this.condition.optimizedBooleanConstant();
            boolean isConditionOptimizedFalse = cst != Constant.NotAConstant && cst.booleanValue() == false;
            if (isConditionOptimizedFalse) {
                this.condition.generateCode(currentScope, codeStream, false);
            } else {
                this.condition.generateOptimizedBoolean(currentScope, codeStream, actionLabel, null, true);
            }
        }
        // May loose some local variable initializations : affecting the local variable attributes
        if (this.mergedInitStateIndex != -1) {
            codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.mergedInitStateIndex);
            codeStream.addDefinitelyAssignedVariables(currentScope, this.mergedInitStateIndex);
        }
        if (this.breakLabel.forwardReferenceCount() > 0) {
            this.breakLabel.place();
        }

        codeStream.recordPositionsFrom(pc, this.sourceStart);
    }

    @Override
    public StringBuffer printStatement(int indent, StringBuffer output) {
        printIndent(indent, output).append("do"); //$NON-NLS-1$
        if (this.action == null)
            output.append(" ;\n"); //$NON-NLS-1$
        else {
            output.append('\n');
            this.action.printStatement(indent + 1, output).append('\n');
        }
        output.append("while ("); //$NON-NLS-1$
        return this.condition.printExpression(0, output).append(");"); //$NON-NLS-1$
    }

    @Override
    public void resolve(BlockScope scope) {
        TypeBinding type = this.condition.resolveTypeExpecting(scope, TypeBinding.BOOLEAN);
        this.condition.computeConversion(scope, type, type);
        if (this.action != null)
            this.action.resolve(scope);
    }

    @Override
    public void traverse(ASTVisitor visitor, BlockScope scope) {
        if (visitor.visit(this, scope)) {
            if (this.action != null) {
                this.action.traverse(visitor, scope);
            }
            this.condition.traverse(visitor, scope);
        }
        visitor.endVisit(this, scope);
    }

    @Override
    public boolean doesNotCompleteNormally() {
        Constant cst = this.condition.constant;
        boolean isConditionTrue = cst == null || cst != Constant.NotAConstant && cst.booleanValue() == true;
        cst = this.condition.optimizedBooleanConstant();
        boolean isConditionOptimizedTrue = cst == null ? true
                : cst != Constant.NotAConstant && cst.booleanValue() == true;

        if (isConditionTrue || isConditionOptimizedTrue)
            return this.action == null || !this.action.breaksOut(null);
        if (this.action == null || this.action.breaksOut(null))
            return false;
        return this.action.doesNotCompleteNormally() && !this.action.completesByContinue();
    }

    @Override
    public boolean completesByContinue() {
        return this.action.continuesAtOuterLabel();
    }
}