eslint 8.49.0 → 8.51.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/bin/eslint.js +15 -4
- package/lib/cli.js +6 -2
- package/lib/config/flat-config-schema.js +12 -4
- package/lib/eslint/eslint-helpers.js +8 -3
- package/lib/eslint/flat-eslint.js +15 -6
- package/lib/linter/apply-disable-directives.js +1 -1
- package/lib/linter/code-path-analysis/code-path-state.js +1108 -243
- package/lib/linter/code-path-analysis/code-path.js +127 -33
- package/lib/linter/code-path-analysis/fork-context.js +173 -72
- package/lib/linter/config-comment-parser.js +1 -1
- package/lib/linter/linter.js +172 -57
- package/lib/options.js +13 -0
- package/lib/rule-tester/flat-rule-tester.js +50 -5
- package/lib/rule-tester/rule-tester.js +78 -20
- package/lib/rules/array-callback-return.js +139 -14
- package/lib/rules/index.js +1 -0
- package/lib/rules/logical-assignment-operators.js +31 -3
- package/lib/rules/no-misleading-character-class.js +65 -15
- package/lib/rules/no-new-object.js +7 -0
- package/lib/rules/no-object-constructor.js +118 -0
- package/lib/source-code/source-code.js +350 -3
- package/package.json +2 -2
@@ -80,7 +80,9 @@ class CodePath {
|
|
80
80
|
}
|
81
81
|
|
82
82
|
/**
|
83
|
-
* The initial code path segment.
|
83
|
+
* The initial code path segment. This is the segment that is at the head
|
84
|
+
* of the code path.
|
85
|
+
* This is a passthrough to the underlying `CodePathState`.
|
84
86
|
* @type {CodePathSegment}
|
85
87
|
*/
|
86
88
|
get initialSegment() {
|
@@ -88,8 +90,10 @@ class CodePath {
|
|
88
90
|
}
|
89
91
|
|
90
92
|
/**
|
91
|
-
* Final code path segments.
|
92
|
-
*
|
93
|
+
* Final code path segments. These are the terminal (tail) segments in the
|
94
|
+
* code path, which is the combination of `returnedSegments` and `thrownSegments`.
|
95
|
+
* All segments in this array are reachable.
|
96
|
+
* This is a passthrough to the underlying `CodePathState`.
|
93
97
|
* @type {CodePathSegment[]}
|
94
98
|
*/
|
95
99
|
get finalSegments() {
|
@@ -97,9 +101,14 @@ class CodePath {
|
|
97
101
|
}
|
98
102
|
|
99
103
|
/**
|
100
|
-
* Final code path segments
|
101
|
-
*
|
102
|
-
*
|
104
|
+
* Final code path segments that represent normal completion of the code path.
|
105
|
+
* For functions, this means both explicit `return` statements and implicit returns,
|
106
|
+
* such as the last reachable segment in a function that does not have an
|
107
|
+
* explicit `return` as this implicitly returns `undefined`. For scripts,
|
108
|
+
* modules, class field initializers, and class static blocks, this means
|
109
|
+
* all lines of code have been executed.
|
110
|
+
* These segments are also present in `finalSegments`.
|
111
|
+
* This is a passthrough to the underlying `CodePathState`.
|
103
112
|
* @type {CodePathSegment[]}
|
104
113
|
*/
|
105
114
|
get returnedSegments() {
|
@@ -107,7 +116,9 @@ class CodePath {
|
|
107
116
|
}
|
108
117
|
|
109
118
|
/**
|
110
|
-
* Final code path segments
|
119
|
+
* Final code path segments that represent `throw` statements.
|
120
|
+
* This is a passthrough to the underlying `CodePathState`.
|
121
|
+
* These segments are also present in `finalSegments`.
|
111
122
|
* @type {CodePathSegment[]}
|
112
123
|
*/
|
113
124
|
get thrownSegments() {
|
@@ -115,7 +126,12 @@ class CodePath {
|
|
115
126
|
}
|
116
127
|
|
117
128
|
/**
|
118
|
-
*
|
129
|
+
* Tracks the traversal of the code path through each segment. This array
|
130
|
+
* starts empty and segments are added or removed as the code path is
|
131
|
+
* traversed. This array always ends up empty at the end of a code path
|
132
|
+
* traversal. The `CodePathState` uses this to track its progress through
|
133
|
+
* the code path.
|
134
|
+
* This is a passthrough to the underlying `CodePathState`.
|
119
135
|
* @type {CodePathSegment[]}
|
120
136
|
* @deprecated
|
121
137
|
*/
|
@@ -126,46 +142,70 @@ class CodePath {
|
|
126
142
|
/**
|
127
143
|
* Traverses all segments in this code path.
|
128
144
|
*
|
129
|
-
* codePath.traverseSegments(
|
145
|
+
* codePath.traverseSegments((segment, controller) => {
|
130
146
|
* // do something.
|
131
147
|
* });
|
132
148
|
*
|
133
149
|
* This method enumerates segments in order from the head.
|
134
150
|
*
|
135
|
-
* The `controller`
|
151
|
+
* The `controller` argument has two methods:
|
136
152
|
*
|
137
|
-
* - `
|
138
|
-
* - `
|
139
|
-
*
|
140
|
-
*
|
141
|
-
*
|
153
|
+
* - `skip()` - skips the following segments in this branch
|
154
|
+
* - `break()` - skips all following segments in the traversal
|
155
|
+
*
|
156
|
+
* A note on the parameters: the `options` argument is optional. This means
|
157
|
+
* the first argument might be an options object or the callback function.
|
158
|
+
* @param {Object} [optionsOrCallback] Optional first and last segments to traverse.
|
159
|
+
* @param {CodePathSegment} [optionsOrCallback.first] The first segment to traverse.
|
160
|
+
* @param {CodePathSegment} [optionsOrCallback.last] The last segment to traverse.
|
142
161
|
* @param {Function} callback A callback function.
|
143
162
|
* @returns {void}
|
144
163
|
*/
|
145
|
-
traverseSegments(
|
164
|
+
traverseSegments(optionsOrCallback, callback) {
|
165
|
+
|
166
|
+
// normalize the arguments into a callback and options
|
146
167
|
let resolvedOptions;
|
147
168
|
let resolvedCallback;
|
148
169
|
|
149
|
-
if (typeof
|
150
|
-
resolvedCallback =
|
170
|
+
if (typeof optionsOrCallback === "function") {
|
171
|
+
resolvedCallback = optionsOrCallback;
|
151
172
|
resolvedOptions = {};
|
152
173
|
} else {
|
153
|
-
resolvedOptions =
|
174
|
+
resolvedOptions = optionsOrCallback || {};
|
154
175
|
resolvedCallback = callback;
|
155
176
|
}
|
156
177
|
|
178
|
+
// determine where to start traversing from based on the options
|
157
179
|
const startSegment = resolvedOptions.first || this.internal.initialSegment;
|
158
180
|
const lastSegment = resolvedOptions.last;
|
159
181
|
|
160
|
-
|
182
|
+
// set up initial location information
|
183
|
+
let record = null;
|
161
184
|
let index = 0;
|
162
185
|
let end = 0;
|
163
186
|
let segment = null;
|
164
|
-
|
187
|
+
|
188
|
+
// segments that have already been visited during traversal
|
189
|
+
const visited = new Set();
|
190
|
+
|
191
|
+
// tracks the traversal steps
|
165
192
|
const stack = [[startSegment, 0]];
|
193
|
+
|
194
|
+
// tracks the last skipped segment during traversal
|
166
195
|
let skippedSegment = null;
|
196
|
+
|
197
|
+
// indicates if we exited early from the traversal
|
167
198
|
let broken = false;
|
199
|
+
|
200
|
+
/**
|
201
|
+
* Maintains traversal state.
|
202
|
+
*/
|
168
203
|
const controller = {
|
204
|
+
|
205
|
+
/**
|
206
|
+
* Skip the following segments in this branch.
|
207
|
+
* @returns {void}
|
208
|
+
*/
|
169
209
|
skip() {
|
170
210
|
if (stack.length <= 1) {
|
171
211
|
broken = true;
|
@@ -173,32 +213,52 @@ class CodePath {
|
|
173
213
|
skippedSegment = stack[stack.length - 2][0];
|
174
214
|
}
|
175
215
|
},
|
216
|
+
|
217
|
+
/**
|
218
|
+
* Stop traversal completely - do not traverse to any
|
219
|
+
* other segments.
|
220
|
+
* @returns {void}
|
221
|
+
*/
|
176
222
|
break() {
|
177
223
|
broken = true;
|
178
224
|
}
|
179
225
|
};
|
180
226
|
|
181
227
|
/**
|
182
|
-
* Checks a given previous segment has been visited.
|
228
|
+
* Checks if a given previous segment has been visited.
|
183
229
|
* @param {CodePathSegment} prevSegment A previous segment to check.
|
184
230
|
* @returns {boolean} `true` if the segment has been visited.
|
185
231
|
*/
|
186
232
|
function isVisited(prevSegment) {
|
187
233
|
return (
|
188
|
-
visited
|
234
|
+
visited.has(prevSegment) ||
|
189
235
|
segment.isLoopedPrevSegment(prevSegment)
|
190
236
|
);
|
191
237
|
}
|
192
238
|
|
239
|
+
// the traversal
|
193
240
|
while (stack.length > 0) {
|
194
|
-
|
195
|
-
|
196
|
-
|
241
|
+
|
242
|
+
/*
|
243
|
+
* This isn't a pure stack. We use the top record all the time
|
244
|
+
* but don't always pop it off. The record is popped only if
|
245
|
+
* one of the following is true:
|
246
|
+
*
|
247
|
+
* 1) We have already visited the segment.
|
248
|
+
* 2) We have not visited *all* of the previous segments.
|
249
|
+
* 3) We have traversed past the available next segments.
|
250
|
+
*
|
251
|
+
* Otherwise, we just read the value and sometimes modify the
|
252
|
+
* record as we traverse.
|
253
|
+
*/
|
254
|
+
record = stack[stack.length - 1];
|
255
|
+
segment = record[0];
|
256
|
+
index = record[1];
|
197
257
|
|
198
258
|
if (index === 0) {
|
199
259
|
|
200
260
|
// Skip if this segment has been visited already.
|
201
|
-
if (visited
|
261
|
+
if (visited.has(segment)) {
|
202
262
|
stack.pop();
|
203
263
|
continue;
|
204
264
|
}
|
@@ -212,18 +272,29 @@ class CodePath {
|
|
212
272
|
continue;
|
213
273
|
}
|
214
274
|
|
215
|
-
// Reset the flag
|
275
|
+
// Reset the skipping flag if all branches have been skipped.
|
216
276
|
if (skippedSegment && segment.prevSegments.includes(skippedSegment)) {
|
217
277
|
skippedSegment = null;
|
218
278
|
}
|
219
|
-
visited
|
279
|
+
visited.add(segment);
|
220
280
|
|
221
|
-
|
281
|
+
/*
|
282
|
+
* If the most recent segment hasn't been skipped, then we call
|
283
|
+
* the callback, passing in the segment and the controller.
|
284
|
+
*/
|
222
285
|
if (!skippedSegment) {
|
223
286
|
resolvedCallback.call(this, segment, controller);
|
287
|
+
|
288
|
+
// exit if we're at the last segment
|
224
289
|
if (segment === lastSegment) {
|
225
290
|
controller.skip();
|
226
291
|
}
|
292
|
+
|
293
|
+
/*
|
294
|
+
* If the previous statement was executed, or if the callback
|
295
|
+
* called a method on the controller, we might need to exit the
|
296
|
+
* loop, so check for that and break accordingly.
|
297
|
+
*/
|
227
298
|
if (broken) {
|
228
299
|
break;
|
229
300
|
}
|
@@ -233,12 +304,35 @@ class CodePath {
|
|
233
304
|
// Update the stack.
|
234
305
|
end = segment.nextSegments.length - 1;
|
235
306
|
if (index < end) {
|
236
|
-
|
307
|
+
|
308
|
+
/*
|
309
|
+
* If we haven't yet visited all of the next segments, update
|
310
|
+
* the current top record on the stack to the next index to visit
|
311
|
+
* and then push a record for the current segment on top.
|
312
|
+
*
|
313
|
+
* Setting the current top record's index lets us know how many
|
314
|
+
* times we've been here and ensures that the segment won't be
|
315
|
+
* reprocessed (because we only process segments with an index
|
316
|
+
* of 0).
|
317
|
+
*/
|
318
|
+
record[1] += 1;
|
237
319
|
stack.push([segment.nextSegments[index], 0]);
|
238
320
|
} else if (index === end) {
|
239
|
-
|
240
|
-
|
321
|
+
|
322
|
+
/*
|
323
|
+
* If we are at the last next segment, then reset the top record
|
324
|
+
* in the stack to next segment and set its index to 0 so it will
|
325
|
+
* be processed next.
|
326
|
+
*/
|
327
|
+
record[0] = segment.nextSegments[index];
|
328
|
+
record[1] = 0;
|
241
329
|
} else {
|
330
|
+
|
331
|
+
/*
|
332
|
+
* If index > end, that means we have no more segments that need
|
333
|
+
* processing. So, we pop that record off of the stack in order to
|
334
|
+
* continue traversing at the next level up.
|
335
|
+
*/
|
242
336
|
stack.pop();
|
243
337
|
}
|
244
338
|
}
|
@@ -21,8 +21,8 @@ const assert = require("assert"),
|
|
21
21
|
//------------------------------------------------------------------------------
|
22
22
|
|
23
23
|
/**
|
24
|
-
*
|
25
|
-
* @param {CodePathSegment} segment
|
24
|
+
* Determines whether or not a given segment is reachable.
|
25
|
+
* @param {CodePathSegment} segment The segment to check.
|
26
26
|
* @returns {boolean} `true` if the segment is reachable.
|
27
27
|
*/
|
28
28
|
function isReachable(segment) {
|
@@ -30,32 +30,64 @@ function isReachable(segment) {
|
|
30
30
|
}
|
31
31
|
|
32
32
|
/**
|
33
|
-
* Creates new
|
33
|
+
* Creates a new segment for each fork in the given context and appends it
|
34
|
+
* to the end of the specified range of segments. Ultimately, this ends up calling
|
35
|
+
* `new CodePathSegment()` for each of the forks using the `create` argument
|
36
|
+
* as a wrapper around special behavior.
|
37
|
+
*
|
38
|
+
* The `startIndex` and `endIndex` arguments specify a range of segments in
|
39
|
+
* `context` that should become `allPrevSegments` for the newly created
|
40
|
+
* `CodePathSegment` objects.
|
34
41
|
*
|
35
42
|
* 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
|
37
|
-
*
|
38
|
-
*
|
39
|
-
* @param {
|
40
|
-
*
|
41
|
-
* @param {
|
42
|
-
*
|
43
|
+
* `end` is `-1`, this creates two new segments, `[g, h]`. This `g` is appended to
|
44
|
+
* the end of the path from `a`, `c`, and `e`. This `h` is appended to the end of
|
45
|
+
* `b`, `d`, and `f`.
|
46
|
+
* @param {ForkContext} context An instance from which the previous segments
|
47
|
+
* will be obtained.
|
48
|
+
* @param {number} startIndex The index of the first segment in the context
|
49
|
+
* that should be specified as previous segments for the newly created segments.
|
50
|
+
* @param {number} endIndex The index of the last segment in the context
|
51
|
+
* that should be specified as previous segments for the newly created segments.
|
52
|
+
* @param {Function} create A function that creates new `CodePathSegment`
|
53
|
+
* instances in a particular way. See the `CodePathSegment.new*` methods.
|
54
|
+
* @returns {Array<CodePathSegment>} An array of the newly-created segments.
|
43
55
|
*/
|
44
|
-
function
|
56
|
+
function createSegments(context, startIndex, endIndex, create) {
|
57
|
+
|
58
|
+
/** @type {Array<Array<CodePathSegment>>} */
|
45
59
|
const list = context.segmentsList;
|
46
60
|
|
47
|
-
|
48
|
-
|
61
|
+
/*
|
62
|
+
* Both `startIndex` and `endIndex` work the same way: if the number is zero
|
63
|
+
* or more, then the number is used as-is. If the number is negative,
|
64
|
+
* then that number is added to the length of the segments list to
|
65
|
+
* determine the index to use. That means -1 for either argument
|
66
|
+
* is the last element, -2 is the second to last, and so on.
|
67
|
+
*
|
68
|
+
* So if `startIndex` is 0, `endIndex` is -1, and `list.length` is 3, the
|
69
|
+
* effective `startIndex` is 0 and the effective `endIndex` is 2, so this function
|
70
|
+
* will include items at indices 0, 1, and 2.
|
71
|
+
*
|
72
|
+
* Therefore, if `startIndex` is -1 and `endIndex` is -1, that means we'll only
|
73
|
+
* be using the last segment in `list`.
|
74
|
+
*/
|
75
|
+
const normalizedBegin = startIndex >= 0 ? startIndex : list.length + startIndex;
|
76
|
+
const normalizedEnd = endIndex >= 0 ? endIndex : list.length + endIndex;
|
49
77
|
|
78
|
+
/** @type {Array<CodePathSegment>} */
|
50
79
|
const segments = [];
|
51
80
|
|
52
81
|
for (let i = 0; i < context.count; ++i) {
|
82
|
+
|
83
|
+
// this is passed into `new CodePathSegment` to add to code path.
|
53
84
|
const allPrevSegments = [];
|
54
85
|
|
55
86
|
for (let j = normalizedBegin; j <= normalizedEnd; ++j) {
|
56
87
|
allPrevSegments.push(list[j][i]);
|
57
88
|
}
|
58
89
|
|
90
|
+
// note: `create` is just a wrapper that augments `new CodePathSegment`.
|
59
91
|
segments.push(create(context.idGenerator.next(), allPrevSegments));
|
60
92
|
}
|
61
93
|
|
@@ -63,28 +95,57 @@ function makeSegments(context, begin, end, create) {
|
|
63
95
|
}
|
64
96
|
|
65
97
|
/**
|
66
|
-
*
|
67
|
-
* control statement (such as `break
|
68
|
-
*
|
69
|
-
*
|
70
|
-
* @param {
|
71
|
-
* @
|
72
|
-
* @returns {CodePathSegment[]} The merged segments.
|
98
|
+
* Inside of a `finally` block we end up with two parallel paths. If the code path
|
99
|
+
* exits by a control statement (such as `break` or `continue`) from the `finally`
|
100
|
+
* block, then we need to merge the remaining parallel paths back into one.
|
101
|
+
* @param {ForkContext} context The fork context to work on.
|
102
|
+
* @param {Array<CodePathSegment>} segments Segments to merge.
|
103
|
+
* @returns {Array<CodePathSegment>} The merged segments.
|
73
104
|
*/
|
74
105
|
function mergeExtraSegments(context, segments) {
|
75
106
|
let currentSegments = segments;
|
76
107
|
|
108
|
+
/*
|
109
|
+
* We need to ensure that the array returned from this function contains no more
|
110
|
+
* than the number of segments that the context allows. `context.count` indicates
|
111
|
+
* how many items should be in the returned array to ensure that the new segment
|
112
|
+
* entries will line up with the already existing segment entries.
|
113
|
+
*/
|
77
114
|
while (currentSegments.length > context.count) {
|
78
115
|
const merged = [];
|
79
116
|
|
80
|
-
|
117
|
+
/*
|
118
|
+
* Because `context.count` is a factor of 2 inside of a `finally` block,
|
119
|
+
* we can divide the segment count by 2 to merge the paths together.
|
120
|
+
* This loops through each segment in the list and creates a new `CodePathSegment`
|
121
|
+
* that has the segment and the segment two slots away as previous segments.
|
122
|
+
*
|
123
|
+
* If `currentSegments` is [a,b,c,d], this will create new segments e and f, such
|
124
|
+
* that:
|
125
|
+
*
|
126
|
+
* When `i` is 0:
|
127
|
+
* a->e
|
128
|
+
* c->e
|
129
|
+
*
|
130
|
+
* When `i` is 1:
|
131
|
+
* b->f
|
132
|
+
* d->f
|
133
|
+
*/
|
134
|
+
for (let i = 0, length = Math.floor(currentSegments.length / 2); i < length; ++i) {
|
81
135
|
merged.push(CodePathSegment.newNext(
|
82
136
|
context.idGenerator.next(),
|
83
137
|
[currentSegments[i], currentSegments[i + length]]
|
84
138
|
));
|
85
139
|
}
|
140
|
+
|
141
|
+
/*
|
142
|
+
* Go through the loop condition one more time to see if we have the
|
143
|
+
* number of segments for the context. If not, we'll keep merging paths
|
144
|
+
* of the merged segments until we get there.
|
145
|
+
*/
|
86
146
|
currentSegments = merged;
|
87
147
|
}
|
148
|
+
|
88
149
|
return currentSegments;
|
89
150
|
}
|
90
151
|
|
@@ -93,25 +154,55 @@ function mergeExtraSegments(context, segments) {
|
|
93
154
|
//------------------------------------------------------------------------------
|
94
155
|
|
95
156
|
/**
|
96
|
-
*
|
157
|
+
* Manages the forking of code paths.
|
97
158
|
*/
|
98
159
|
class ForkContext {
|
99
160
|
|
100
161
|
/**
|
162
|
+
* Creates a new instance.
|
101
163
|
* @param {IdGenerator} idGenerator An identifier generator for segments.
|
102
|
-
* @param {ForkContext|null} upper
|
103
|
-
* @param {number} count
|
164
|
+
* @param {ForkContext|null} upper The preceding fork context.
|
165
|
+
* @param {number} count The number of parallel segments in each element
|
166
|
+
* of `segmentsList`.
|
104
167
|
*/
|
105
168
|
constructor(idGenerator, upper, count) {
|
169
|
+
|
170
|
+
/**
|
171
|
+
* The ID generator that will generate segment IDs for any new
|
172
|
+
* segments that are created.
|
173
|
+
* @type {IdGenerator}
|
174
|
+
*/
|
106
175
|
this.idGenerator = idGenerator;
|
176
|
+
|
177
|
+
/**
|
178
|
+
* The preceding fork context.
|
179
|
+
* @type {ForkContext|null}
|
180
|
+
*/
|
107
181
|
this.upper = upper;
|
182
|
+
|
183
|
+
/**
|
184
|
+
* The number of elements in each element of `segmentsList`. In most
|
185
|
+
* cases, this is 1 but can be 2 when there is a `finally` present,
|
186
|
+
* which forks the code path outside of normal flow. In the case of nested
|
187
|
+
* `finally` blocks, this can be a multiple of 2.
|
188
|
+
* @type {number}
|
189
|
+
*/
|
108
190
|
this.count = count;
|
191
|
+
|
192
|
+
/**
|
193
|
+
* The segments within this context. Each element in this array has
|
194
|
+
* `count` elements that represent one step in each fork. For example,
|
195
|
+
* when `segmentsList` is `[[a, b], [c, d], [e, f]]`, there is one path
|
196
|
+
* a->c->e and one path b->d->f, and `count` is 2 because each element
|
197
|
+
* is an array with two elements.
|
198
|
+
* @type {Array<Array<CodePathSegment>>}
|
199
|
+
*/
|
109
200
|
this.segmentsList = [];
|
110
201
|
}
|
111
202
|
|
112
203
|
/**
|
113
|
-
* The
|
114
|
-
* @type {CodePathSegment
|
204
|
+
* The segments that begin this fork context.
|
205
|
+
* @type {Array<CodePathSegment>}
|
115
206
|
*/
|
116
207
|
get head() {
|
117
208
|
const list = this.segmentsList;
|
@@ -120,7 +211,7 @@ class ForkContext {
|
|
120
211
|
}
|
121
212
|
|
122
213
|
/**
|
123
|
-
*
|
214
|
+
* Indicates if the context contains no segments.
|
124
215
|
* @type {boolean}
|
125
216
|
*/
|
126
217
|
get empty() {
|
@@ -128,7 +219,7 @@ class ForkContext {
|
|
128
219
|
}
|
129
220
|
|
130
221
|
/**
|
131
|
-
*
|
222
|
+
* Indicates if there are any segments that are reachable.
|
132
223
|
* @type {boolean}
|
133
224
|
*/
|
134
225
|
get reachable() {
|
@@ -138,75 +229,82 @@ class ForkContext {
|
|
138
229
|
}
|
139
230
|
|
140
231
|
/**
|
141
|
-
* Creates new segments
|
142
|
-
*
|
143
|
-
*
|
144
|
-
* @
|
232
|
+
* Creates new segments in this context and appends them to the end of the
|
233
|
+
* already existing `CodePathSegment`s specified by `startIndex` and
|
234
|
+
* `endIndex`.
|
235
|
+
* @param {number} startIndex The index of the first segment in the context
|
236
|
+
* that should be specified as previous segments for the newly created segments.
|
237
|
+
* @param {number} endIndex The index of the last segment in the context
|
238
|
+
* that should be specified as previous segments for the newly created segments.
|
239
|
+
* @returns {Array<CodePathSegment>} An array of the newly created segments.
|
145
240
|
*/
|
146
|
-
makeNext(
|
147
|
-
return
|
241
|
+
makeNext(startIndex, endIndex) {
|
242
|
+
return createSegments(this, startIndex, endIndex, CodePathSegment.newNext);
|
148
243
|
}
|
149
244
|
|
150
245
|
/**
|
151
|
-
* Creates new segments
|
152
|
-
*
|
153
|
-
*
|
154
|
-
* @param {number}
|
155
|
-
*
|
246
|
+
* Creates new unreachable segments in this context and appends them to the end of the
|
247
|
+
* already existing `CodePathSegment`s specified by `startIndex` and
|
248
|
+
* `endIndex`.
|
249
|
+
* @param {number} startIndex The index of the first segment in the context
|
250
|
+
* that should be specified as previous segments for the newly created segments.
|
251
|
+
* @param {number} endIndex The index of the last segment in the context
|
252
|
+
* that should be specified as previous segments for the newly created segments.
|
253
|
+
* @returns {Array<CodePathSegment>} An array of the newly created segments.
|
156
254
|
*/
|
157
|
-
makeUnreachable(
|
158
|
-
return
|
255
|
+
makeUnreachable(startIndex, endIndex) {
|
256
|
+
return createSegments(this, startIndex, endIndex, CodePathSegment.newUnreachable);
|
159
257
|
}
|
160
258
|
|
161
259
|
/**
|
162
|
-
* Creates new segments
|
163
|
-
*
|
164
|
-
*
|
165
|
-
*
|
166
|
-
*
|
167
|
-
*
|
260
|
+
* Creates new segments in this context and does not append them to the end
|
261
|
+
* of the already existing `CodePathSegment`s specified by `startIndex` and
|
262
|
+
* `endIndex`. The `startIndex` and `endIndex` are only used to determine if
|
263
|
+
* the new segments should be reachable. If any of the segments in this range
|
264
|
+
* are reachable then the new segments are also reachable; otherwise, the new
|
265
|
+
* segments are unreachable.
|
266
|
+
* @param {number} startIndex The index of the first segment in the context
|
267
|
+
* that should be considered for reachability.
|
268
|
+
* @param {number} endIndex The index of the last segment in the context
|
269
|
+
* that should be considered for reachability.
|
270
|
+
* @returns {Array<CodePathSegment>} An array of the newly created segments.
|
168
271
|
*/
|
169
|
-
makeDisconnected(
|
170
|
-
return
|
272
|
+
makeDisconnected(startIndex, endIndex) {
|
273
|
+
return createSegments(this, startIndex, endIndex, CodePathSegment.newDisconnected);
|
171
274
|
}
|
172
275
|
|
173
276
|
/**
|
174
|
-
* Adds segments
|
175
|
-
*
|
176
|
-
* @param {CodePathSegment[]} segments Segments to add.
|
277
|
+
* Adds segments to the head of this context.
|
278
|
+
* @param {Array<CodePathSegment>} segments The segments to add.
|
177
279
|
* @returns {void}
|
178
280
|
*/
|
179
281
|
add(segments) {
|
180
282
|
assert(segments.length >= this.count, `${segments.length} >= ${this.count}`);
|
181
|
-
|
182
283
|
this.segmentsList.push(mergeExtraSegments(this, segments));
|
183
284
|
}
|
184
285
|
|
185
286
|
/**
|
186
|
-
* Replaces the head segments with given segments.
|
287
|
+
* Replaces the head segments with the given segments.
|
187
288
|
* The current head segments are removed.
|
188
|
-
* @param {CodePathSegment
|
289
|
+
* @param {Array<CodePathSegment>} replacementHeadSegments The new head segments.
|
189
290
|
* @returns {void}
|
190
291
|
*/
|
191
|
-
replaceHead(
|
192
|
-
assert(
|
193
|
-
|
194
|
-
|
292
|
+
replaceHead(replacementHeadSegments) {
|
293
|
+
assert(
|
294
|
+
replacementHeadSegments.length >= this.count,
|
295
|
+
`${replacementHeadSegments.length} >= ${this.count}`
|
296
|
+
);
|
297
|
+
this.segmentsList.splice(-1, 1, mergeExtraSegments(this, replacementHeadSegments));
|
195
298
|
}
|
196
299
|
|
197
300
|
/**
|
198
301
|
* Adds all segments of a given fork context into this context.
|
199
|
-
* @param {ForkContext}
|
302
|
+
* @param {ForkContext} otherForkContext The fork context to add from.
|
200
303
|
* @returns {void}
|
201
304
|
*/
|
202
|
-
addAll(
|
203
|
-
assert(
|
204
|
-
|
205
|
-
const source = context.segmentsList;
|
206
|
-
|
207
|
-
for (let i = 0; i < source.length; ++i) {
|
208
|
-
this.segmentsList.push(source[i]);
|
209
|
-
}
|
305
|
+
addAll(otherForkContext) {
|
306
|
+
assert(otherForkContext.count === this.count);
|
307
|
+
this.segmentsList.push(...otherForkContext.segmentsList);
|
210
308
|
}
|
211
309
|
|
212
310
|
/**
|
@@ -218,7 +316,8 @@ class ForkContext {
|
|
218
316
|
}
|
219
317
|
|
220
318
|
/**
|
221
|
-
* Creates
|
319
|
+
* Creates a new root context, meaning that there are no parent
|
320
|
+
* fork contexts.
|
222
321
|
* @param {IdGenerator} idGenerator An identifier generator for segments.
|
223
322
|
* @returns {ForkContext} New fork context.
|
224
323
|
*/
|
@@ -233,14 +332,16 @@ class ForkContext {
|
|
233
332
|
/**
|
234
333
|
* Creates an empty fork context preceded by a given context.
|
235
334
|
* @param {ForkContext} parentContext The parent fork context.
|
236
|
-
* @param {boolean}
|
335
|
+
* @param {boolean} shouldForkLeavingPath Indicates that we are inside of
|
336
|
+
* a `finally` block and should therefore fork the path that leaves
|
337
|
+
* `finally`.
|
237
338
|
* @returns {ForkContext} New fork context.
|
238
339
|
*/
|
239
|
-
static newEmpty(parentContext,
|
340
|
+
static newEmpty(parentContext, shouldForkLeavingPath) {
|
240
341
|
return new ForkContext(
|
241
342
|
parentContext.idGenerator,
|
242
343
|
parentContext,
|
243
|
-
(
|
344
|
+
(shouldForkLeavingPath ? 2 : 1) * parentContext.count
|
244
345
|
);
|
245
346
|
}
|
246
347
|
}
|