Search in sources :

Example 6 with ExceptionBlock

use of org.checkerframework.dataflow.cfg.block.ExceptionBlock in project checker-framework by typetools.

the class MustCallConsistencyAnalyzer method propagateObligationsToSuccessorBlocks.

/**
 * Propagates a set of Obligations to successors, and performs consistency checks when variables
 * are going out of scope.
 *
 * <p>The basic algorithm loops over the successor blocks of the current block. For each
 * successor, it checks every Obligation in obligations. If the successor is an exit block or all
 * of an Obligation's resource aliases might be going out of scope, then a consistency check
 * occurs (with two exceptions, both related to temporary variables that don't actually get
 * assigned; see code comments for details) and an error is issued if it fails. If the successor
 * is any other kind of block and there is information about at least one of the Obligation's
 * aliases in the successor store (i.e. the resource itself definitely does not go out of scope),
 * then the Obligation is passed forward to the successor ("propagated") with any definitely
 * out-of-scope aliases removed from its resource alias set.
 *
 * @param obligations Obligations for the current block
 * @param currentBlock the current block
 * @param visited block-Obligations pairs already analyzed or already on the worklist
 * @param worklist current worklist
 */
private void propagateObligationsToSuccessorBlocks(Set<Obligation> obligations, Block currentBlock, Set<BlockWithObligations> visited, Deque<BlockWithObligations> worklist) {
    List<Node> currentBlockNodes = currentBlock.getNodes();
    // loop performs a consistency check for that Obligation.
    for (Pair<Block, @Nullable TypeMirror> successorAndExceptionType : getSuccessorsExceptIgnoredExceptions(currentBlock)) {
        Block successor = successorAndExceptionType.first;
        // If nonnull, currentBlock is an ExceptionBlock.
        TypeMirror exceptionType = successorAndExceptionType.second;
        // successorObligations eventually contains the Obligations to propagate to successor. The
        // loop below mutates it.
        Set<Obligation> successorObligations = new LinkedHashSet<>();
        // A detailed reason to give in the case that the last resource alias of an Obligation
        // goes out of scope without a called-methods type that satisfies the corresponding
        // must-call obligation along the current control-flow edge. Computed here for efficiency;
        // used in the loop over the Obligations, below.
        String exitReasonForErrorMessage = exceptionType == null ? // doesn't seem to provide additional helpful information.
        "regular method exit" : "possible exceptional exit due to " + ((ExceptionBlock) currentBlock).getNode().getTree() + " with exception type " + exceptionType;
        // Computed outside the Obligation loop for efficiency.
        CFStore regularStoreOfSuccessor = analysis.getInput(successor).getRegularStore();
        for (Obligation obligation : obligations) {
            // This boolean is true if there is no evidence that the Obligation does not go out of
            // scope - that is, if there is definitely a resource alias that is in scope in the
            // successor.
            boolean obligationGoesOutOfScopeBeforeSuccessor = true;
            for (ResourceAlias resourceAlias : obligation.resourceAliases) {
                if (aliasInScopeInSuccessor(regularStoreOfSuccessor, resourceAlias)) {
                    obligationGoesOutOfScopeBeforeSuccessor = false;
                    break;
                }
            }
            // should occur.
            if (successor.getType() == BlockType.SPECIAL_BLOCK || /* special blocks are exit blocks */
            obligationGoesOutOfScopeBeforeSuccessor) {
                MustCallAnnotatedTypeFactory mcAtf = typeFactory.getTypeFactoryOfSubchecker(MustCallChecker.class);
                // are ignored. Whether exceptionType is null captures the logic of both of these cases.
                if (exceptionType != null) {
                    Node exceptionalNode = NodeUtils.removeCasts(((ExceptionBlock) currentBlock).getNode());
                    LocalVariableNode tmpVarForExcNode = typeFactory.getTempVarForNode(exceptionalNode);
                    if (tmpVarForExcNode != null && obligation.resourceAliases.size() == 1 && obligation.canBeSatisfiedThrough(tmpVarForExcNode)) {
                        continue;
                    }
                }
                // unwrapped at various points in the analysis.
                if (currentBlockNodes.size() == 1 && inCast(currentBlockNodes.get(0))) {
                    successorObligations.add(obligation);
                    continue;
                }
                // being resolved some other way.
                if (obligation.derivedFromMustCallAlias()) {
                    checker.reportError(obligation.resourceAliases.asList().get(0).tree, "mustcallalias.out.of.scope", exitReasonForErrorMessage);
                    continue;
                }
                // Which stores from the called-methods and must-call checkers are used in
                // the consistency check varies depending on the context. The rules are:
                // 1. if the current block has no nodes (and therefore the store must come from a block
                // rather than a node):
                // 1a. if there is information about any alias in the resource alias set
                // in the successor store, use the successor's CM and MC stores, which
                // contain whatever information is true after this block finishes.
                // 1b. if there is not any information about any alias in the resource alias
                // set in the successor store, use the current blocks' CM and MC stores,
                // which contain whatever information is true before this (empty) block.
                // 2. if the current block has one or more nodes, always use the CM store after
                // the last node. To decide which MC store to use:
                // 2a. if the last node in the block is the invocation of an @CreatesMustCallFor
                // method that might throw an exception, and the consistency check is for
                // an exceptional path, use the MC store immediately before the method invocation,
                // because the method threw an exception rather than finishing and therefore did
                // not actually create any must-call obligation, so the MC store after might
                // contain must-call obligations that do not need to be fulfilled along this path.
                // 2b. in all other cases, use the MC store from after the last node in the block.
                CFStore mcStore, cmStore;
                if (currentBlockNodes.size() == 0) /* currentBlock is special or conditional */
                {
                    cmStore = obligationGoesOutOfScopeBeforeSuccessor ? // 1a. (CM)
                    analysis.getInput(currentBlock).getRegularStore() : // 1b. (CM)
                    regularStoreOfSuccessor;
                    mcStore = mcAtf.getStoreForBlock(obligationGoesOutOfScopeBeforeSuccessor, // 1a. (MC)
                    currentBlock, // 1b. (MC)
                    successor);
                } else {
                    // In this case, current block has at least one node.
                    // Use the called-methods store immediately after the last node in currentBlock.
                    // 2. (CM)
                    Node last = currentBlockNodes.get(currentBlockNodes.size() - 1);
                    cmStore = typeFactory.getStoreAfter(last);
                    // an exception. Otherwise, use the store after.
                    if (exceptionType != null && isInvocationOfCreatesMustCallForMethod(last)) {
                        // 2a. (MC)
                        mcStore = mcAtf.getStoreBefore(last);
                    } else {
                        // 2b. (MC)
                        mcStore = mcAtf.getStoreAfter(last);
                    }
                }
                checkMustCall(obligation, cmStore, mcStore, exitReasonForErrorMessage);
            } else {
                // In this case, there is info in the successor store about some alias in the Obligation.
                // Handles the possibility that some resource in the Obligation may go out of scope.
                Set<ResourceAlias> copyOfResourceAliases = new LinkedHashSet<>(obligation.resourceAliases);
                copyOfResourceAliases.removeIf(alias -> !aliasInScopeInSuccessor(regularStoreOfSuccessor, alias));
                successorObligations.add(new Obligation(copyOfResourceAliases));
            }
        }
        propagate(new BlockWithObligations(successor, successorObligations), visited, worklist);
    }
}
Also used : LinkedHashSet(java.util.LinkedHashSet) CFStore(org.checkerframework.framework.flow.CFStore) TypeCastNode(org.checkerframework.dataflow.cfg.node.TypeCastNode) ObjectCreationNode(org.checkerframework.dataflow.cfg.node.ObjectCreationNode) LocalVariableNode(org.checkerframework.dataflow.cfg.node.LocalVariableNode) ThisNode(org.checkerframework.dataflow.cfg.node.ThisNode) AssignmentNode(org.checkerframework.dataflow.cfg.node.AssignmentNode) NullLiteralNode(org.checkerframework.dataflow.cfg.node.NullLiteralNode) FieldAccessNode(org.checkerframework.dataflow.cfg.node.FieldAccessNode) ReturnNode(org.checkerframework.dataflow.cfg.node.ReturnNode) MethodInvocationNode(org.checkerframework.dataflow.cfg.node.MethodInvocationNode) Node(org.checkerframework.dataflow.cfg.node.Node) MustCallAnnotatedTypeFactory(org.checkerframework.checker.mustcall.MustCallAnnotatedTypeFactory) LocalVariableNode(org.checkerframework.dataflow.cfg.node.LocalVariableNode) AnnotatedTypeMirror(org.checkerframework.framework.type.AnnotatedTypeMirror) TypeMirror(javax.lang.model.type.TypeMirror) ExceptionBlock(org.checkerframework.dataflow.cfg.block.ExceptionBlock) Block(org.checkerframework.dataflow.cfg.block.Block) SingleSuccessorBlock(org.checkerframework.dataflow.cfg.block.SingleSuccessorBlock)

Example 7 with ExceptionBlock

use of org.checkerframework.dataflow.cfg.block.ExceptionBlock in project checker-framework by typetools.

the class BackwardAnalysisImpl method addStoreAfter.

/**
 * Add a store after the basic block {@code pred} by merging with the existing stores for that
 * location.
 *
 * @param pred the basic block
 * @param node the node of the basic block {@code b}
 * @param s the store being added
 * @param addBlockToWorklist whether the basic block {@code b} should be added back to {@code
 *     Worklist}
 */
protected void addStoreAfter(Block pred, @Nullable Node node, S s, boolean addBlockToWorklist) {
    // If the block pred is an exception block, decide whether the block of passing node is an
    // exceptional successor of the block pred
    TypeMirror excSuccType = getSuccExceptionType(pred, node);
    if (excSuccType != null) {
        if (isIgnoredExceptionType(excSuccType)) {
            return;
        }
        // If the block of passing node is an exceptional successor of Block pred, propagate
        // store to the exceptionStores. Currently it doesn't track the label of an
        // exceptional edge from exception block to its exceptional successors in backward
        // direction. Instead, all exception stores of exceptional successors of an
        // exception block will merge to one exception store at the exception block
        ExceptionBlock ebPred = (ExceptionBlock) pred;
        S exceptionStore = exceptionStores.get(ebPred);
        S newExceptionStore = (exceptionStore != null) ? exceptionStore.leastUpperBound(s) : s;
        if (!newExceptionStore.equals(exceptionStore)) {
            exceptionStores.put(ebPred, newExceptionStore);
            inputs.put(ebPred, new TransferInput<V, S>(node, this, newExceptionStore));
            addBlockToWorklist = true;
        }
    } else {
        S predOutStore = getStoreAfter(pred);
        S newPredOutStore = (predOutStore != null) ? predOutStore.leastUpperBound(s) : s;
        if (!newPredOutStore.equals(predOutStore)) {
            outStores.put(pred, newPredOutStore);
            inputs.put(pred, new TransferInput<>(node, this, newPredOutStore));
            addBlockToWorklist = true;
        }
    }
    if (addBlockToWorklist) {
        addToWorklist(pred);
    }
}
Also used : ExceptionBlock(org.checkerframework.dataflow.cfg.block.ExceptionBlock) TypeMirror(javax.lang.model.type.TypeMirror)

