Search in sources :

Example 6 with Block

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

the class DOTCFGVisualizer method getProcessOrder.

protected IdentityHashMap<Block, List<Integer>> getProcessOrder(ControlFlowGraph cfg) {
    IdentityHashMap<Block, List<Integer>> depthFirstOrder = new IdentityHashMap<>();
    int count = 1;
    for (Block b : cfg.getDepthFirstOrderedBlocks()) {
        if (depthFirstOrder.get(b) == null) {
            depthFirstOrder.put(b, new ArrayList<>());
        }
        depthFirstOrder.get(b).add(count++);
    }
    return depthFirstOrder;
}
Also used : IdentityHashMap(java.util.IdentityHashMap) ExceptionBlock(org.checkerframework.dataflow.cfg.block.ExceptionBlock) SpecialBlock(org.checkerframework.dataflow.cfg.block.SpecialBlock) Block(org.checkerframework.dataflow.cfg.block.Block) RegularBlock(org.checkerframework.dataflow.cfg.block.RegularBlock) SingleSuccessorBlock(org.checkerframework.dataflow.cfg.block.SingleSuccessorBlock) ConditionalBlock(org.checkerframework.dataflow.cfg.block.ConditionalBlock) ArrayList(java.util.ArrayList) List(java.util.List)

Example 7 with Block

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

the class DOTCFGVisualizer method visualizeNodes.

@SuppressWarnings("keyfor:enhancedfor")
@Override
public String visualizeNodes(Set<Block> blocks, ControlFlowGraph cfg, @Nullable Analysis<V, S, T> analysis) {
    StringBuilder sbDotNodes = new StringBuilder();
    IdentityHashMap<Block, List<Integer>> processOrder = getProcessOrder(cfg);
    // Definition of all nodes including their labels.
    for (@KeyFor("processOrder") Block v : blocks) {
        sbDotNodes.append("    ").append(v.getUid()).append(" [");
        if (v.getType() == BlockType.CONDITIONAL_BLOCK) {
            sbDotNodes.append("shape=polygon sides=8 ");
        } else if (v.getType() == BlockType.SPECIAL_BLOCK) {
            sbDotNodes.append("shape=oval ");
        } else {
            sbDotNodes.append("shape=rectangle ");
        }
        sbDotNodes.append("label=\"");
        if (verbose) {
            sbDotNodes.append(getProcessOrderSimpleString(processOrder.get(v))).append(getSeparator());
        }
        String strBlock = visualizeBlock(v, analysis);
        if (strBlock.length() == 0) {
            if (v.getType() == BlockType.CONDITIONAL_BLOCK) {
                // The footer of the conditional block.
                sbDotNodes.append("\"];");
            } else {
                // The footer of the block which has no content and is not a special or conditional block.
                sbDotNodes.append("?? empty ??\"];");
            }
        } else {
            sbDotNodes.append(strBlock).append("\"];");
        }
        sbDotNodes.append(System.lineSeparator());
    }
    return sbDotNodes.toString();
}
Also used : KeyFor(org.checkerframework.checker.nullness.qual.KeyFor) 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)

Example 8 with Block

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

the class AbstractCFGVisualizer method visualizeGraphWithoutHeaderAndFooter.

/**
 * Helper method to visualize a control flow graph, without outputting a header or footer.
 *
 * @param cfg the control flow graph
 * @param entry the entry block of the control flow graph
 * @param analysis the current analysis
 * @return the String representation of the control flow graph
 */
protected String visualizeGraphWithoutHeaderAndFooter(ControlFlowGraph cfg, Block entry, @Nullable Analysis<V, S, T> analysis) {
    Set<Block> visited = new LinkedHashSet<>();
    StringBuilder sbGraph = new StringBuilder();
    Queue<Block> workList = new ArrayDeque<>();
    Block cur = entry;
    visited.add(entry);
    while (cur != null) {
        handleSuccessorsHelper(cur, visited, workList, sbGraph);
        cur = workList.poll();
    }
    sbGraph.append(lineSeparator);
    sbGraph.append(visualizeNodes(visited, cfg, analysis));
    return sbGraph.toString();
}
Also used : LinkedHashSet(java.util.LinkedHashSet) ExceptionBlock(org.checkerframework.dataflow.cfg.block.ExceptionBlock) SpecialBlock(org.checkerframework.dataflow.cfg.block.SpecialBlock) Block(org.checkerframework.dataflow.cfg.block.Block) SingleSuccessorBlock(org.checkerframework.dataflow.cfg.block.SingleSuccessorBlock) ConditionalBlock(org.checkerframework.dataflow.cfg.block.ConditionalBlock) ArrayDeque(java.util.ArrayDeque)

Example 9 with Block

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

the class AbstractCFGVisualizer method handleSuccessorsHelper.

