Permalink
Cannot retrieve contributors at this time
203 lines (169 sloc)
6.41 KB
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
codeql-action/node_modules/eslint/lib/linter/code-path-analysis/debug-helpers.js
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @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. | |
*/ | |
/* c8 ignore next */ | |
function getId(segment) { // eslint-disable-line jsdoc/require-jsdoc -- Ignoring | |
return segment.id + (segment.reachable ? "" : "!"); | |
} | |
/** | |
* Get string for the given node and operation. | |
* @param {ASTNode} node The node to convert. | |
* @param {"enter" | "exit" | undefined} label The operation label. | |
* @returns {string} The string representation. | |
*/ | |
function nodeToString(node, label) { | |
const suffix = label ? `:${label}` : ""; | |
switch (node.type) { | |
case "Identifier": return `${node.type}${suffix} (${node.name})`; | |
case "Literal": return `${node.type}${suffix} (${node.value})`; | |
default: return `${node.type}${suffix}`; | |
} | |
} | |
//------------------------------------------------------------------------------ | |
// 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 : /* c8 ignore next */ function(node, state, leaving) { | |
for (let i = 0; i < state.currentSegments.length; ++i) { | |
const segInternal = state.currentSegments[i].internal; | |
if (leaving) { | |
const last = segInternal.nodes.length - 1; | |
if (last >= 0 && segInternal.nodes[last] === nodeToString(node, "enter")) { | |
segInternal.nodes[last] = nodeToString(node, void 0); | |
} else { | |
segInternal.nodes.push(nodeToString(node, "exit")); | |
} | |
} else { | |
segInternal.nodes.push(nodeToString(node, "enter")); | |
} | |
} | |
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 visualized 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 : /* c8 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=true];\n"; | |
} | |
const traceMap = Object.create(null); | |
const arrows = this.makeDotArrows(codePath, traceMap); | |
for (const id in traceMap) { // eslint-disable-line guard-for-in -- Want ability to traverse prototype | |
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) { | |
text += segment.internal.nodes.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 visualized 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};`; | |
} | |
}; |