Example 8 with ExceptionBlock

use of org.checkerframework.dataflow.cfg.block.ExceptionBlock in project checker-framework by typetools.

the class BackwardAnalysisImpl method runAnalysisFor.

@Override
public S runAnalysisFor(@FindDistinct Node node, Analysis.BeforeOrAfter preOrPost, TransferInput<V, S> blockTransferInput, IdentityHashMap<Node, V> nodeValues, Map<TransferInput<V, S>, IdentityHashMap<Node, TransferResult<V, S>>> analysisCaches) {
    Block block = node.getBlock();
    assert block != null : "@AssumeAssertion(nullness): invariant";
    Node oldCurrentNode = currentNode;
    if (isRunning) {
        assert currentInput != null : "@AssumeAssertion(nullness): invariant";
        return currentInput.getRegularStore();
    }
    isRunning = true;
    try {
        switch(block.getType()) {
            case REGULAR_BLOCK:
                {
                    RegularBlock rBlock = (RegularBlock) block;
                    // Apply transfer function to contents until we found the node we are looking for.
                    TransferInput<V, S> store = blockTransferInput;
                    List<Node> nodeList = rBlock.getNodes();
                    ListIterator<Node> reverseIter = nodeList.listIterator(nodeList.size());
                    while (reverseIter.hasPrevious()) {
                        Node n = reverseIter.previous();
                        setCurrentNode(n);
                        if (n == node && preOrPost == Analysis.BeforeOrAfter.AFTER) {
                            return store.getRegularStore();
                        }
                        // Copy the store to avoid changing other blocks' transfer inputs in
                        // {@link #inputs}
                        TransferResult<V, S> transferResult = callTransferFunction(n, store.copy());
                        if (n == node) {
                            return transferResult.getRegularStore();
                        }
                        store = new TransferInput<>(n, this, transferResult);
                    }
                    throw new BugInCF("node %s is not in node.getBlock()=%s", node, block);
                }
            case EXCEPTION_BLOCK:
                {
                    ExceptionBlock eb = (ExceptionBlock) block;
                    if (eb.getNode() != node) {
                        throw new BugInCF("Node should be equal to eb.getNode(). But get: node: " + node + "\teb.getNode(): " + eb.getNode());
                    }
                    if (preOrPost == Analysis.BeforeOrAfter.AFTER) {
                        return blockTransferInput.getRegularStore();
                    }
                    setCurrentNode(node);
                    // Copy the store to avoid changing other blocks' transfer inputs in {@link #inputs}
                    TransferResult<V, S> transferResult = callTransferFunction(node, blockTransferInput.copy());
                    // Merge transfer result with the exception store of this exceptional block
                    S exceptionStore = exceptionStores.get(eb);
                    return exceptionStore == null ? transferResult.getRegularStore() : transferResult.getRegularStore().leastUpperBound(exceptionStore);
                }
            default:
                // Only regular blocks and exceptional blocks can hold nodes.
                throw new BugInCF("Unexpected block type: " + block.getType());
        }
    } finally {
        setCurrentNode(oldCurrentNode);
        isRunning = false;
    }
}
Also used : ExceptionBlock(org.checkerframework.dataflow.cfg.block.ExceptionBlock) ReturnNode(org.checkerframework.dataflow.cfg.node.ReturnNode) Node(org.checkerframework.dataflow.cfg.node.Node) RegularBlock(org.checkerframework.dataflow.cfg.block.RegularBlock) ExceptionBlock(org.checkerframework.dataflow.cfg.block.ExceptionBlock) SpecialBlock(org.checkerframework.dataflow.cfg.block.SpecialBlock) Block(org.checkerframework.dataflow.cfg.block.Block) ConditionalBlock(org.checkerframework.dataflow.cfg.block.ConditionalBlock) List(java.util.List) RegularBlock(org.checkerframework.dataflow.cfg.block.RegularBlock) ListIterator(java.util.ListIterator) BugInCF(org.checkerframework.javacutil.BugInCF)