/**
 * Outputs, to sbGraph, a visualization of a block's edges, but not the block itself. (The block
 * itself is output elsewhere.) Also adds the successors of the block to the work list and the
 * visited blocks list.
 *
 * @param cur the current block
 * @param visited the set of blocks that have already been visited or are in the work list; side
 *     effected by this method
 * @param workList the queue of blocks to be processed; side effected by this method
 * @param sbGraph the {@link StringBuilder} to store the graph; side effected by this method
 */
protected void handleSuccessorsHelper(Block cur, Set<Block> visited, Queue<Block> workList, StringBuilder sbGraph) {
    if (cur.getType() == Block.BlockType.CONDITIONAL_BLOCK) {
        ConditionalBlock ccur = ((ConditionalBlock) cur);
        Block thenSuccessor = ccur.getThenSuccessor();
        sbGraph.append(visualizeEdge(ccur.getUid(), thenSuccessor.getUid(), ccur.getThenFlowRule().toString()));
        sbGraph.append(lineSeparator);
        addBlock(thenSuccessor, visited, workList);
        Block elseSuccessor = ccur.getElseSuccessor();
        sbGraph.append(visualizeEdge(ccur.getUid(), elseSuccessor.getUid(), ccur.getElseFlowRule().toString()));
        sbGraph.append(lineSeparator);
        addBlock(elseSuccessor, visited, workList);
    } else {
        SingleSuccessorBlock sscur = (SingleSuccessorBlock) cur;
        Block succ = sscur.getSuccessor();
        if (succ != null) {
            sbGraph.append(visualizeEdge(cur.getUid(), succ.getUid(), sscur.getFlowRule().name()));
            sbGraph.append(lineSeparator);
            addBlock(succ, visited, workList);
        }
    }
    if (cur.getType() == Block.BlockType.EXCEPTION_BLOCK) {
        ExceptionBlock ecur = (ExceptionBlock) cur;
        for (Map.Entry<TypeMirror, Set<Block>> e : ecur.getExceptionalSuccessors().entrySet()) {
            TypeMirror cause = e.getKey();
            String exception = cause.toString();
            if (exception.startsWith("java.lang.")) {
                exception = exception.replace("java.lang.", "");
            }
            for (Block b : e.getValue()) {
                sbGraph.append(visualizeEdge(cur.getUid(), b.getUid(), exception));
                sbGraph.append(lineSeparator);
                addBlock(b, visited, workList);
            }
        }
    }
}
Also used : ExceptionBlock(org.checkerframework.dataflow.cfg.block.ExceptionBlock) LinkedHashSet(java.util.LinkedHashSet) Set(java.util.Set) ConditionalBlock(org.checkerframework.dataflow.cfg.block.ConditionalBlock) SingleSuccessorBlock(org.checkerframework.dataflow.cfg.block.SingleSuccessorBlock) TypeMirror(javax.lang.model.type.TypeMirror) ExceptionBlock(org.checkerframework.dataflow.cfg.block.ExceptionBlock) SpecialBlock(org.checkerframework.dataflow.cfg.block.SpecialBlock) Block(org.checkerframework.dataflow.cfg.block.Block) SingleSuccessorBlock(org.checkerframework.dataflow.cfg.block.SingleSuccessorBlock) ConditionalBlock(org.checkerframework.dataflow.cfg.block.ConditionalBlock) Map(java.util.Map) IdentityHashMap(java.util.IdentityHashMap)

Example 10 with Block

use of org.checkerframework.dataflow.cfg.block.Block 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)

Aggregations

Block (org.checkerframework.dataflow.cfg.block.Block)37 ExceptionBlock (org.checkerframework.dataflow.cfg.block.ExceptionBlock)30 ConditionalBlock (org.checkerframework.dataflow.cfg.block.ConditionalBlock)27 SpecialBlock (org.checkerframework.dataflow.cfg.block.SpecialBlock)25 RegularBlock (org.checkerframework.dataflow.cfg.block.RegularBlock)20 SingleSuccessorBlock (org.checkerframework.dataflow.cfg.block.SingleSuccessorBlock)18 HashSet (java.util.HashSet)10 ArrayList (java.util.ArrayList)9 List (java.util.List)9 TypeMirror (javax.lang.model.type.TypeMirror)9 Node (org.checkerframework.dataflow.cfg.node.Node)8 LocalVariableNode (org.checkerframework.dataflow.cfg.node.LocalVariableNode)7 ArrayDeque (java.util.ArrayDeque)6 IdentityHashMap (java.util.IdentityHashMap)6 LinkedHashSet (java.util.LinkedHashSet)6 Set (java.util.Set)6 BugInCF (org.checkerframework.javacutil.BugInCF)6 LinkedList (java.util.LinkedList)5 ReturnNode (org.checkerframework.dataflow.cfg.node.ReturnNode)5 Map (java.util.Map)4