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

org.jetbrains.java.decompiler.util.DotExporter Maven / Gradle / Ivy

Go to download

Modern Java & JVM language decompiler aiming to be as accurate as possible, with an emphasis on output quality.

The newest version!
package org.jetbrains.java.decompiler.util;

import org.jetbrains.java.decompiler.code.cfg.BasicBlock;
import org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph;
import org.jetbrains.java.decompiler.code.cfg.ExceptionRangeCFG;
import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.main.rels.DecompileRecord;
import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
import org.jetbrains.java.decompiler.modules.decompiler.StatEdge;
import org.jetbrains.java.decompiler.modules.decompiler.decompose.DominatorEngine;
import org.jetbrains.java.decompiler.modules.decompiler.flow.DirectEdge;
import org.jetbrains.java.decompiler.modules.decompiler.flow.DirectEdgeType;
import org.jetbrains.java.decompiler.modules.decompiler.flow.DirectGraph;
import org.jetbrains.java.decompiler.modules.decompiler.flow.DirectNode;
import org.jetbrains.java.decompiler.modules.decompiler.stats.*;
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionNode;
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionsGraph;
import org.jetbrains.java.decompiler.struct.StructMethod;
import org.jetbrains.java.decompiler.util.collections.FastSparseSetFactory.FastSparseSet;
import org.jetbrains.java.decompiler.util.collections.SFormsFastMapDirect;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.util.*;
import java.util.Map.Entry;

public class DotExporter {
  private static final String DOTS_FOLDER = System.getProperty("DOT_EXPORT_DIR", null);
  private static final String DOTS_ERROR_FOLDER = System.getProperty("DOT_ERROR_EXPORT_DIR", null);
  public static final boolean DUMP_DOTS = DOTS_FOLDER != null && !DOTS_FOLDER.trim().isEmpty();
  public static final boolean DUMP_ERROR_DOTS = DOTS_ERROR_FOLDER != null && !DOTS_ERROR_FOLDER.trim().isEmpty();

  private static final boolean EXTENDED_MODE = false;
  private static final boolean STATEMENT_LR_MODE = false;
  private static final boolean SAME_RANK_MODE = false;
  // http://graphs.grevian.org/graph is a nice visualizer for the outputed dots.

  // Outputs a statement and as much of its information as possible into a dot formatted string.
  // Nodes represent statements, their id, their type, and their code.
  // Black arrows represent a statement's successors.
  // Dotted arrows represent a statement's exception successors.
  // Blue arrows represent a statement's predecessors. The arrow points towards the predecessor.
  // Red arrows represent the statement tree. The arrows points to the statement's children.
  // Black arrows with a diamond head represent a statement's closure, or the statement that it's enclosed in.
  // Purple arrows with a bar head represent a statement's neighbors. TODO: needs backwards as well
  // Statements with no successors or predecessors (but still contained in the tree) will be in a subgraph titled "Isolated statements".
  // Statements that aren't found will be circular, and will have a message stating so.
  // Nodes with green borders are the canonical exit of method, but these may not always be emitted.

  private static String statToDot(Statement stat, String name) {
    try (var lock = DecompilerContext.getImportCollector().lock()) {
      return statToDot(stat, name, null);
    }
  }

