All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.checkerframework.dataflow.cfg.visualize.AbstractCFGVisualizer Maven / Gradle / Ivy

Go to download

The Checker Framework enhances Java's type system to make it more powerful and useful. This lets software developers detect and prevent errors in their Java programs. The Checker Framework includes compiler plug-ins ("checkers") that find bugs or verify their absence. It also permits you to write your own compiler plug-ins.

There is a newer version: 3.43.0
Show newest version
package org.checkerframework.dataflow.cfg.visualize;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.StringJoiner;
import javax.lang.model.type.TypeMirror;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.dataflow.analysis.AbstractValue;
import org.checkerframework.dataflow.analysis.Analysis;
import org.checkerframework.dataflow.analysis.Analysis.Direction;
import org.checkerframework.dataflow.analysis.Store;
import org.checkerframework.dataflow.analysis.TransferFunction;
import org.checkerframework.dataflow.analysis.TransferInput;
import org.checkerframework.dataflow.cfg.ControlFlowGraph;
import org.checkerframework.dataflow.cfg.block.Block;
import org.checkerframework.dataflow.cfg.block.ConditionalBlock;
import org.checkerframework.dataflow.cfg.block.ExceptionBlock;
import org.checkerframework.dataflow.cfg.block.SingleSuccessorBlock;
import org.checkerframework.dataflow.cfg.block.SpecialBlock;
import org.checkerframework.dataflow.cfg.node.Node;
import org.checkerframework.javacutil.BugInCF;
import org.plumelib.util.StringsPlume;
import org.plumelib.util.UniqueId;

/**
 * This abstract class makes implementing a {@link CFGVisualizer} easier. Some of the methods in
 * {@link CFGVisualizer} are already implemented in this abstract class, but can be overridden if
 * necessary.
 *
 * @param  the abstract value type to be tracked by the analysis
 * @param  the store type used in the analysis
 * @param  the transfer function type that is used to approximate runtime behavior
 * @see DOTCFGVisualizer
 * @see StringCFGVisualizer
 */
