| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200 | /** * @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};`;    }};
 |