summaryrefslogtreecommitdiff
path: root/tools/node_modules/eslint/lib/code-path-analysis/debug-helpers.js
diff options
context:
space:
mode:
Diffstat (limited to 'tools/node_modules/eslint/lib/code-path-analysis/debug-helpers.js')
-rw-r--r--tools/node_modules/eslint/lib/code-path-analysis/debug-helpers.js200
1 files changed, 200 insertions, 0 deletions
diff --git a/tools/node_modules/eslint/lib/code-path-analysis/debug-helpers.js b/tools/node_modules/eslint/lib/code-path-analysis/debug-helpers.js
new file mode 100644
index 0000000000..9af985ce85
--- /dev/null
+++ b/tools/node_modules/eslint/lib/code-path-analysis/debug-helpers.js
@@ -0,0 +1,200 @@
+/**
+ * @fileoverview Helpers to debug for code path analysis.
+ * @author Toru Nagashima
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const debug = require("debug")("eslint:code-path");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Gets id of a given segment.
+ * @param {CodePathSegment} segment - A segment to get.
+ * @returns {string} Id of the segment.
+ */
+/* istanbul ignore next */
+function getId(segment) { // eslint-disable-line require-jsdoc
+ return segment.id + (segment.reachable ? "" : "!");
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = {
+
+ /**
+ * A flag that debug dumping is enabled or not.
+ * @type {boolean}
+ */
+ enabled: debug.enabled,
+
+ /**
+ * Dumps given objects.
+ *
+ * @param {...any} args - objects to dump.
+ * @returns {void}
+ */
+ dump: debug,
+
+ /**
+ * Dumps the current analyzing state.
+ *
+ * @param {ASTNode} node - A node to dump.
+ * @param {CodePathState} state - A state to dump.
+ * @param {boolean} leaving - A flag whether or not it's leaving
+ * @returns {void}
+ */
+ dumpState: !debug.enabled ? debug : /* istanbul ignore next */ function(node, state, leaving) {
+ for (let i = 0; i < state.currentSegments.length; ++i) {
+ const segInternal = state.currentSegments[i].internal;
+
+ if (leaving) {
+ segInternal.exitNodes.push(node);
+ } else {
+ segInternal.nodes.push(node);
+ }
+ }
+
+ debug([
+ `${state.currentSegments.map(getId).join(",")})`,
+ `${node.type}${leaving ? ":exit" : ""}`
+ ].join(" "));
+ },
+
+ /**
+ * Dumps a DOT code of a given code path.
+ * The DOT code can be visialized with Graphvis.
+ *
+ * @param {CodePath} codePath - A code path to dump.
+ * @returns {void}
+ * @see http://www.graphviz.org
+ * @see http://www.webgraphviz.com
+ */
+ dumpDot: !debug.enabled ? debug : /* istanbul ignore next */ function(codePath) {
+ let text =
+ "\n" +
+ "digraph {\n" +
+ "node[shape=box,style=\"rounded,filled\",fillcolor=white];\n" +
+ "initial[label=\"\",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];\n";
+
+ if (codePath.returnedSegments.length > 0) {
+ text += "final[label=\"\",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];\n";
+ }
+ if (codePath.thrownSegments.length > 0) {
+ text += "thrown[label=\"✘\",shape=circle,width=0.3,height=0.3,fixedsize];\n";
+ }
+
+ const traceMap = Object.create(null);
+ const arrows = this.makeDotArrows(codePath, traceMap);
+
+ for (const id in traceMap) { // eslint-disable-line guard-for-in
+ const segment = traceMap[id];
+
+ text += `${id}[`;
+
+ if (segment.reachable) {
+ text += "label=\"";
+ } else {
+ text += "style=\"rounded,dashed,filled\",fillcolor=\"#FF9800\",label=\"<<unreachable>>\\n";
+ }
+
+ if (segment.internal.nodes.length > 0 || segment.internal.exitNodes.length > 0) {
+ text += [].concat(
+ segment.internal.nodes.map(node => {
+ switch (node.type) {
+ case "Identifier": return `${node.type} (${node.name})`;
+ case "Literal": return `${node.type} (${node.value})`;
+ default: return node.type;
+ }
+ }),
+ segment.internal.exitNodes.map(node => {
+ switch (node.type) {
+ case "Identifier": return `${node.type}:exit (${node.name})`;
+ case "Literal": return `${node.type}:exit (${node.value})`;
+ default: return `${node.type}:exit`;
+ }
+ })
+ ).join("\\n");
+ } else {
+ text += "????";
+ }
+
+ text += "\"];\n";
+ }
+
+ text += `${arrows}\n`;
+ text += "}";
+ debug("DOT", text);
+ },
+
+ /**
+ * Makes a DOT code of a given code path.
+ * The DOT code can be visialized with Graphvis.
+ *
+ * @param {CodePath} codePath - A code path to make DOT.
+ * @param {Object} traceMap - Optional. A map to check whether or not segments had been done.
+ * @returns {string} A DOT code of the code path.
+ */
+ makeDotArrows(codePath, traceMap) {
+ const stack = [[codePath.initialSegment, 0]];
+ const done = traceMap || Object.create(null);
+ let lastId = codePath.initialSegment.id;
+ let text = `initial->${codePath.initialSegment.id}`;
+
+ while (stack.length > 0) {
+ const item = stack.pop();
+ const segment = item[0];
+ const index = item[1];
+
+ if (done[segment.id] && index === 0) {
+ continue;
+ }
+ done[segment.id] = segment;
+
+ const nextSegment = segment.allNextSegments[index];
+
+ if (!nextSegment) {
+ continue;
+ }
+
+ if (lastId === segment.id) {
+ text += `->${nextSegment.id}`;
+ } else {
+ text += `;\n${segment.id}->${nextSegment.id}`;
+ }
+ lastId = nextSegment.id;
+
+ stack.unshift([segment, 1 + index]);
+ stack.push([nextSegment, 0]);
+ }
+
+ codePath.returnedSegments.forEach(finalSegment => {
+ if (lastId === finalSegment.id) {
+ text += "->final";
+ } else {
+ text += `;\n${finalSegment.id}->final`;
+ }
+ lastId = null;
+ });
+
+ codePath.thrownSegments.forEach(finalSegment => {
+ if (lastId === finalSegment.id) {
+ text += "->thrown";
+ } else {
+ text += `;\n${finalSegment.id}->thrown`;
+ }
+ lastId = null;
+ });
+
+ return `${text};`;
+ }
+};