public abstract class AbstractCFGVisualizer<
        V extends AbstractValue, S extends Store, T extends TransferFunction>
    implements CFGVisualizer {

  /**
   * If {@code true}, {@link CFGVisualizer} returns more detailed information.
   *
   * 

Initialized in {@link #init(Map)}. */ protected boolean verbose; /** The line separator. */ protected static final String lineSeparator = System.lineSeparator(); /** The indentation for elements of the store. */ protected static final String storeEntryIndent = " "; @Override public void init(Map args) { this.verbose = toBoolean(args.get("verbose")); } /** * Convert the value to boolean, by parsing a string or casting any other value. null converts to * false. * * @param o an object to convert to boolean * @return {@code o} converted to boolean */ private static boolean toBoolean(@Nullable Object o) { if (o == null) { return false; } if (o instanceof String) { return Boolean.parseBoolean((String) o); } return (boolean) o; } /** * Visualize a control flow graph. * * @param cfg the current control flow graph * @param entry the entry block of the control flow graph * @param analysis the current analysis * @return the representation of the control flow graph */ protected String visualizeGraph( ControlFlowGraph cfg, Block entry, @Nullable Analysis analysis) { return visualizeGraphHeader() + visualizeGraphWithoutHeaderAndFooter(cfg, entry, analysis) + visualizeGraphFooter(); } /** * 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 analysis) { Set visited = new LinkedHashSet<>(); StringBuilder sbGraph = new StringBuilder(); Queue 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(); } /** * 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 visited, Queue 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> 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); } } } } /** * Checks whether a block exists in the visited blocks list, and, if not, adds it to the visited * blocks list and the work list. * * @param b the block to check * @param visited the set of blocks that have already been visited or are in the work list * @param workList the queue of blocks to be processed */ protected void addBlock(Block b, Set visited, Queue workList) { if (visited.add(b)) { workList.add(b); } } /** * Helper method to visualize a block. * *

NOTE: The output ends with a separator, only if an "after" store is visualized. The client * {@link #visualizeBlock} should correct this if needed. * * @param bb the block * @param analysis the current analysis * @param separator the line separator. Examples: "\\l" for left justification in {@link * DOTCFGVisualizer} (this is really a terminator, not a separator), "\n" to add a new line in * {@link StringCFGVisualizer} * @return the String representation of the block */ protected String visualizeBlockHelper( Block bb, @Nullable Analysis analysis, String separator) { StringBuilder sbBlock = new StringBuilder(); String contents = loopOverBlockContents(bb, analysis, separator); if (!contents.isEmpty()) { sbBlock.append(contents); } if (sbBlock.length() == 0) { // Nothing got appended; use default text for empty block if (bb.getType() == Block.BlockType.SPECIAL_BLOCK) { sbBlock.append(visualizeSpecialBlock((SpecialBlock) bb)); } else if (bb.getType() == Block.BlockType.CONDITIONAL_BLOCK) { sbBlock.append(visualizeConditionalBlock((ConditionalBlock) bb)); } else { sbBlock.append(""); } } // Visualize transfer input if necessary. if (analysis != null) { sbBlock.insert(0, visualizeBlockTransferInputBefore(bb, analysis) + separator); if (verbose) { Node lastNode = bb.getLastNode(); if (lastNode != null) { if (!sbBlock.toString().endsWith(separator)) { sbBlock.append(separator); } sbBlock.append(visualizeBlockTransferInputAfter(bb, analysis) + separator); } } } return sbBlock.toString(); } /** * Iterates over the block content and visualizes all the nodes in it. * * @param bb the block * @param analysis the current analysis * @param separator the separator between the nodes of the block * @return the String representation of the contents of the block */ protected String loopOverBlockContents( Block bb, @Nullable Analysis analysis, String separator) { List contents = addBlockContent(bb); StringJoiner sjBlockContents = new StringJoiner(separator); for (Node t : contents) { sjBlockContents.add(visualizeBlockNode(t, analysis)); } return sjBlockContents.toString(); } /** * Returns the contents of the block. * * @param bb the block * @return the contents of the block, as a list of nodes */ protected List addBlockContent(Block bb) { return bb.getNodes(); } /** * Format the given object as a String suitable for the output format, i.e. with format-specific * characters escaped. * * @param obj an object * @return the formatted String from the given object */ protected abstract String format(Object obj); @Override public String visualizeBlockNode(Node t, @Nullable Analysis analysis) { StringBuilder sbBlockNode = new StringBuilder(); sbBlockNode.append(format(t)).append(" [ ").append(getNodeSimpleName(t)).append(" ]"); if (analysis != null) { V value = analysis.getValue(t); if (value != null) { sbBlockNode.append(" > ").append(format(value)); } } return sbBlockNode.toString(); } /** Whether to visualize before or after a block. */ protected enum VisualizeWhere { /** Visualize before the block. */ BEFORE, /** Visualize after the block. */ AFTER } /** * Visualize the transfer input before or after the given block. * * @param where either BEFORE or AFTER * @param bb a block * @param analysis the current analysis * @param separator the line separator. Examples: "\\l" for left justification in {@link * DOTCFGVisualizer} (which is actually a line TERMINATOR, not a separator!), "\n" to add a * new line in {@link StringCFGVisualizer} * @return the visualization of the transfer input before or after the given block */ protected String visualizeBlockTransferInputHelper( VisualizeWhere where, Block bb, Analysis analysis, String separator) { if (analysis == null) { throw new BugInCF( "analysis must be non-null when visualizing the transfer input of a block."); } Direction analysisDirection = analysis.getDirection(); S regularStore; S thenStore = null; S elseStore = null; boolean isTwoStores = false; UniqueId storesFrom; if (analysisDirection == Direction.FORWARD && where == VisualizeWhere.AFTER) { regularStore = analysis.getResult().getStoreAfter(bb); storesFrom = analysis.getResult(); } else if (analysisDirection == Direction.BACKWARD && where == VisualizeWhere.BEFORE) { regularStore = analysis.getResult().getStoreBefore(bb); storesFrom = analysis.getResult(); } else { TransferInput input = analysis.getInput(bb); assert input != null : "@AssumeAssertion(nullness): invariant"; storesFrom = input; isTwoStores = input.containsTwoStores(); regularStore = input.getRegularStore(); thenStore = input.getThenStore(); elseStore = input.getElseStore(); } StringBuilder sbStore = new StringBuilder(); if (verbose) { sbStore.append(storesFrom.getClassAndUid() + separator); } sbStore.append(where == VisualizeWhere.BEFORE ? "Before: " : "After: "); if (!isTwoStores) { sbStore.append(visualizeStore(regularStore)); } else { assert thenStore != null : "@AssumeAssertion(nullness): invariant"; assert elseStore != null : "@AssumeAssertion(nullness): invariant"; sbStore.append("then="); sbStore.append(visualizeStore(thenStore)); sbStore.append(","); sbStore.append(separator); sbStore.append("else="); sbStore.append(visualizeStore(elseStore)); } if (where == VisualizeWhere.BEFORE) { sbStore.append(separator + "~~~~~~~~~"); } else { sbStore.insert(0, "~~~~~~~~~" + separator); } return sbStore.toString(); } /** * Visualize a special block. * * @param sbb the special block * @return the String representation of the special block */ protected String visualizeSpecialBlockHelper(SpecialBlock sbb) { switch (sbb.getSpecialType()) { case ENTRY: return ""; case EXIT: return ""; case EXCEPTIONAL_EXIT: return ""; default: throw new BugInCF("Unrecognized special block type: " + sbb.getType()); } } /** * Generate the order of processing blocks. Because a block may appear more than once in {@link * ControlFlowGraph#getDepthFirstOrderedBlocks()}, the orders of each block are stored in a * separate array list. * * @param cfg the current control flow graph * @return an IdentityHashMap that maps from blocks to their orders */ protected IdentityHashMap> getProcessOrder(ControlFlowGraph cfg) { IdentityHashMap> depthFirstOrder = new IdentityHashMap<>(); int count = 1; for (Block b : cfg.getDepthFirstOrderedBlocks()) { depthFirstOrder.computeIfAbsent(b, k -> new ArrayList<>()); @SuppressWarnings("nullness:assignment") // computeIfAbsent's function doesn't return null @NonNull List blockIds = depthFirstOrder.get(b); blockIds.add(count++); } return depthFirstOrder; } @Override public String visualizeStore(S store) { return store.visualize(this); } /** * Generate the String representation of the nodes of a control flow graph. * * @param blocks the set of all the blocks in a control flow graph * @param cfg the control flow graph * @param analysis the current analysis * @return the String representation of the nodes */ protected abstract String visualizeNodes( Set blocks, ControlFlowGraph cfg, @Nullable Analysis analysis); /** * Generate the String representation of an edge. * * @param sId a representation of the current block, such as its ID * @param eId a representation of the successor block, such as its ID * @param flowRule the content of the edge * @return the String representation of the edge */ protected abstract String visualizeEdge(Object sId, Object eId, String flowRule); /** * Return the header of the generated graph. * * @return the String representation of the header of the control flow graph */ protected abstract String visualizeGraphHeader(); /** * Return the footer of the generated graph. * * @return the String representation of the footer of the control flow graph */ protected abstract String visualizeGraphFooter(); /** * Given a list of process orders (integers), returns a string representation. * *

Examples: "Process order: 23", "Process order: 23,25". * * @param order a list of process orders * @return a String representation of the given process orders */ protected String getProcessOrderSimpleString(List order) { return "Process order: " + StringsPlume.join(",", order); } /** * Get the simple name of a node. * * @param t a node * @return the node's simple name, without "Node" */ protected String getNodeSimpleName(Node t) { String name = t.getClass().getSimpleName(); return name.replace("Node", ""); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy