Search in sources :

Example 1 with LoopingFlowContext

use of org.eclipse.jdt.internal.compiler.flow.LoopingFlowContext in project bazel-jdt-java-toolchain by salesforce.

the class ForStatement method analyseCode.

@Override
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, true)), condInfo);
            this.condition.checkNPEbyUnboxing(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) {
                // for(;false;p());
                this.continueLabel = null;
            }
            actionInfo = condInfo.initsWhenTrue().unconditionalCopy();
            loopingContext = new LoopingFlowContext(flowContext, flowInfo, this, this.breakLabel, this.continueLabel, this.scope, false);
        // there is no action guarded by a preTest, so we use preTest=false
        // to avoid pointless burdens of updating FlowContext.conditionalLevel
        }
    } else {
        loopingContext = new LoopingFlowContext(flowContext, flowInfo, this, this.breakLabel, this.continueLabel, this.scope, true);
        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, true) < Statement.COMPLAINED_UNREACHABLE) {
            if (this.condition != null)
                this.condition.updateFlowOnBooleanResult(actionInfo, true);
            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, true);
            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
            // loopback | (loopback + action):
            loopbackFlowInfo = loopbackFlowInfo.mergedWith(loopbackFlowInfo.unconditionalCopy().addNullInfoFrom(actionInfo).unconditionalInits());
        }
        loopingContext.simulateThrowAfterLoopBack(loopbackFlowInfo);
    }
    // end of loop
    FlowInfo mergedInfo = FlowInfo.mergedOptimizedBranches((loopingContext.initsOnBreak.tagBits & FlowInfo.UNREACHABLE) != 0 ? loopingContext.initsOnBreak : // recover upstream null info
    flowInfo.addInitializationsFrom(loopingContext.initsOnBreak), isConditionOptimizedTrue, exitBranch, isConditionOptimizedFalse, !isConditionTrue);
    // https://bugs.eclipse.org/bugs/show_bug.cgi?id=359495
    if (this.initializations != null) {
        for (int i = 0; i < this.initializations.length; i++) {
            Statement init = this.initializations[i];
            if (init instanceof LocalDeclaration) {
                LocalVariableBinding binding = ((LocalDeclaration) init).binding;
                mergedInfo.resetAssignmentInfo(binding);
            }
        }
    }
    this.mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo);
    this.scope.checkUnclosedCloseables(mergedInfo, loopingContext, null, null);
    if (this.condition != null)
        this.condition.updateFlowOnBooleanResult(mergedInfo, false);
    return mergedInfo;
}
Also used : UnconditionalFlowInfo(org.eclipse.jdt.internal.compiler.flow.UnconditionalFlowInfo) FlowInfo(org.eclipse.jdt.internal.compiler.flow.FlowInfo) BranchLabel(org.eclipse.jdt.internal.compiler.codegen.BranchLabel) Constant(org.eclipse.jdt.internal.compiler.impl.Constant) LoopingFlowContext(org.eclipse.jdt.internal.compiler.flow.LoopingFlowContext) UnconditionalFlowInfo(org.eclipse.jdt.internal.compiler.flow.UnconditionalFlowInfo) LocalVariableBinding(org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding)

Example 2 with LoopingFlowContext

use of org.eclipse.jdt.internal.compiler.flow.LoopingFlowContext in project bazel-jdt-java-toolchain by salesforce.

the class ForeachStatement method analyseCode.

