Spaces:
Runtime error
Runtime error
chatwine-korean
/
vivino-api
/node_modules
/eslint
/lib
/linter
/code-path-analysis
/debug-helpers.js
/** | |
* @fileoverview Helpers to debug for code path analysis. | |
* @author Toru Nagashima | |
*/ | |
; | |
//------------------------------------------------------------------------------ | |
// 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 jsdoc/require-jsdoc | |
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 : /* istanbul 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 : /* 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) { | |
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};`; | |
} | |
}; | |