  private static String statToDot(Statement stat, String name, Map extraProps) {
    StringBuffer buffer = new StringBuffer();
    // List subgraph = new ArrayList<>();
    Set visitedNodes = new HashSet<>();
    Set exits = new HashSet<>();
    Set referenced = new HashSet<>();

    buffer.append("digraph " + name + " {\r\n");

    if (STATEMENT_LR_MODE) {
      buffer.append("  rankdir = LR;\r\n");
    }

    List stats = new ArrayList<>();
    stats.add(stat);
    findAllStats(stats, stat);

    DummyExitStatement exit = null;
    if (stat instanceof RootStatement) {
      exit = ((RootStatement)stat).getDummyExit();
    }

    // Pre process
    Map extraData = new HashMap<>();
    Set extraDataSeen = new HashSet<>();

    for (Statement st : stats) {
      if (st instanceof IfStatement) {
        IfStatement ifs = (IfStatement) st;

        if (ifs.getIfEdge() != null) {
          extraData.put(ifs.getIfEdge(), "If Edge");
        }

        if (ifs.getElseEdge() != null) {
          extraData.put(ifs.getElseEdge(), "Else Edge");
        }
      }
      if (SAME_RANK_MODE && st.getStats().size() > 1){
        buffer.append(" subgraph { rank = same; ");
        for (Statement s : st.getStats()) {
          if (st instanceof IfStatement || st instanceof SwitchStatement) {
            if (s == st.getFirst()){
              continue;
            }
          }

          buffer.append(s.id + "; ");
        }
        buffer.append("}\r\n");
      }
    }

    for(Statement st : stats) {
      String sourceId = st.id + (st.getSuccessorEdges(StatEdge.TYPE_EXCEPTION).isEmpty()?"":"000000");

      boolean edges = false;
      for(StatEdge edge : st.getSuccessorEdges(Statement.STATEDGE_ALL)) {
        String destId = edge.getDestination().id + (edge.getDestination().getSuccessorEdges(StatEdge.TYPE_EXCEPTION).isEmpty()?"":"000000");

        String edgeType = getEdgeType(edge);
        String meta = getEdgeMeta(edge);

        // Add extra edge data
        // TODO do same for predecessors?
        for (Entry entry : extraData.entrySet()) {
          if (edge.getSource().id == entry.getKey().getSource().id && edge.getDestination().id == entry.getKey().getDestination().id) {
            edgeType = edgeType == null ? entry.getValue() : edgeType + " (" + entry.getValue() + ")";
            extraDataSeen.add(entry.getKey());
          }
        }

        if (edge.closure != null ) {
          edgeType = edgeType == null ? "Closure: " + edge.closure.id : edgeType + " (Closure: " + edge.closure.id + ")";
        }

        buffer.append(sourceId + "->" + destId + (edgeType != null ? "[label=\"" + edgeType + "\", " + meta + "]" : "[" + meta + "]") + ";\n");

        if (EXTENDED_MODE && edge.closure != null) {
          String clsId = edge.closure.id + (edge.getDestination().getSuccessorEdges(StatEdge.TYPE_EXCEPTION).isEmpty()?"":"000000");
          buffer.append(sourceId + "->" + clsId + " [arrowhead=diamond,label=\"Closure\"];\r\n");
        }

        if (edge.getType() == StatEdge.TYPE_FINALLYEXIT || edge.getType() == StatEdge.TYPE_BREAK) {
          exits.add(edge.getDestination().id);
        }

        referenced.add(edge.getDestination().id);

        edges = true;
      }

      if(EXTENDED_MODE) {
        for (StatEdge labelEdge : st.getLabelEdges()) {
          String src = labelEdge.getSource().id + (labelEdge.getSource().getSuccessorEdges(StatEdge.TYPE_EXCEPTION).isEmpty() ? "" : "000000");
          String destId = labelEdge.getDestination().id + (labelEdge.getDestination().getSuccessorEdges(StatEdge.TYPE_EXCEPTION).isEmpty() ? "" : "000000");
          String data = "";
          if (labelEdge.labeled) {
            data += "Labeled";
          }
          if (labelEdge.labeled && labelEdge.explicit) {
            data += ", ";
          }
          if (labelEdge.explicit) {
            data += "Explicit";
          }
          buffer.append(src + "->" + destId + " [color=orange,label=\"Label Edge (" + data + ") (Contained by " + st.id + ")\"];\r\n");
        }
      }

      // Neighbor set is redundant
//      for (Statement neighbour : st.getNeighbours(Statement.STATEDGE_ALL, Statement.DIRECTION_FORWARD)) {
//        String destId = neighbour.id + (neighbour.getSuccessorEdges(StatEdge.TYPE_EXCEPTION).isEmpty()?"":"000000");
//        buffer.append(sourceId + "->" + destId + " [arrowhead=tee,color=purple];\r\n");
//      }

      if (EXTENDED_MODE) {
        for(StatEdge edge : st.getPredecessorEdges(Statement.STATEDGE_ALL)) {
          String destId = edge.getSource().id + (edge.getSource().getSuccessorEdges(StatEdge.TYPE_EXCEPTION).isEmpty() ? "" : "000000");

          String edgeType = getEdgeType(edge);

          buffer.append(sourceId + "->" + destId + "[color=blue" + (edgeType != null ? ",fontcolor=blue,label=\"" + edgeType + "\"" : "") + "];\r\n");

          referenced.add(edge.getSource().id);

          edges = true;
        }
      }

      for(StatEdge edge : st.getSuccessorEdges(StatEdge.TYPE_EXCEPTION)) {
        String destId = edge.getDestination().id + (edge.getDestination().getSuccessorEdges(StatEdge.TYPE_EXCEPTION).isEmpty()?"":"000000");

        buffer.append(sourceId + " -> " + destId + " [style=dotted];\r\n");
        referenced.add(edge.getDestination().id);

        edges = true;
      }

      // Special case if edges
      // TODO: add labels onto existing successors

      // Graph tree
      boolean foundFirst = false;
      boolean isIf = st instanceof IfStatement;
      boolean foundIf = false;
      boolean foundElse = false;
      for (Statement s : st.getStats()) {
        String destId = s.id + (s.getSuccessorEdges(StatEdge.TYPE_EXCEPTION).isEmpty()?"":"000000");

        String label = "";

        if (s == st.getFirst()) {
          label = "First";
          foundFirst = true;
        }

        if (st instanceof IfStatement) {
          IfStatement ifs = (IfStatement) st;
          if (s == ifs.getIfstat()) {
            label = "If stat";
            foundIf = true;
          }
          if (s == ifs.getElsestat()) {
            label = "Else stat";
            foundElse = true;
          }
        }

        buffer.append(sourceId + " -> " + destId + " [arrowhead=vee,color=red" + (!label.equals("") ? ",fontcolor=red,label=\"" + label + "\"" : "") + "];\r\n");
        referenced.add(s.id);
      }

      if (!foundFirst && st.getFirst() != null) {
        String destId = st.getFirst().id + (st.getFirst().getSuccessorEdges(StatEdge.TYPE_EXCEPTION).isEmpty()?"":"000000");
        buffer.append(sourceId + "->" + destId + " [arrowhead=vee,color=red,fontcolor=red,label=\"Dangling First statement!\"];\r\n");
      }

      if (isIf) {
        if (!foundIf && ((IfStatement) st).getIfstat() != null) {
          String destId = ((IfStatement) st).getIfstat().id + (st.getFirst().getSuccessorEdges(StatEdge.TYPE_EXCEPTION).isEmpty()?"":"000000");
          buffer.append(sourceId + "->" + destId + " [arrowhead=vee,color=red,fontcolor=red,label=\"Dangling If statement!\"];\r\n");
        }

        if (!foundElse && ((IfStatement) st).getElsestat() != null) {
          String destId = ((IfStatement) st).getElsestat().id + (((IfStatement) st).getElsestat().getSuccessorEdges(StatEdge.TYPE_EXCEPTION).isEmpty()?"":"000000");
          buffer.append(sourceId + "->" + destId + " [arrowhead=vee,color=red,fontcolor=red,label=\"Dangling Else statement!\"];\r\n");
        }
      }

      visitedNodes.add(st.id);

      String extra = extraProps == null || !extraProps.containsKey(st) ? "" : "," + extraProps.get(st);

      String node = sourceId + " [shape=box,label=\"" + st.id + " (" + getStatType(st) + ")\\n" + toJava(st) + "\"" + (st == stat ? ",color=red" : "") + extra + "];\n";
//      if (edges || st == stat) {
        buffer.append(node);
//      } else {
//        subgraph.add(node);
//      }
    }

    // Exits
    if (exit != null) {
      buffer.append(exit.id + " [color=green,label=\"" + exit.id + " (Canonical Return)\"];\n");
      referenced.remove(exit.id);
    }

//    for (Integer exit : exits) {
//      if (!visitedNodes.contains(exit)) {
//        buffer.append(exit + " [color=green,label=\"" + exit + " (Canonical Return)\"];\r\n");
//      }
//
//      referenced.remove(exit);
//    }

    referenced.removeAll(visitedNodes);

    // Unresolved statement references
    for (Integer integer : referenced) {
      buffer.append(integer + " [color=red,label=\"" + integer + " (Unknown statement!)\"];\r\n");
    }

    for (StatEdge labelEdge : extraData.keySet()) {
      if (extraDataSeen.contains(labelEdge)) {
        continue;
      }

      String src = labelEdge.getSource().id + (labelEdge.getSource().getSuccessorEdges(StatEdge.TYPE_EXCEPTION).isEmpty() ? "" : "000000");
      String destId = labelEdge.getDestination().id + (labelEdge.getDestination().getSuccessorEdges(StatEdge.TYPE_EXCEPTION).isEmpty() ? "" : "000000");
      String label = "Floating extra edge: ("  + extraData.get(labelEdge) + ")";

      buffer.append(src + " -> " + destId + " [arrowhead=vee,color=red,fontcolor=red,label=\"" + label + "\"];\r\n");
    }

//    if (subgraph.size() > 0) {
//      buffer.append("subgraph cluster_non_parented {\r\n\tlabel=\"Isolated statements\";\r\n");
//
//      for (String s : subgraph) {
//        buffer.append("\t"+s);
//      }
//
//      buffer.append("\t}\r\n");
//    }

    buffer.append("}");

    return buffer.toString();
  }