@Override
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
    // initialize break and continue labels
    this.breakLabel = new BranchLabel();
    this.continueLabel = new BranchLabel();
    int initialComplaintLevel = (flowInfo.reachMode() & FlowInfo.UNREACHABLE) != 0 ? Statement.COMPLAINED_FAKE_REACHABLE : Statement.NOT_COMPLAINED;
    // process the element variable and collection
    flowInfo = this.elementVariable.analyseCode(this.scope, flowContext, flowInfo);
    FlowInfo condInfo = this.collection.analyseCode(this.scope, flowContext, flowInfo.copy());
    this.collection.checkNPE(currentScope, flowContext, condInfo.copy(), 1);
    LocalVariableBinding elementVarBinding = this.elementVariable.binding;
    // element variable will be assigned when iterating
    condInfo.markAsDefinitelyAssigned(elementVarBinding);
    this.postCollectionInitStateIndex = currentScope.methodScope().recordInitializationStates(condInfo);
    // process the action
    LoopingFlowContext loopingContext = new LoopingFlowContext(flowContext, flowInfo, this, this.breakLabel, this.continueLabel, this.scope, true);
    UnconditionalFlowInfo actionInfo = condInfo.nullInfoLessUnconditionalCopy();
    actionInfo.markAsDefinitelyUnknown(elementVarBinding);
    if (currentScope.compilerOptions().isAnnotationBasedNullAnalysisEnabled) {
        int elementNullStatus = NullAnnotationMatching.nullStatusFromExpressionType(this.collectionElementType);
        int nullStatus = // have no useful flowinfo for element var
        NullAnnotationMatching.checkAssignment(// have no useful flowinfo for element var
        currentScope, // have no useful flowinfo for element var
        flowContext, // have no useful flowinfo for element var
        elementVarBinding, // have no useful flowinfo for element var
        null, elementNullStatus, this.collection, this.collectionElementType);
        if ((this.kind == GENERIC_ITERABLE || this.kind == RAW_ITERABLE) && !currentScope.compilerOptions().usesNullTypeAnnotations()) {
            // respect declaration annotation on Iterator.next():
            ReferenceBinding iterator = currentScope.getJavaUtilIterator();
            if (iterator != null) {
                MethodBinding next = iterator.getExactMethod(TypeConstants.NEXT, Binding.NO_TYPES, currentScope.compilationUnitScope);
                if (next != null && ((next.tagBits & TagBits.AnnotationNullMASK) != 0)) {
                    nullStatus = FlowInfo.tagBitsToNullStatus(next.tagBits);
                }
            }
        }
        if ((elementVarBinding.type.tagBits & TagBits.IsBaseType) == 0) {
            actionInfo.markNullStatus(elementVarBinding, nullStatus);
        }
    }
    FlowInfo exitBranch;
    if (!(this.action == null || (this.action.isEmptyBlock() && currentScope.compilerOptions().complianceLevel <= ClassFileConstants.JDK1_3))) {
        if (this.action.complainIfUnreachable(actionInfo, this.scope, initialComplaintLevel, true) < Statement.COMPLAINED_UNREACHABLE) {
            actionInfo = this.action.analyseCode(this.scope, loopingContext, actionInfo).unconditionalCopy();
            FakedTrackingVariable.markForeachElementVar(this.elementVariable);
            // action.analyseCode() missed the following check due to identical scopes of ForeachStatement and action:
            // previously action did not see nullinfo from condInfo
            FlowInfo actionNullInfo = condInfo.copy().addNullInfoFrom(actionInfo);
            this.scope.checkUnclosedCloseables(actionNullInfo, loopingContext, null, null);
        }
        // code generation can be optimized when no need to continue in the loop
        exitBranch = flowInfo.unconditionalCopy().addInitializationsFrom(condInfo.initsWhenFalse());
        // TODO (maxime) no need to test when false: can optimize (same for action being unreachable above)
        if ((actionInfo.tagBits & loopingContext.initsOnContinue.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) != 0) {
            this.continueLabel = null;
        } else {
            actionInfo = actionInfo.mergedWith(loopingContext.initsOnContinue);
            loopingContext.complainOnDeferredFinalChecks(this.scope, actionInfo);
            exitBranch.addPotentialInitializationsFrom(actionInfo);
        }
    } else {
        exitBranch = condInfo.initsWhenFalse();
        if (this.action instanceof Block && !this.action.isEmptyBlock()) {
            this.scope.checkUnclosedCloseables(actionInfo, loopingContext, null, null);
        }
    }
    // we need the variable to iterate the collection even if the
    // element variable is not used
    final boolean hasEmptyAction = this.action == null || this.action.isEmptyBlock() || ((this.action.bits & IsUsefulEmptyStatement) != 0);
    switch(this.kind) {
        case ARRAY:
            if (!hasEmptyAction || elementVarBinding.resolvedPosition != -1) {
                this.collectionVariable.useFlag = LocalVariableBinding.USED;
                if (this.continueLabel != null) {
                    this.indexVariable.useFlag = LocalVariableBinding.USED;
                    this.maxVariable.useFlag = LocalVariableBinding.USED;
                }
            }
            break;
        case RAW_ITERABLE:
        case GENERIC_ITERABLE:
            this.indexVariable.useFlag = LocalVariableBinding.USED;
            break;
    }
    // end of loop
    loopingContext.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
            // loopback | (loopback + action):
            loopbackFlowInfo = loopbackFlowInfo.mergedWith(loopbackFlowInfo.unconditionalCopy().addNullInfoFrom(actionInfo).unconditionalInits());
        }
        loopingContext.simulateThrowAfterLoopBack(loopbackFlowInfo);
    }
    FlowInfo mergedInfo = FlowInfo.mergedOptimizedBranches((loopingContext.initsOnBreak.tagBits & FlowInfo.UNREACHABLE) != 0 ? loopingContext.initsOnBreak : // recover upstream null info
    flowInfo.addInitializationsFrom(loopingContext.initsOnBreak), false, exitBranch, false, true);
    mergedInfo.resetAssignmentInfo(this.elementVariable.binding);
    this.mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo);
    return mergedInfo;
}
Also used : FlowInfo(org.eclipse.jdt.internal.compiler.flow.FlowInfo) UnconditionalFlowInfo(org.eclipse.jdt.internal.compiler.flow.UnconditionalFlowInfo) BranchLabel(org.eclipse.jdt.internal.compiler.codegen.BranchLabel) MethodBinding(org.eclipse.jdt.internal.compiler.lookup.MethodBinding) ReferenceBinding(org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding) ProblemReferenceBinding(org.eclipse.jdt.internal.compiler.lookup.ProblemReferenceBinding) LoopingFlowContext(org.eclipse.jdt.internal.compiler.flow.LoopingFlowContext) UnconditionalFlowInfo(org.eclipse.jdt.internal.compiler.flow.UnconditionalFlowInfo) LocalVariableBinding(org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding)

Aggregations

BranchLabel (org.eclipse.jdt.internal.compiler.codegen.BranchLabel)2 FlowInfo (org.eclipse.jdt.internal.compiler.flow.FlowInfo)2 LoopingFlowContext (org.eclipse.jdt.internal.compiler.flow.LoopingFlowContext)2 UnconditionalFlowInfo (org.eclipse.jdt.internal.compiler.flow.UnconditionalFlowInfo)2 LocalVariableBinding (org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding)2 Constant (org.eclipse.jdt.internal.compiler.impl.Constant)1 MethodBinding (org.eclipse.jdt.internal.compiler.lookup.MethodBinding)1 ProblemReferenceBinding (org.eclipse.jdt.internal.compiler.lookup.ProblemReferenceBinding)1 ReferenceBinding (org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding)1