.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / linter / code-path-analysis / fork-context.js
1 /**
2  * @fileoverview A class to operate forking.
3  *
4  * This is state of forking.
5  * This has a fork list and manages it.
6  *
7  * @author Toru Nagashima
8  */
9
10 "use strict";
11
12 //------------------------------------------------------------------------------
13 // Requirements
14 //------------------------------------------------------------------------------
15
16 const assert = require("assert"),
17     CodePathSegment = require("./code-path-segment");
18
19 //------------------------------------------------------------------------------
20 // Helpers
21 //------------------------------------------------------------------------------
22
23 /**
24  * Gets whether or not a given segment is reachable.
25  * @param {CodePathSegment} segment A segment to get.
26  * @returns {boolean} `true` if the segment is reachable.
27  */
28 function isReachable(segment) {
29     return segment.reachable;
30 }
31
32 /**
33  * Creates new segments from the specific range of `context.segmentsList`.
34  *
35  * When `context.segmentsList` is `[[a, b], [c, d], [e, f]]`, `begin` is `0`, and
36  * `end` is `-1`, this creates `[g, h]`. This `g` is from `a`, `c`, and `e`.
37  * This `h` is from `b`, `d`, and `f`.
38  * @param {ForkContext} context An instance.
39  * @param {number} begin The first index of the previous segments.
40  * @param {number} end The last index of the previous segments.
41  * @param {Function} create A factory function of new segments.
42  * @returns {CodePathSegment[]} New segments.
43  */
44 function makeSegments(context, begin, end, create) {
45     const list = context.segmentsList;
46
47     const normalizedBegin = begin >= 0 ? begin : list.length + begin;
48     const normalizedEnd = end >= 0 ? end : list.length + end;
49
50     const segments = [];
51
52     for (let i = 0; i < context.count; ++i) {
53         const allPrevSegments = [];
54
55         for (let j = normalizedBegin; j <= normalizedEnd; ++j) {
56             allPrevSegments.push(list[j][i]);
57         }
58
59         segments.push(create(context.idGenerator.next(), allPrevSegments));
60     }
61
62     return segments;
63 }
64
65 /**
66  * `segments` becomes doubly in a `finally` block. Then if a code path exits by a
67  * control statement (such as `break`, `continue`) from the `finally` block, the
68  * destination's segments may be half of the source segments. In that case, this
69  * merges segments.
70  * @param {ForkContext} context An instance.
71  * @param {CodePathSegment[]} segments Segments to merge.
72  * @returns {CodePathSegment[]} The merged segments.
73  */
74 function mergeExtraSegments(context, segments) {
75     let currentSegments = segments;
76
77     while (currentSegments.length > context.count) {
78         const merged = [];
79
80         for (let i = 0, length = currentSegments.length / 2 | 0; i < length; ++i) {
81             merged.push(CodePathSegment.newNext(
82                 context.idGenerator.next(),
83                 [currentSegments[i], currentSegments[i + length]]
84             ));
85         }
86         currentSegments = merged;
87     }
88     return currentSegments;
89 }
90
91 //------------------------------------------------------------------------------
92 // Public Interface
93 //------------------------------------------------------------------------------
94
95 /**
96  * A class to manage forking.
97  */
98 class ForkContext {
99
100     // eslint-disable-next-line jsdoc/require-description
101     /**
102      * @param {IdGenerator} idGenerator An identifier generator for segments.
103      * @param {ForkContext|null} upper An upper fork context.
104      * @param {number} count A number of parallel segments.
105      */
106     constructor(idGenerator, upper, count) {
107         this.idGenerator = idGenerator;
108         this.upper = upper;
109         this.count = count;
110         this.segmentsList = [];
111     }
112
113     /**
114      * The head segments.
115      * @type {CodePathSegment[]}
116      */
117     get head() {
118         const list = this.segmentsList;
119
120         return list.length === 0 ? [] : list[list.length - 1];
121     }
122
123     /**
124      * A flag which shows empty.
125      * @type {boolean}
126      */
127     get empty() {
128         return this.segmentsList.length === 0;
129     }
130
131     /**
132      * A flag which shows reachable.
133      * @type {boolean}
134      */
135     get reachable() {
136         const segments = this.head;
137
138         return segments.length > 0 && segments.some(isReachable);
139     }
140
141     /**
142      * Creates new segments from this context.
143      * @param {number} begin The first index of previous segments.
144      * @param {number} end The last index of previous segments.
145      * @returns {CodePathSegment[]} New segments.
146      */
147     makeNext(begin, end) {
148         return makeSegments(this, begin, end, CodePathSegment.newNext);
149     }
150
151     /**
152      * Creates new segments from this context.
153      * The new segments is always unreachable.
154      * @param {number} begin The first index of previous segments.
155      * @param {number} end The last index of previous segments.
156      * @returns {CodePathSegment[]} New segments.
157      */
158     makeUnreachable(begin, end) {
159         return makeSegments(this, begin, end, CodePathSegment.newUnreachable);
160     }
161
162     /**
163      * Creates new segments from this context.
164      * The new segments don't have connections for previous segments.
165      * But these inherit the reachable flag from this context.
166      * @param {number} begin The first index of previous segments.
167      * @param {number} end The last index of previous segments.
168      * @returns {CodePathSegment[]} New segments.
169      */
170     makeDisconnected(begin, end) {
171         return makeSegments(this, begin, end, CodePathSegment.newDisconnected);
172     }
173
174     /**
175      * Adds segments into this context.
176      * The added segments become the head.
177      * @param {CodePathSegment[]} segments Segments to add.
178      * @returns {void}
179      */
180     add(segments) {
181         assert(segments.length >= this.count, `${segments.length} >= ${this.count}`);
182
183         this.segmentsList.push(mergeExtraSegments(this, segments));
184     }
185
186     /**
187      * Replaces the head segments with given segments.
188      * The current head segments are removed.
189      * @param {CodePathSegment[]} segments Segments to add.
190      * @returns {void}
191      */
192     replaceHead(segments) {
193         assert(segments.length >= this.count, `${segments.length} >= ${this.count}`);
194
195         this.segmentsList.splice(-1, 1, mergeExtraSegments(this, segments));
196     }
197
198     /**
199      * Adds all segments of a given fork context into this context.
200      * @param {ForkContext} context A fork context to add.
201      * @returns {void}
202      */
203     addAll(context) {
204         assert(context.count === this.count);
205
206         const source = context.segmentsList;
207
208         for (let i = 0; i < source.length; ++i) {
209             this.segmentsList.push(source[i]);
210         }
211     }
212
213     /**
214      * Clears all segments in this context.
215      * @returns {void}
216      */
217     clear() {
218         this.segmentsList = [];
219     }
220
221     /**
222      * Creates the root fork context.
223      * @param {IdGenerator} idGenerator An identifier generator for segments.
224      * @returns {ForkContext} New fork context.
225      */
226     static newRoot(idGenerator) {
227         const context = new ForkContext(idGenerator, null, 1);
228
229         context.add([CodePathSegment.newRoot(idGenerator.next())]);
230
231         return context;
232     }
233
234     /**
235      * Creates an empty fork context preceded by a given context.
236      * @param {ForkContext} parentContext The parent fork context.
237      * @param {boolean} forkLeavingPath A flag which shows inside of `finally` block.
238      * @returns {ForkContext} New fork context.
239      */
240     static newEmpty(parentContext, forkLeavingPath) {
241         return new ForkContext(
242             parentContext.idGenerator,
243             parentContext,
244             (forkLeavingPath ? 2 : 1) * parentContext.count
245         );
246     }
247 }
248
249 module.exports = ForkContext;