  private static String statementHierarchy(Statement stat) {
    StringBuffer buffer = new StringBuffer();

    buffer.append("digraph G {\r\n");

    buffer.append("subgraph cluster_root {\r\n");

    buffer.append(stat.id + " [shape=box,label=\"" + stat.id + " (" + getStatType(stat) + ")\r\n" + toJava(stat) + "\"" + "];\r\n");

    recursivelyGraphStatement(buffer, stat);

    buffer.append("}\r\n");

    buffer.append("}");

    return buffer.toString();
  }

  private static void recursivelyGraphStatement(StringBuffer buffer, Statement statement) {
    buffer.append("subgraph cluster_" + statement.id + " {\r\n");
    buffer.append("label=\"" + statement.id + " (" + getStatType(statement) + ")\r\n" + toJava(statement) + "\"" + ";\r\n");

    for (Statement stat : statement.getStats()) {
      buffer.append(stat.id + " [shape=box,label=\"" + stat.id + " (" + getStatType(stat) + ")\r\n" + toJava(stat) + "\"" + "];\r\n");

      recursivelyGraphStatement(buffer, stat);
    }


    buffer.append("}\r\n");
  }

  private static String toJava(Statement statement) {
    try {
      String java = statement.toJava().convertToStringAndAllowDataDiscard()
        .replace("\"", "\\\"")
        .replace("\r", "")
        .replace("\n", "\\l");
      if (statement instanceof BasicBlockStatement) {
        if (statement.getExprents() == null || statement.getExprents().isEmpty()) {
          java = "<" + (statement.getExprents() == null ? "null" : "empty") + " basic block>\\n" + java;
        }
      }

      return java;
    } catch (Exception e) {
      return "Could not get content";
    }
  }

