1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | "use strict";
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 | const debug = require("debug")("eslint:code-path");
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 | function getId(segment) {
|
25 | return segment.id + (segment.reachable ? "" : "!");
|
26 | }
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 | module.exports = {
|
33 |
|
34 | |
35 |
|
36 |
|
37 |
|
38 | enabled: debug.enabled,
|
39 |
|
40 | |
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 | dump: debug,
|
47 |
|
48 | |
49 |
|
50 |
|
51 |
|
52 |
|
53 |
|
54 |
|
55 |
|
56 | dumpState: !debug.enabled ? debug : function(node, state, leaving) {
|
57 | for (let i = 0; i < state.currentSegments.length; ++i) {
|
58 | const segInternal = state.currentSegments[i].internal;
|
59 |
|
60 | if (leaving) {
|
61 | segInternal.exitNodes.push(node);
|
62 | } else {
|
63 | segInternal.nodes.push(node);
|
64 | }
|
65 | }
|
66 |
|
67 | debug([
|
68 | `${state.currentSegments.map(getId).join(",")})`,
|
69 | `${node.type}${leaving ? ":exit" : ""}`
|
70 | ].join(" "));
|
71 | },
|
72 |
|
73 | |
74 |
|
75 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 | dumpDot: !debug.enabled ? debug : function(codePath) {
|
83 | let text =
|
84 | "\n" +
|
85 | "digraph {\n" +
|
86 | "node[shape=box,style=\"rounded,filled\",fillcolor=white];\n" +
|
87 | "initial[label=\"\",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];\n";
|
88 |
|
89 | if (codePath.returnedSegments.length > 0) {
|
90 | text += "final[label=\"\",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];\n";
|
91 | }
|
92 | if (codePath.thrownSegments.length > 0) {
|
93 | text += "thrown[label=\"✘\",shape=circle,width=0.3,height=0.3,fixedsize];\n";
|
94 | }
|
95 |
|
96 | const traceMap = Object.create(null);
|
97 | const arrows = this.makeDotArrows(codePath, traceMap);
|
98 |
|
99 | for (const id in traceMap) {
|
100 | const segment = traceMap[id];
|
101 |
|
102 | text += `${id}[`;
|
103 |
|
104 | if (segment.reachable) {
|
105 | text += "label=\"";
|
106 | } else {
|
107 | text += "style=\"rounded,dashed,filled\",fillcolor=\"#FF9800\",label=\"<<unreachable>>\\n";
|
108 | }
|
109 |
|
110 | if (segment.internal.nodes.length > 0 || segment.internal.exitNodes.length > 0) {
|
111 | text += [].concat(
|
112 | segment.internal.nodes.map(node => {
|
113 | switch (node.type) {
|
114 | case "Identifier": return `${node.type} (${node.name})`;
|
115 | case "Literal": return `${node.type} (${node.value})`;
|
116 | default: return node.type;
|
117 | }
|
118 | }),
|
119 | segment.internal.exitNodes.map(node => {
|
120 | switch (node.type) {
|
121 | case "Identifier": return `${node.type}:exit (${node.name})`;
|
122 | case "Literal": return `${node.type}:exit (${node.value})`;
|
123 | default: return `${node.type}:exit`;
|
124 | }
|
125 | })
|
126 | ).join("\\n");
|
127 | } else {
|
128 | text += "????";
|
129 | }
|
130 |
|
131 | text += "\"];\n";
|
132 | }
|
133 |
|
134 | text += `${arrows}\n`;
|
135 | text += "}";
|
136 | debug("DOT", text);
|
137 | },
|
138 |
|
139 | |
140 |
|
141 |
|
142 |
|
143 |
|
144 |
|
145 |
|
146 |
|
147 | makeDotArrows(codePath, traceMap) {
|
148 | const stack = [[codePath.initialSegment, 0]];
|
149 | const done = traceMap || Object.create(null);
|
150 | let lastId = codePath.initialSegment.id;
|
151 | let text = `initial->${codePath.initialSegment.id}`;
|
152 |
|
153 | while (stack.length > 0) {
|
154 | const item = stack.pop();
|
155 | const segment = item[0];
|
156 | const index = item[1];
|
157 |
|
158 | if (done[segment.id] && index === 0) {
|
159 | continue;
|
160 | }
|
161 | done[segment.id] = segment;
|
162 |
|
163 | const nextSegment = segment.allNextSegments[index];
|
164 |
|
165 | if (!nextSegment) {
|
166 | continue;
|
167 | }
|
168 |
|
169 | if (lastId === segment.id) {
|
170 | text += `->${nextSegment.id}`;
|
171 | } else {
|
172 | text += `;\n${segment.id}->${nextSegment.id}`;
|
173 | }
|
174 | lastId = nextSegment.id;
|
175 |
|
176 | stack.unshift([segment, 1 + index]);
|
177 | stack.push([nextSegment, 0]);
|
178 | }
|
179 |
|
180 | codePath.returnedSegments.forEach(finalSegment => {
|
181 | if (lastId === finalSegment.id) {
|
182 | text += "->final";
|
183 | } else {
|
184 | text += `;\n${finalSegment.id}->final`;
|
185 | }
|
186 | lastId = null;
|
187 | });
|
188 |
|
189 | codePath.thrownSegments.forEach(finalSegment => {
|
190 | if (lastId === finalSegment.id) {
|
191 | text += "->thrown";
|
192 | } else {
|
193 | text += `;\n${finalSegment.id}->thrown`;
|
194 | }
|
195 | lastId = null;
|
196 | });
|
197 |
|
198 | return `${text};`;
|
199 | }
|
200 | };
|