source: imaps-frontend/node_modules/eslint/lib/linter/code-path-analysis/code-path-segment.js

main
Last change on this file was d565449, checked in by stefan toskovski <stefantoska84@…>, 4 weeks ago

Update repo after prototype presentation

  • Property mode set to 100644
File size: 8.6 KB
Line 
1/**
2 * @fileoverview The CodePathSegment class.
3 * @author Toru Nagashima
4 */
5
6"use strict";
7
8//------------------------------------------------------------------------------
9// Requirements
10//------------------------------------------------------------------------------
11
12const debug = require("./debug-helpers");
13
14//------------------------------------------------------------------------------
15// Helpers
16//------------------------------------------------------------------------------
17
18/**
19 * Checks whether or not a given segment is reachable.
20 * @param {CodePathSegment} segment A segment to check.
21 * @returns {boolean} `true` if the segment is reachable.
22 */
23function isReachable(segment) {
24 return segment.reachable;
25}
26
27//------------------------------------------------------------------------------
28// Public Interface
29//------------------------------------------------------------------------------
30
31/**
32 * A code path segment.
33 *
34 * Each segment is arranged in a series of linked lists (implemented by arrays)
35 * that keep track of the previous and next segments in a code path. In this way,
36 * you can navigate between all segments in any code path so long as you have a
37 * reference to any segment in that code path.
38 *
39 * When first created, the segment is in a detached state, meaning that it knows the
40 * segments that came before it but those segments don't know that this new segment
41 * follows it. Only when `CodePathSegment#markUsed()` is called on a segment does it
42 * officially become part of the code path by updating the previous segments to know
43 * that this new segment follows.
44 */
45class CodePathSegment {
46
47 /**
48 * Creates a new instance.
49 * @param {string} id An identifier.
50 * @param {CodePathSegment[]} allPrevSegments An array of the previous segments.
51 * This array includes unreachable segments.
52 * @param {boolean} reachable A flag which shows this is reachable.
53 */
54 constructor(id, allPrevSegments, reachable) {
55
56 /**
57 * The identifier of this code path.
58 * Rules use it to store additional information of each rule.
59 * @type {string}
60 */
61 this.id = id;
62
63 /**
64 * An array of the next reachable segments.
65 * @type {CodePathSegment[]}
66 */
67 this.nextSegments = [];
68
69 /**
70 * An array of the previous reachable segments.
71 * @type {CodePathSegment[]}
72 */
73 this.prevSegments = allPrevSegments.filter(isReachable);
74
75 /**
76 * An array of all next segments including reachable and unreachable.
77 * @type {CodePathSegment[]}
78 */
79 this.allNextSegments = [];
80
81 /**
82 * An array of all previous segments including reachable and unreachable.
83 * @type {CodePathSegment[]}
84 */
85 this.allPrevSegments = allPrevSegments;
86
87 /**
88 * A flag which shows this is reachable.
89 * @type {boolean}
90 */
91 this.reachable = reachable;
92
93 // Internal data.
94 Object.defineProperty(this, "internal", {
95 value: {
96
97 // determines if the segment has been attached to the code path
98 used: false,
99
100 // array of previous segments coming from the end of a loop
101 loopedPrevSegments: []
102 }
103 });
104
105 /* c8 ignore start */
106 if (debug.enabled) {
107 this.internal.nodes = [];
108 }/* c8 ignore stop */
109 }
110
111 /**
112 * Checks a given previous segment is coming from the end of a loop.
113 * @param {CodePathSegment} segment A previous segment to check.
114 * @returns {boolean} `true` if the segment is coming from the end of a loop.
115 */
116 isLoopedPrevSegment(segment) {
117 return this.internal.loopedPrevSegments.includes(segment);
118 }
119
120 /**
121 * Creates the root segment.
122 * @param {string} id An identifier.
123 * @returns {CodePathSegment} The created segment.
124 */
125 static newRoot(id) {
126 return new CodePathSegment(id, [], true);
127 }
128
129 /**
130 * Creates a new segment and appends it after the given segments.
131 * @param {string} id An identifier.
132 * @param {CodePathSegment[]} allPrevSegments An array of the previous segments
133 * to append to.
134 * @returns {CodePathSegment} The created segment.
135 */
136 static newNext(id, allPrevSegments) {
137 return new CodePathSegment(
138 id,
139 CodePathSegment.flattenUnusedSegments(allPrevSegments),
140 allPrevSegments.some(isReachable)
141 );
142 }
143
144 /**
145 * Creates an unreachable segment and appends it after the given segments.
146 * @param {string} id An identifier.
147 * @param {CodePathSegment[]} allPrevSegments An array of the previous segments.
148 * @returns {CodePathSegment} The created segment.
149 */
150 static newUnreachable(id, allPrevSegments) {
151 const segment = new CodePathSegment(id, CodePathSegment.flattenUnusedSegments(allPrevSegments), false);
152
153 /*
154 * In `if (a) return a; foo();` case, the unreachable segment preceded by
155 * the return statement is not used but must not be removed.
156 */
157 CodePathSegment.markUsed(segment);
158
159 return segment;
160 }
161
162 /**
163 * Creates a segment that follows given segments.
164 * This factory method does not connect with `allPrevSegments`.
165 * But this inherits `reachable` flag.
166 * @param {string} id An identifier.
167 * @param {CodePathSegment[]} allPrevSegments An array of the previous segments.
168 * @returns {CodePathSegment} The created segment.
169 */
170 static newDisconnected(id, allPrevSegments) {
171 return new CodePathSegment(id, [], allPrevSegments.some(isReachable));
172 }
173
174 /**
175 * Marks a given segment as used.
176 *
177 * And this function registers the segment into the previous segments as a next.
178 * @param {CodePathSegment} segment A segment to mark.
179 * @returns {void}
180 */
181 static markUsed(segment) {
182 if (segment.internal.used) {
183 return;
184 }
185 segment.internal.used = true;
186
187 let i;
188
189 if (segment.reachable) {
190
191 /*
192 * If the segment is reachable, then it's officially part of the
193 * code path. This loops through all previous segments to update
194 * their list of next segments. Because the segment is reachable,
195 * it's added to both `nextSegments` and `allNextSegments`.
196 */
197 for (i = 0; i < segment.allPrevSegments.length; ++i) {
198 const prevSegment = segment.allPrevSegments[i];
199
200 prevSegment.allNextSegments.push(segment);
201 prevSegment.nextSegments.push(segment);
202 }
203 } else {
204
205 /*
206 * If the segment is not reachable, then it's not officially part of the
207 * code path. This loops through all previous segments to update
208 * their list of next segments. Because the segment is not reachable,
209 * it's added only to `allNextSegments`.
210 */
211 for (i = 0; i < segment.allPrevSegments.length; ++i) {
212 segment.allPrevSegments[i].allNextSegments.push(segment);
213 }
214 }
215 }
216
217 /**
218 * Marks a previous segment as looped.
219 * @param {CodePathSegment} segment A segment.
220 * @param {CodePathSegment} prevSegment A previous segment to mark.
221 * @returns {void}
222 */
223 static markPrevSegmentAsLooped(segment, prevSegment) {
224 segment.internal.loopedPrevSegments.push(prevSegment);
225 }
226
227 /**
228 * Creates a new array based on an array of segments. If any segment in the
229 * array is unused, then it is replaced by all of its previous segments.
230 * All used segments are returned as-is without replacement.
231 * @param {CodePathSegment[]} segments The array of segments to flatten.
232 * @returns {CodePathSegment[]} The flattened array.
233 */
234 static flattenUnusedSegments(segments) {
235 const done = new Set();
236
237 for (let i = 0; i < segments.length; ++i) {
238 const segment = segments[i];
239
240 // Ignores duplicated.
241 if (done.has(segment)) {
242 continue;
243 }
244
245 // Use previous segments if unused.
246 if (!segment.internal.used) {
247 for (let j = 0; j < segment.allPrevSegments.length; ++j) {
248 const prevSegment = segment.allPrevSegments[j];
249
250 if (!done.has(prevSegment)) {
251 done.add(prevSegment);
252 }
253 }
254 } else {
255 done.add(segment);
256 }
257 }
258
259 return [...done];
260 }
261}
262
263module.exports = CodePathSegment;
Note: See TracBrowser for help on using the repository browser.