  private static String getEdgeType(StatEdge edge) {
    switch (edge.getType()) {
      case StatEdge.TYPE_REGULAR: return null;
      case StatEdge.TYPE_EXCEPTION: return "Exception";
      case StatEdge.TYPE_BREAK: return "Break";
      case StatEdge.TYPE_CONTINUE: return "Continue";
      case StatEdge.TYPE_FINALLYEXIT: return "Finally Exit";
      default: return "Unknown Edge (composite?)";
    }
  }

  private static String getEdgeMeta(StatEdge edge) {
    switch (edge.getType()) {
      case StatEdge.TYPE_REGULAR: return "weight=1, color=black";
      case StatEdge.TYPE_EXCEPTION: return "weight=1, color=orange, style=dashed";
      case StatEdge.TYPE_BREAK: return "weight=0.4, color=blue";
      case StatEdge.TYPE_CONTINUE: return "weight=0.2, color=green";
      case StatEdge.TYPE_FINALLYEXIT: return "weight=1, color=orange, style=dotted";
      default: return "weight=1, color=purple";
    }
  }

  private static String getStatType(Statement st) {
    switch (st.type) {
      case GENERAL: return ((GeneralStatement) st).isPlaceholder() ? "General (Placeholder)" : "General";
      case IF: return "If";
      case DO: return "Do";
      case SWITCH: return "Switch";
      case TRY_CATCH: return "Try Catch";
      case BASIC_BLOCK: return "Basic Block #" + ((BasicBlockStatement)st).getBlock().getId();
      case SYNCHRONIZED: return "Synchronized";
      case CATCH_ALL: return "Catch All";
      case ROOT: return "Root";
      case DUMMY_EXIT: return "Dummy Exit";
      case SEQUENCE: return "Sequence";
      default: return "Unknown";
    }
  }

