
com.google.javascript.jscomp.NameReferenceGraphReport Maven / Gradle / Ivy
/*
* Copyright 2009 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.common.base.Preconditions;
import com.google.common.collect.Iterables;
import com.google.javascript.jscomp.NameReferenceGraph.Name;
import com.google.javascript.jscomp.NameReferenceGraph.Reference;
import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge;
import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.jstype.JSType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* Generate a nice HTML file describing the name reference graph.
* For each declaration, list the sites where the declaration's name
* is referenced, and list all the names that the declaration references.
* For each, name exactly where use occurs in the source code.
*
* This report should be useful both for internal compiler
* developers and for engineers trying to understand running behavior
* of their code or who want to understand why the compiler won't
* move their code into a new module.
*
* @author [email protected] (Robert Bowdidge)
*/
final class NameReferenceGraphReport {
private NameReferenceGraph graph = null;
/**
* Create a NameReferenceGraphReport object.
*
* @param g name reference graph to describe in report.
*/
NameReferenceGraphReport(NameReferenceGraph g) {
this.graph = g;
}
/**
* Generate a nice HTML file describing the name reference graph.
* For each declaration, list the sites where the declaration's name
* is referenced, and list all the names that the declaration references.
* For each, name exactly where use occurs in the source code.
*
*
This report should be useful both for internal compiler
* developers and for engineers trying to understand running
* behavior of their code or who want to understand why
* AbstractCompiler won't move their code into a new module.
*
* @return String containing the entire HTML for the report.
*/
public String getHtmlReport() {
StringBuilder builder = new StringBuilder();
List> nodes = new ArrayList<>();
Iterables.addAll(nodes, graph.getDirectedGraphNodes());
generateHtmlReportHeader(builder);
builder.append("Name Reference Graph Dump
\n");
builder.append("OVERALL STATS\n");
builder.append("\n");
builder.append("- Total names: " + nodes.size());
builder.append("
\n");
builder.append("ALL NAMES\n");
builder.append("\n");
// Sort declarations in alphabetical order.
Collections.sort(nodes, new DiGraphNodeComparator());
for (DiGraphNode n : nodes) {
// Generate the HTML describing the declaration itself.
generateDeclarationReport(builder, n);
// Next, list the places where this name is used (REFERS TO), and the
// names that this declaration refers to (REFERENCED BY).
List> outEdges =
graph.getOutEdges(n.getValue());
List> inEdges =
graph.getInEdges(n.getValue());
// Don't bother to create the dotted list if we don't have anything to
// put in it.
if (!outEdges.isEmpty() || !inEdges.isEmpty()) {
builder.append("");
if (!outEdges.isEmpty()) {
builder.append("- REFERS TO:
\n");
builder.append("");
for (DiGraphEdge edge : outEdges) {
generateEdgeReport(builder, edge.getDestination().getValue(),
edge);
}
builder.append("
\n");
}
if (!inEdges.isEmpty()) {
builder.append(" - REFERENCED BY:
\n");
builder.append("");
for (DiGraphEdge edge : inEdges) {
generateEdgeReport(builder, edge.getSource().getValue(), edge);
}
builder.append("
");
}
builder.append("
\n");
}
}
builder.append("
\n");
generateHtmlReportFooter(builder);
return builder.toString();
}
/**
* Given a node, find the name of the containing source file.
*
* @param node Parse tree node whose filename is requested
* @return String containing name of source file, or empty string if name
* cannot be identified.
*/
private static String getSourceFile(Node node) {
String filename = node.getSourceFileName();
if (filename == null) {
return "";
}
return filename;
}
/**
* Generate the HTML for describing a specific declaration.
* @param builder contents of report to be generated
* @param declarationNode declaration to describe
*/
private void generateDeclarationReport(StringBuilder builder,
DiGraphNode declarationNode) {
// Provide the name and location of declaration,
// with an anchor to allow navigation to this declaration.
String declName = declarationNode.getValue().getQualifiedName();
JSType declType = declarationNode.getValue().getType();
builder.append(" ");
builder.append("");
builder.append(declName);
builder.append("\n");
// Provide the type of the declaration.
// This is helpful for debugging.
generateType(builder, declType);
// List all the definitions of this name that were found in the code.
// For each, list
List defs =
declarationNode.getValue().getDeclarations();
if (defs.isEmpty()) {
builder.append("
No definitions found
");
} else {
// Otherwise, provide a list of definitions in a dotted list.
// For each definition, print the location where that definition is
// found.
builder.append("");
for (DefinitionsRemover.Definition def : defs) {
Node fnDef = def.getRValue();
String sourceFileName = getSourceFile(fnDef);
builder.append("- Defined: ");
generateSourceReferenceLink(builder,
sourceFileName, fnDef.getLineno(), fnDef.getCharno());
}
builder.append("
");
}
}
/**
* Generate the HTML header for the report style.
* Borrowed straight from NameAnalyzer's report style.
*
* @param builder contents of the report to be generated
*/
private static void generateHtmlReportHeader(StringBuilder builder) {
builder.append("\n" +
"" +
"" +
"" +
"Name Reference Graph Dump " +
"\n");
}
/**
* Generate the HTML footer for the report style.
*/
private static void generateHtmlReportFooter(StringBuilder builder) {
builder.append("");
}
/**
* Generate a description of a specific edge between two nodes.
* For each edge, name the element being linked, the location of the
* reference in the source file, and the type of the reference.
*
* @param builder contents of the report to be generated
* @param referencedDecl name of the declaration being referenced
* @param edge the graph edge being described
*/
private void generateEdgeReport(StringBuilder builder,
Name referencedDecl, DiGraphEdge edge) {
String srcDeclName = referencedDecl.getQualifiedName();
builder.append("");
builder.append(srcDeclName);
builder.append(" ");
Node def = edge.getValue().getSite();
int lineNumber = def.getLineno();
int columnNumber = def.getCharno();
String sourceFile = getSourceFile(def);
generateSourceReferenceLink(builder, sourceFile, lineNumber, columnNumber);
JSType defType = edge.getValue().getSite().getJSType();
generateType(builder, defType);
}
/**
* Generate a link and text for a reference to a particular location
* in a source file. Selecting the link should take the programmer
* to a browsable version of the file.
*
* @param builder contents of the report to be generated
* @param sourceFile Path to the file
* @param lineNumber line where the object to view is located
* @param columnNumber column where the object to highlight is located.
*/
private static void generateSourceReferenceLink(StringBuilder builder,
String sourceFile, int lineNumber, int columnNumber) {
assert(sourceFile != null);
builder.append("(");
// Print out the text path so the user knows where things come from.
builder.append(sourceFile + ":" +
lineNumber + "," + columnNumber);
builder.append(")");
}
/**
* Dump a type in a nice, readable way.
*
* @param builder contents of the report to be generated.
* @param defType type to describe
*/
private static void generateType(StringBuilder builder, JSType defType) {
if (defType == null) {
builder.append(" (type: null) ");
} else if (defType.isUnknownType()) {
builder.append(" (type: unknown) ");
} else {
builder.append(" (type: " + defType + ") ");
}
}
/**
* DiGraphNodeComparator gives us a way to generate sorted lists
* of DiGraphNodes. It provides a compare function used by the
* String class's sort method.
*/
class DiGraphNodeComparator implements
Comparator> {
@Override
public int compare(DiGraphNode node1,
DiGraphNode node2) {
Preconditions.checkNotNull(node1.getValue());
Preconditions.checkNotNull(node2.getValue());
if ((node1.getValue().getQualifiedName() == null) &&
(node2.getValue().getQualifiedName() == null)) {
return 0;
}
// Node 1, if null, comes before node 2.
if (node1.getValue().getQualifiedName() == null) {
return -1;
}
// Node 2, if null, comes before node 1.
if (node2.getValue().getQualifiedName() == null) {
return 1;
}
return node1.getValue().getQualifiedName().compareTo(
node2.getValue().getQualifiedName());
}
}
}