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

com.google.javascript.jscomp.DotFormatter Maven / Gradle / Ivy

/*
 * Copyright 2007 The Closure Compiler Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.javascript.jscomp;

import com.google.javascript.jscomp.ControlFlowGraph.Branch;
import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge;
import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode;
import com.google.javascript.jscomp.graph.GraphvizGraph;
import com.google.javascript.jscomp.graph.GraphvizGraph.GraphvizEdge;
import com.google.javascript.jscomp.graph.GraphvizGraph.GraphvizNode;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.jstype.JSType;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import org.jspecify.nullness.Nullable;

/**
 * 

DotFormatter prints out a dot file of the Abstract Syntax Tree. * For a detailed description of the dot format and visualization tool refer * to Graphviz.

*

Typical usage of this class

* System.out.println(new DotFormatter().toDot(node)); *

This class is not thread safe and should not be used without proper * external synchronization.

*/ public final class DotFormatter { private static final String INDENT = " "; private static final String ARROW = " -> "; private static final String LINE = " -- "; private static final int MAX_LABEL_NAME_LENGTH = 10; // stores the current assignment of node to keys private final HashMap assignments = new HashMap<>(); // key count in order to assign a unique key to each node private int keyCount = 0; // the builder used to generate the dot diagram private final Appendable builder; private final @Nullable ControlFlowGraph cfg; private final boolean printAnnotations; /** For Testing Only */ private DotFormatter() { this.builder = new StringBuilder(); this.cfg = null; this.printAnnotations = false; } private DotFormatter(Node n, ControlFlowGraph cfg, Appendable builder, boolean printAnnotations) throws IOException { this.cfg = cfg; this.builder = builder; this.printAnnotations = printAnnotations; formatPreamble(); traverseNodes(n); formatConclusion(); } /** * Converts an AST to dot representation. * @param n the root of the AST described in the dot formatted string * @return the dot representation of the AST */ public static String toDot(Node n) throws IOException { return toDot(n, null); } /** * Converts an AST to dot representation. * * @param n the root of the AST described in the dot formatted string * @param inCFG Control Flow Graph. * @return the dot representation of the AST */ static String toDot(Node n, @Nullable ControlFlowGraph inCFG) throws IOException { StringBuilder builder = new StringBuilder(); DotFormatter unused = new DotFormatter(n, inCFG, builder, false); return builder.toString(); } /** * Outputs a string in DOT format that presents the graph. * * @param graph Input graph. * @return A string in Dot format that presents the graph. */ public static String toDot(GraphvizGraph graph) { StringBuilder builder = new StringBuilder(); builder.append(graph.isDirected() ? "digraph" : "graph"); builder.append(INDENT); builder.append(graph.getName()); builder.append(" {\n"); builder.append(INDENT); builder.append("node [color=lightblue2, style=filled];\n"); final String edgeSymbol = graph.isDirected() ? ARROW : LINE; List nodes = graph.getGraphvizNodes(); String[] nodeNames = new String[nodes.size()]; for (int i = 0; i < nodeNames.length; i++) { GraphvizNode gNode = nodes.get(i); nodeNames[i] = gNode.getId() + " [label=\"" + gNode.getLabel() + "\" color=\"" + gNode.getColor() + "\"]"; } // We sort the nodes so we get a deterministic output every time regardless // of the implementation of the graph data structure. Arrays.sort(nodeNames); for (String nodeName : nodeNames) { builder.append(INDENT); builder.append(nodeName); builder.append(";\n"); } List edges = graph.getGraphvizEdges(); String[] edgeNames = new String[edges.size()]; for (int i = 0; i < edgeNames.length; i++) { GraphvizEdge edge = edges.get(i); edgeNames[i] = edge.getNode1Id() + edgeSymbol + edge.getNode2Id(); } // Again, we sort the edges as well. Arrays.sort(edgeNames); for (String edgeName : edgeNames) { builder.append(INDENT); builder.append(edgeName); builder.append(";\n"); } builder.append("}\n"); return builder.toString(); } /** * Converts an AST to dot representation and appends it to the given buffer. * @param n the root of the AST described in the dot formatted string * @param inCFG Control Flow Graph. * @param builder A place to dump the graph. */ static void appendDot(Node n, ControlFlowGraph inCFG, Appendable builder) throws IOException { DotFormatter unused = new DotFormatter(n, inCFG, builder, false); } /** * Creates a DotFormatter purely for testing DotFormatter's internal methods. */ static DotFormatter newInstanceForTesting() { return new DotFormatter(); } private void traverseNodes(Node parent) throws IOException { // key int keyParent = key(parent); // edges for (Node child = parent.getFirstChild(); child != null; child = child.getNext()) { int keyChild = key(child); builder.append(INDENT); builder.append(formatNodeName(keyParent)); builder.append(ARROW); builder.append(formatNodeName(keyChild)); builder.append(" [weight=1];\n"); traverseNodes(child); } // Flow Edges if (cfg != null && cfg.hasNode(parent)) { List> outEdges = cfg.getOutEdges(parent); String[] edgeList = new String[outEdges.size()]; for (int i = 0; i < edgeList.length; i++) { DiGraphEdge edge = outEdges.get(i); DiGraphNode succ = edge.getDestination(); String toNode = null; if (succ == cfg.getImplicitReturn()) { toNode = "RETURN"; } else { int keySucc = key(succ.getValue()); toNode = formatNodeName(keySucc); } edgeList[i] = formatNodeName(keyParent) + ARROW + toNode + " [label=\"" + edge.getValue() + "\", " + "fontcolor=\"red\", " + "weight=0.01, color=\"red\"];\n"; } Arrays.sort(edgeList); for (String element : edgeList) { builder.append(INDENT); builder.append(element); } } } int key(Node n) throws IOException { Integer key = assignments.get(n); if (key == null) { key = keyCount++; assignments.put(n, key); builder.append(INDENT); builder.append(formatNodeName(key)); builder.append(" [label=\""); builder.append(n.getToken().toString()); if (n.isName() || n.isStringLit() || n.isGetProp() || n.isImportStar() || n.isStringKey()) { builder.append(getNodeLabel(n)); } JSType type = n.getJSType(); if (type != null) { builder.append(" : "); builder.append(type.toString()); } if (printAnnotations && cfg != null && cfg.hasNode(n)) { Object annotation = cfg.getNode(n).getAnnotation(); if (annotation != null) { builder.append("\\n"); builder.append(annotation.toString()); } } builder.append("\""); if (n.getJSDocInfo() != null) { builder.append(" color=\"green\""); } builder.append("];\n"); } return key; } private static String getNodeLabel(Node n) { String content = n.getString(); if (content.length() > MAX_LABEL_NAME_LENGTH) { content = content.substring(0, MAX_LABEL_NAME_LENGTH); } if (!content.isEmpty()) { content = "(" + content + ")"; } return content; } private static String formatNodeName(Integer key) { return "node" + key; } private void formatPreamble() throws IOException { builder.append("digraph AST {\n"); builder.append(INDENT); builder.append("node [color=lightblue2, style=filled];\n"); } private void formatConclusion() throws IOException { builder.append("}\n"); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy