Skip to content

Instantly share code, notes, and snippets.

@Tikam02
Forked from pxpc2/Graph.java
Created November 19, 2016 21:43
Show Gist options
  • Save Tikam02/7cda74e9a13b30e67ce44c9d1dc592df to your computer and use it in GitHub Desktop.
Save Tikam02/7cda74e9a13b30e67ce44c9d1dc592df to your computer and use it in GitHub Desktop.

Revisions

  1. @pxpc2 pxpc2 created this gist Apr 13, 2013.
    123 changes: 123 additions & 0 deletions Graph.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,123 @@
    package us.brtm.cfg;

    import org.objectweb.asm.tree.AbstractInsnNode;
    import org.objectweb.asm.tree.LabelNode;
    import org.objectweb.asm.tree.MethodNode;
    import org.objectweb.asm.tree.analysis.*;
    import us.brtm.cfg.generic.Node;

    /**
    * Class that represents a Control Flow Graph constructed for a specified method.
    * Useful for static analysis of applications and/or compiler optimizations.
    *
    * @author Pedro Daia Cardoso
    */
    public final class Graph {
    /**
    * The representation of the analyzed method.
    */
    private MethodNode method;
    /**
    * The analyzer instance of an analyzed method :P
    */
    private Analyzer analyzer;
    /**
    * All frames -- representing a block -- in the method.
    */
    private Frame[] frames;
    /**
    * The first or opening frame or node in the graph.
    */
    private Frame entryBlock;

    /**
    * @param owner the internal name of the class to which the method belongs.
    * @param method the method to be analyzed.
    * @param debug debug flag
    * @throws AnalyzerException if a problem occurs during the analysis.
    */
    public Graph(String owner, MethodNode method, boolean debug) throws AnalyzerException {
    if (debug) {
    System.out.println("Starting to build graph for " + owner + "." + method.name + "()");
    }
    this.method = method;
    this.analyzer = new Analyzer<BasicValue>(new BasicInterpreter()) {
    protected Frame<BasicValue> newFrame(int nLocals, int nStack) {
    return new Node(nLocals, nStack);
    }

    protected void newControlFlowEdge(int src, int dst) {
    Node s = (Node) getFrames()[src];
    s.getSuccessors().add((Node) getFrames()[dst]);
    }
    };
    analyzer.analyze(owner, method);
    int totalInsns = method.instructions.size();
    Frame entryBlock = locateEntryBlock();
    if (entryBlock == null && debug) {
    System.out.println("Entry block for the graph's null. Fail.");
    }
    this.entryBlock = entryBlock;
    optimizeMethod();
    if (debug) {
    System.out.println("Built graph for " + owner + "." + method.name + "().");
    System.out.println("Cyclomatic complexity: " + getCyclomaticComplexity());
    System.out.println("Optimized " + (totalInsns - method.instructions.size()) + " instructions.");
    }
    }

    public Frame locateEntryBlock() {
    for (Frame frame : frames) {
    if (((Node) frame).getSuccessors().size() > 0) {
    continue;
    }
    return frame;
    }
    return null;
    }

    /**
    * Optimizes the method by removing useless things and/or
    * simplifying the code.
    */
    public void optimizeMethod() {
    AbstractInsnNode[] instructions = method.instructions.toArray();
    for (int i = 0; i < frames.length; i++) {
    AbstractInsnNode instruction = instructions[i];
    if (frames[i] == null && !(instruction instanceof LabelNode)) {
    method.instructions.remove(instructions[i]);
    }
    }
    }

    /**
    * A simple method to calculate the cyclomatic complexity of a method. This metric is defined
    * as the number of edges in the control flow graph, minus the number of nodes, plus two.
    * The metric gives a good indication if the complexity of a method, which have a
    * relation with the average amount of bugs in the method.
    *
    * @return the cyclomatic complexity of this graph's method.
    */
    public int getCyclomaticComplexity() {
    int edges = 0;
    int nodes = 0;
    for (Frame frame : frames) {
    if (frame != null) {
    edges += ((Node) frame).getSuccessors().size();
    nodes += 1;
    }
    }
    return edges - nodes + 2;
    }

    /**
    * @return returns the analyzer instance
    */
    public Analyzer getAnalyzer() {
    return analyzer;
    }

    public Frame getEntryBlock() {
    return entryBlock;
    }
    }