  private static void findAllStats(List list, Statement root) {
    for (Statement stat : root.getStats()) {
      if (!list.contains(stat)) {
        list.add(stat);
      }

      if (stat instanceof IfStatement) {
        IfStatement ifs = (IfStatement) stat;

        if (ifs.getIfstat() != null && !list.contains(ifs.getIfstat())) {
          list.add(ifs.getIfstat());
        }

        if (ifs.getElsestat() != null && !list.contains(ifs.getElsestat())) {
          list.add(ifs.getElsestat());
        }
      }

      findAllStats(list, stat);
    }
  }


  private static String cfgToDot(String name, ControlFlowGraph graph, boolean showMultipleEdges) {

    StringBuffer buffer = new StringBuffer();

    buffer.append("digraph " + name + " {\r\n");

    List blocks = graph.getBlocks();
    for (BasicBlock block : blocks) {
      buffer.append(block.getId() + " [shape=box,label=\"Block " + block.getId() + "\n" + block.getSeq() + "\"];\r\n");

      List suc = block.getSuccs();
      List preds = block.getPreds();

//      if(!showMultipleEdges) {
//        HashSet set = new HashSet<>();
//        set.addAll(suc);
//        suc = Collections.list(Collections.enumeration(set));
//      }

      for (BasicBlock basicBlock : suc) {
        buffer.append(block.getId() + " -> " + basicBlock.getId() + ";\r\n");
      }

//      for (BasicBlock pred : preds) {
//        buffer.append(block.getDebugId() + " -> " + pred.getDebugId() + " [color=blue];\r\n");
//      }

      suc = block.getSuccExceptions();
      preds = block.getPredExceptions();

//      if(!showMultipleEdges) {
//        HashSet set = new HashSet<>();
//        set.addAll(suc);
//        suc = Collections.list(Collections.enumeration(set));
//      }

      for (int j = 0; j < suc.size(); j++) {
        buffer.append(block.getId() + " -> " + suc.get(j).getId() + " [style=dotted];\r\n");
      }

//      for (BasicBlock pred : preds) {
//        buffer.append(block.getDebugId() + " -> " + pred.getDebugId() + " [color=blue,style=dotted];\r\n");
//      }
    }

    for (int i = 0; i < graph.getExceptions().size(); i++) {
      ExceptionRangeCFG ex = graph.getExceptions().get(i);
      buffer.append("subgraph cluster_ex_" + i + " {\r\n\tlabel=\"Exception range for Block " + ex.getHandler().getId() + " \";\r\n");
      for (BasicBlock bb : ex.getProtectedRange()) {
        buffer.append("\t" + bb.getId() + ";\r\n");
      }
      buffer.append("\t}\r\n");
    }

    buffer.append("}");

    return buffer.toString();
  }