Example 9 with ExceptionBlock

use of org.checkerframework.dataflow.cfg.block.ExceptionBlock in project bazel by bazelbuild.

the class ControlFlowGraph method getSuccessors.

/**
     * Get a list of all successor Blocks for cur
     * @param cur
     * @return A Deque of successor Blocks
     */
private Deque<Block> getSuccessors(Block cur) {
    Deque<Block> succs = new LinkedList<>();
    if (cur.getType() == BlockType.CONDITIONAL_BLOCK) {
        ConditionalBlock ccur = ((ConditionalBlock) cur);
        succs.add(ccur.getThenSuccessor());
        succs.add(ccur.getElseSuccessor());
    } else {
        assert cur instanceof SingleSuccessorBlock;
        Block b = ((SingleSuccessorBlock) cur).getSuccessor();
        if (b != null) {
            succs.add(b);
        }
    }
    if (cur.getType() == BlockType.EXCEPTION_BLOCK) {
        ExceptionBlock ecur = (ExceptionBlock) cur;
        for (Set<Block> exceptionSuccSet : ecur.getExceptionalSuccessors().values()) {
            succs.addAll(exceptionSuccSet);
        }
    }
    return succs;
}
Also used : ExceptionBlock(org.checkerframework.dataflow.cfg.block.ExceptionBlock) ConditionalBlock(org.checkerframework.dataflow.cfg.block.ConditionalBlock) SingleSuccessorBlock(org.checkerframework.dataflow.cfg.block.SingleSuccessorBlock) ExceptionBlock(org.checkerframework.dataflow.cfg.block.ExceptionBlock) SingleSuccessorBlock(org.checkerframework.dataflow.cfg.block.SingleSuccessorBlock) SpecialBlock(org.checkerframework.dataflow.cfg.block.SpecialBlock) Block(org.checkerframework.dataflow.cfg.block.Block) ConditionalBlock(org.checkerframework.dataflow.cfg.block.ConditionalBlock) LinkedList(java.util.LinkedList)

