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