  private static String decompileRecordToDot(DecompileRecord decompileRecord) {
    StringBuilder builder = new StringBuilder();

    List names = decompileRecord.getNames();
    int size = names.size();

    builder.append("digraph decompileRecord {\n");

    for (int i = 0; i < size; i++) {
      builder.append("\t").append(i).append(" [label=\"").append(names.get(i)).append("\"];\n");
    }

    builder.append("\n");

    for (int i = 0; i < size - 1; i++) {
      builder.append("\t").append(i).append(" -> ").append(i + 1).append(";\n");
    }

    builder.append("}");

    return builder.toString();
  }
  private static String varsToDot(VarVersionsGraph graph, Map varAssignmentMap) {
    StringBuilder builder = new StringBuilder();

    builder.append("digraph G {\r\n");

    for (VarVersionNode node : graph.nodes) {
      appendNode(builder, node);

      if (node.state == null) {
        builder.append(" [shape=box, style=dashed, ");
      } else {
        switch (node.state) {
          case PHI:
            builder.append(" [shape=oval,");
            break;
          case READ:
            builder.append(" [shape=box, ");
            break;
          case WRITE:
            builder.append(" [shape=box, style=filled, fillcolor=palegreen1, ");
            break;
          case PHANTOM:
            builder.append(" [shape=box; style=dotted, ");
            break;
          case DEAD_READ:
            builder.append(" [shape=box, style=filled, fillcolor=grey59, ");
            break;
          case PARAM:
            builder.append(" [shape=box, style=filled, fillcolor=skyblue1, ");
            break;
          case CATCH:
            builder.append(" [shape=box, style=filled, fillcolor=gold, ");
            break;
        }
      }

      builder.append("label=\"").append(node.var).append("_").append(node.version).append("\"];\r\n");

      for (VarVersionNode dest : node.successors) {
        appendNode(builder, node);
        builder.append("->");
        appendNode(builder, dest);
        builder.append(";\r\n");
      }

      if (node.phantomNode != null) {
        appendNode(builder, node);
        builder.append("->");
        appendNode(builder, node.phantomNode);
        builder.append(" [style=dotted];\r\n");
      }
    }

    if (varAssignmentMap != null) {
      for (Entry entry : varAssignmentMap.entrySet()) {
        VarVersionPair to = entry.getKey();
        VarVersionPair from = entry.getValue();
        appendNode(builder, from);
        builder.append("->");
        appendNode(builder, to);
        builder.append(" [color=green];\r\n");
      }
    }

    builder.append("}");

    return builder.toString();
  }

  private static void appendNode(StringBuilder builder, VarVersionNode node) {
    appendNode(builder, node.asPair());
  }

  private static void appendNode(StringBuilder builder, VarVersionPair pair) {
    if (pair.var >= 0) {
      builder.append("var").append(pair.var).append("_").append(pair.version);
    } else {
      builder.append("varM").append(-pair.var).append("_").append(pair.version);
    }
  }

  private static String domEngineToDot(DominatorEngine doms) {
    StringBuilder builder = new StringBuilder();

    builder.append("digraph G {\r\n");

    Set nodes = new HashSet<>();

    for (Integer key : doms.getOrderedIDoms().getLstKeys()) {
      nodes.add(key);
      nodes.add(doms.getOrderedIDoms().getWithKey(key));
      builder.append("x" + doms.getOrderedIDoms().getWithKey(key) + " -> x" + key + ";\n");
    }

    for (Integer nd : nodes) {
      builder.append("x" + nd + "[label=\"" + nd + "\"];\n");
    }

    builder.append("}");

    return builder.toString();
  }

  private static String digraphToDot(DirectGraph graph, Map vars) {
    StringBuffer buffer = new StringBuffer();

    buffer.append("digraph G {\r\n");

    List blocks = graph.nodes;
    for (DirectNode block : blocks) {
      StringBuilder label = new StringBuilder(block.id + " in statement " + block.statement.id + " " + getStatType(block.statement));
      label.append("\\n");
      label.append(block.block != null ? toJava(block.block) : "null block");
      if (block.block == null) {
        TextBuffer buf = ExprProcessor.listToJava(block.exprents, 0);
        label.append("\\n");
        label.append(buf.convertToStringAndAllowDataDiscard());
      }
      if (vars != null && vars.containsKey(block.id)) {
        SFormsFastMapDirect map = vars.get(block.id);

        List>> lst = map.entryList();
        if (lst != null) {
          for (Entry> entry : lst) {
            label.append("\\n").append(entry.getKey());
            Set set = entry.getValue().toPlainSet();
            label.append("=").append(set);
          }
        }
      }

      buffer.append("x" + (block.id)+" [shape=box,label=\""+label+"\"];\r\n");

      for (DirectEdgeType type : DirectEdgeType.TYPES) {
        for(DirectEdge dest : block.getSuccessors(type)) {
          buffer.append("x" + (block.id)+" -> x"+(dest.getDestination().id)+ (type == DirectEdgeType.EXCEPTION ? "[style=dotted]" : "") + ";\r\n");
        }
      }

      if (block.tryFinally != null) {
        buffer.append("x" + (block.id)+" -> x"+(block.tryFinally.id) + "[color=blue];\r\n");
      }
    }

    buffer.append("}");

    return buffer.toString();
  }

  private static File getFile(String folder, StructMethod mt, String suffix) {
    return getFile(folder, mt, "", suffix);
  }

  private static File getFile(String folder, StructMethod mt, String subdirectory, String suffix) {
    File root = new File(folder + mt.getClassQualifiedName() + (subdirectory.isEmpty() ? "" : "/" + subdirectory));
    if (!root.isDirectory()) {
      root.mkdirs();
    }

    return new File(root,
      mt.getName().replace('<', '.').replace('>', '_') +
        mt.getDescriptor().replace('/', '.') +
        '_' + suffix + ".dot");
  }

  private static File getFile(String folder, String name) {
    File root = new File(folder);
    if (!root.isDirectory())
      root.mkdirs();
    return new File(root,name + ".dot");
  }

  public static void toDotFile(DirectGraph dgraph, StructMethod mt, String suffix) {
    toDotFile(dgraph, mt, suffix, null);
  }

