]>
git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/linter/code-path-analysis/code-path.js
2 * @fileoverview A class of the code path.
3 * @author Toru Nagashima
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
12 const CodePathState
= require("./code-path-state");
13 const IdGenerator
= require("./id-generator");
15 //------------------------------------------------------------------------------
17 //------------------------------------------------------------------------------
25 * Creates a new instance.
26 * @param {Object} options Options for the function (see below).
27 * @param {string} options.id An identifier.
28 * @param {string} options.origin The type of code path origin.
29 * @param {CodePath|null} options.upper The code path of the upper function scope.
30 * @param {Function} options.onLooped A callback function to notify looping.
32 constructor({ id
, origin
, upper
, onLooped
}) {
35 * The identifier of this code path.
36 * Rules use it to store additional information of each rule.
42 * The reason that this code path was started. May be "program",
43 * "function", "class-field-initializer", or "class-static-block".
49 * The code path of the upper function scope.
50 * @type {CodePath|null}
55 * The code paths of nested function scopes.
58 this.childCodePaths
= [];
60 // Initializes internal state.
61 Object
.defineProperty(
64 { value
: new CodePathState(new IdGenerator(`${id}_`), onLooped
) }
67 // Adds this into `childCodePaths` of `upper`.
69 upper
.childCodePaths
.push(this);
74 * Gets the state of a given code path.
75 * @param {CodePath} codePath A code path to get.
76 * @returns {CodePathState} The state of the code path.
78 static getState(codePath
) {
79 return codePath
.internal;
83 * The initial code path segment.
84 * @type {CodePathSegment}
86 get initialSegment() {
87 return this.internal.initialSegment
;
91 * Final code path segments.
92 * This array is a mix of `returnedSegments` and `thrownSegments`.
93 * @type {CodePathSegment[]}
96 return this.internal.finalSegments
;
100 * Final code path segments which is with `return` statements.
101 * This array contains the last path segment if it's reachable.
102 * Since the reachable last path returns `undefined`.
103 * @type {CodePathSegment[]}
105 get returnedSegments() {
106 return this.internal.returnedForkContext
;
110 * Final code path segments which is with `throw` statements.
111 * @type {CodePathSegment[]}
113 get thrownSegments() {
114 return this.internal.thrownForkContext
;
118 * Current code path segments.
119 * @type {CodePathSegment[]}
121 get currentSegments() {
122 return this.internal.currentSegments
;
126 * Traverses all segments in this code path.
128 * codePath.traverseSegments(function(segment, controller) {
132 * This method enumerates segments in order from the head.
134 * The `controller` object has two methods.
136 * - `controller.skip()` - Skip the following segments in this branch.
137 * - `controller.break()` - Skip all following segments.
138 * @param {Object} [options] Omittable.
139 * @param {CodePathSegment} [options.first] The first segment to traverse.
140 * @param {CodePathSegment} [options.last] The last segment to traverse.
141 * @param {Function} callback A callback function.
144 traverseSegments(options
, callback
) {
146 let resolvedCallback
;
148 if (typeof options
=== "function") {
149 resolvedCallback
= options
;
150 resolvedOptions
= {};
152 resolvedOptions
= options
|| {};
153 resolvedCallback
= callback
;
156 const startSegment
= resolvedOptions
.first
|| this.internal.initialSegment
;
157 const lastSegment
= resolvedOptions
.last
;
163 const visited
= Object
.create(null);
164 const stack
= [[startSegment
, 0]];
165 let skippedSegment
= null;
169 if (stack
.length
<= 1) {
172 skippedSegment
= stack
[stack
.length
- 2][0];
181 * Checks a given previous segment has been visited.
182 * @param {CodePathSegment} prevSegment A previous segment to check.
183 * @returns {boolean} `true` if the segment has been visited.
185 function isVisited(prevSegment
) {
187 visited
[prevSegment
.id
] ||
188 segment
.isLoopedPrevSegment(prevSegment
)
192 while (stack
.length
> 0) {
193 item
= stack
[stack
.length
- 1];
199 // Skip if this segment has been visited already.
200 if (visited
[segment
.id
]) {
205 // Skip if all previous segments have not been visited.
206 if (segment
!== startSegment
&&
207 segment
.prevSegments
.length
> 0 &&
208 !segment
.prevSegments
.every(isVisited
)
214 // Reset the flag of skipping if all branches have been skipped.
215 if (skippedSegment
&& segment
.prevSegments
.includes(skippedSegment
)) {
216 skippedSegment
= null;
218 visited
[segment
.id
] = true;
220 // Call the callback when the first time.
221 if (!skippedSegment
) {
222 resolvedCallback
.call(this, segment
, controller
);
223 if (segment
=== lastSegment
) {
233 end
= segment
.nextSegments
.length
- 1;
236 stack
.push([segment
.nextSegments
[index
], 0]);
237 } else if (index
=== end
) {
238 item
[0] = segment
.nextSegments
[index
];
247 module
.exports
= CodePath
;