Example 10 with ExceptionBlock

use of org.checkerframework.dataflow.cfg.block.ExceptionBlock in project bazel by bazelbuild.

the class ControlFlowGraph method getAllBlocks.

/**
     * @return The set of all basic block in this control flow graph.
     */
public Set<Block> getAllBlocks() {
    Set<Block> visited = new HashSet<>();
    Queue<Block> worklist = new LinkedList<>();
    Block cur = entryBlock;
    visited.add(entryBlock);
    // traverse the whole control flow graph
    while (true) {
        if (cur == null)
            break;
        Queue<Block> succs = new LinkedList<>();
        if (cur.getType() == BlockType.CONDITIONAL_BLOCK) {
            ConditionalBlock ccur = ((ConditionalBlock) cur);
            succs.add(ccur.getThenSuccessor());
            succs.add(ccur.getElseSuccessor());
        } else {
            assert cur instanceof SingleSuccessorBlock;
            Block b = ((SingleSuccessorBlock) cur).getSuccessor();
            if (b != null) {
                succs.add(b);
            }
        }
        if (cur.getType() == BlockType.EXCEPTION_BLOCK) {
            ExceptionBlock ecur = (ExceptionBlock) cur;
            for (Set<Block> exceptionSuccSet : ecur.getExceptionalSuccessors().values()) {
                succs.addAll(exceptionSuccSet);
            }
        }
        for (Block b : succs) {
            if (!visited.contains(b)) {
                visited.add(b);
                worklist.add(b);
            }
        }
        cur = worklist.poll();
    }
    return visited;
}
Also used : ExceptionBlock(org.checkerframework.dataflow.cfg.block.ExceptionBlock) ConditionalBlock(org.checkerframework.dataflow.cfg.block.ConditionalBlock) SingleSuccessorBlock(org.checkerframework.dataflow.cfg.block.SingleSuccessorBlock) ExceptionBlock(org.checkerframework.dataflow.cfg.block.ExceptionBlock) SingleSuccessorBlock(org.checkerframework.dataflow.cfg.block.SingleSuccessorBlock) SpecialBlock(org.checkerframework.dataflow.cfg.block.SpecialBlock) Block(org.checkerframework.dataflow.cfg.block.Block) ConditionalBlock(org.checkerframework.dataflow.cfg.block.ConditionalBlock) LinkedList(java.util.LinkedList) HashSet(java.util.HashSet)

Aggregations

ExceptionBlock (org.checkerframework.dataflow.cfg.block.ExceptionBlock)16 Block (org.checkerframework.dataflow.cfg.block.Block)14 TypeMirror (javax.lang.model.type.TypeMirror)10 ConditionalBlock (org.checkerframework.dataflow.cfg.block.ConditionalBlock)10 SpecialBlock (org.checkerframework.dataflow.cfg.block.SpecialBlock)10 RegularBlock (org.checkerframework.dataflow.cfg.block.RegularBlock)8 SingleSuccessorBlock (org.checkerframework.dataflow.cfg.block.SingleSuccessorBlock)8 Node (org.checkerframework.dataflow.cfg.node.Node)7 Set (java.util.Set)5 ReturnNode (org.checkerframework.dataflow.cfg.node.ReturnNode)5 HashSet (java.util.HashSet)4 AssignmentNode (org.checkerframework.dataflow.cfg.node.AssignmentNode)4 LocalVariableNode (org.checkerframework.dataflow.cfg.node.LocalVariableNode)4 LinkedHashSet (java.util.LinkedHashSet)3 LinkedList (java.util.LinkedList)3 List (java.util.List)3 Map (java.util.Map)3 AnnotatedTypeMirror (org.checkerframework.framework.type.AnnotatedTypeMirror)3 ArrayDeque (java.util.ArrayDeque)2 IdentityHashMap (java.util.IdentityHashMap)2