  public static void toDotFile(DirectGraph dgraph, StructMethod mt, String suffix, Map vars) {
    if (!DUMP_DOTS)
      return;
    try{
      BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(getFile(DOTS_FOLDER, mt, suffix)));
      try (var lock = DecompilerContext.getImportCollector().lock()) {
        out.write(digraphToDot(dgraph, vars).getBytes());
      }
      out.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public static void errorToDotFile(DirectGraph dgraph, StructMethod mt, String suffix) {
    errorToDotFile(dgraph, mt, suffix, null);
  }

  public static void errorToDotFile(DirectGraph dgraph, StructMethod mt, String suffix, Map vars) {
    if (!DUMP_ERROR_DOTS)
      return;
    try{
      BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(getFile(DOTS_ERROR_FOLDER, mt, suffix)));
      try (var lock = DecompilerContext.getImportCollector().lock()) {
        out.write(digraphToDot(dgraph, vars).getBytes());
      }
      out.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public static void toDotFile(Statement stat, StructMethod mt, String suffix) {
    toDotFile(stat, mt, "", suffix);
  }

  public static void toDotFile(Statement stat, StructMethod mt, String subdirectory, String suffix) {
    toDotFile(stat, mt, subdirectory, suffix, null);
  }

  public static void toDotFile(Statement stat, StructMethod mt, String subdirectory, String suffix, Map extraProps) {
    if (!DUMP_DOTS)
      return;
    try{
      BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(getFile(DOTS_FOLDER, mt, subdirectory, suffix)));
      try (var lock = DecompilerContext.getImportCollector().lock()) {
        out.write(statToDot(stat, suffix, extraProps).getBytes());
      }
      out.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public static void statementHierarchyToDot(Statement stat, StructMethod mt, String suffix) {
    if (!DUMP_DOTS)
      return;
    try{
      BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(getFile(DOTS_FOLDER, mt, suffix)));
      out.write(statementHierarchy(stat).getBytes());
      out.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public static void toDotFile(Statement stat, String name) {
    if (!DUMP_DOTS)
      return;
    try{
      BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(getFile(DOTS_FOLDER, name)));
      out.write(statToDot(stat, name).getBytes());
      out.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public static void errorToDotFile(Statement stat, StructMethod mt, String suffix) {
    if (!DUMP_ERROR_DOTS)
      return;
    try{
      BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(getFile(DOTS_ERROR_FOLDER, mt, suffix)));
      out.write(statToDot(stat, suffix).getBytes());
      out.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public static void errorToDotFile(Statement stat, String name) {
    if (!DUMP_ERROR_DOTS)
      return;
    try {
      BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(getFile(DOTS_ERROR_FOLDER, name)));
      out.write(statToDot(stat, name).getBytes());
      out.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public static void toDotFile(VarVersionsGraph graph, StructMethod mt, String suffix, HashMap varAssignmentMap) {
    if (!DUMP_DOTS)
      return;
    try{
      BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(getFile(DOTS_FOLDER, mt, suffix)));
      out.write(varsToDot(graph, varAssignmentMap).getBytes());
      out.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public static void errorToDotFile(VarVersionsGraph graph, StructMethod mt, String suffix, Map varAssignmentMap) {
    if (!DUMP_ERROR_DOTS)
      return;
    try{
      BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(getFile(DOTS_ERROR_FOLDER, mt, suffix)));
      out.write(varsToDot(graph, varAssignmentMap).getBytes());
      out.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public static void toDotFile(DecompileRecord decompileRecord, StructMethod mt, String suffix, boolean error) {
    if (error) {
      if (!DUMP_ERROR_DOTS) {
        return;
      }
    } else {
      if (!DUMP_DOTS) {
        return;
      }
    }

    try {
      BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(getFile(error ? DOTS_ERROR_FOLDER : DOTS_FOLDER, mt, suffix)));
      out.write(decompileRecordToDot(decompileRecord).getBytes());
      out.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public static void toDotFile(ControlFlowGraph graph, StructMethod mt, String suffix, boolean showMultipleEdges) {
    if (!DUMP_DOTS)
      return;
    try{
      BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(getFile(DOTS_FOLDER, mt, suffix)));
      out.write(cfgToDot(suffix, graph, showMultipleEdges).getBytes());
      out.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public static void errorToDotFile(ControlFlowGraph graph, StructMethod mt, String suffix) {
    if (!DUMP_ERROR_DOTS)
      return;
    try{
      BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(getFile(DOTS_ERROR_FOLDER, mt, suffix)));
      out.write(cfgToDot(suffix, graph, true).getBytes());
      out.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public static void toDotFile(DominatorEngine doms, StructMethod mt, String suffix) {
    if (!DUMP_DOTS)
      return;
    try{
      BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(getFile(DOTS_FOLDER, mt, suffix)));
      out.write(domEngineToDot(doms).getBytes());
      out.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy