0
0
mirror of https://github.com/nodejs/node.git synced 2024-12-01 16:10:02 +01:00
nodejs/tools/eslint/lib/code-path-analysis/fork-context.js
Rich Trott f44969a5ab tools: update ESLint to current version
We have been stalled on ESLint 3.8.0 for some time. Current ESLint is
3.13.0. We have been unable to upgrade because of more aggressive
reporting on some rules, including indentation.

ESLint configuration options and bugfixes are now such that we can
reasonably upgrade.

PR-URL: https://github.com/nodejs/node/pull/10561
Reviewed-By: Teddy Katz <teddy.katz@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Sam Roberts <vieuxtech@gmail.com>
2017-01-06 14:36:27 -08:00

262 lines
7.4 KiB
JavaScript

/**
* @fileoverview A class to operate forking.
*
* This is state of forking.
* This has a fork list and manages it.
*
* @author Toru Nagashima
*/
"use strict";
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const assert = require("assert"),
CodePathSegment = require("./code-path-segment");
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
/**
* Gets whether or not a given segment is reachable.
*
* @param {CodePathSegment} segment - A segment to get.
* @returns {boolean} `true` if the segment is reachable.
*/
function isReachable(segment) {
return segment.reachable;
}
/**
* Creates new segments from the specific range of `context.segmentsList`.
*
* When `context.segmentsList` is `[[a, b], [c, d], [e, f]]`, `begin` is `0`, and
* `end` is `-1`, this creates `[g, h]`. This `g` is from `a`, `c`, and `e`.
* This `h` is from `b`, `d`, and `f`.
*
* @param {ForkContext} context - An instance.
* @param {number} begin - The first index of the previous segments.
* @param {number} end - The last index of the previous segments.
* @param {Function} create - A factory function of new segments.
* @returns {CodePathSegment[]} New segments.
*/
function makeSegments(context, begin, end, create) {
const list = context.segmentsList;
if (begin < 0) {
begin = list.length + begin;
}
if (end < 0) {
end = list.length + end;
}
const segments = [];
for (let i = 0; i < context.count; ++i) {
const allPrevSegments = [];
for (let j = begin; j <= end; ++j) {
allPrevSegments.push(list[j][i]);
}
segments.push(create(context.idGenerator.next(), allPrevSegments));
}
return segments;
}
/**
* `segments` becomes doubly in a `finally` block. Then if a code path exits by a
* control statement (such as `break`, `continue`) from the `finally` block, the
* destination's segments may be half of the source segments. In that case, this
* merges segments.
*
* @param {ForkContext} context - An instance.
* @param {CodePathSegment[]} segments - Segments to merge.
* @returns {CodePathSegment[]} The merged segments.
*/
function mergeExtraSegments(context, segments) {
while (segments.length > context.count) {
const merged = [];
for (let i = 0, length = segments.length / 2 | 0; i < length; ++i) {
merged.push(CodePathSegment.newNext(
context.idGenerator.next(),
[segments[i], segments[i + length]]
));
}
segments = merged;
}
return segments;
}
//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------
/**
* A class to manage forking.
*/
class ForkContext {
/**
* @param {IdGenerator} idGenerator - An identifier generator for segments.
* @param {ForkContext|null} upper - An upper fork context.
* @param {number} count - A number of parallel segments.
*/
constructor(idGenerator, upper, count) {
this.idGenerator = idGenerator;
this.upper = upper;
this.count = count;
this.segmentsList = [];
}
/**
* The head segments.
* @type {CodePathSegment[]}
*/
get head() {
const list = this.segmentsList;
return list.length === 0 ? [] : list[list.length - 1];
}
/**
* A flag which shows empty.
* @type {boolean}
*/
get empty() {
return this.segmentsList.length === 0;
}
/**
* A flag which shows reachable.
* @type {boolean}
*/
get reachable() {
const segments = this.head;
return segments.length > 0 && segments.some(isReachable);
}
/**
* Creates new segments from this context.
*
* @param {number} begin - The first index of previous segments.
* @param {number} end - The last index of previous segments.
* @returns {CodePathSegment[]} New segments.
*/
makeNext(begin, end) {
return makeSegments(this, begin, end, CodePathSegment.newNext);
}
/**
* Creates new segments from this context.
* The new segments is always unreachable.
*
* @param {number} begin - The first index of previous segments.
* @param {number} end - The last index of previous segments.
* @returns {CodePathSegment[]} New segments.
*/
makeUnreachable(begin, end) {
return makeSegments(this, begin, end, CodePathSegment.newUnreachable);
}
/**
* Creates new segments from this context.
* The new segments don't have connections for previous segments.
* But these inherit the reachable flag from this context.
*
* @param {number} begin - The first index of previous segments.
* @param {number} end - The last index of previous segments.
* @returns {CodePathSegment[]} New segments.
*/
makeDisconnected(begin, end) {
return makeSegments(this, begin, end, CodePathSegment.newDisconnected);
}
/**
* Adds segments into this context.
* The added segments become the head.
*
* @param {CodePathSegment[]} segments - Segments to add.
* @returns {void}
*/
add(segments) {
assert(segments.length >= this.count, `${segments.length} >= ${this.count}`);
this.segmentsList.push(mergeExtraSegments(this, segments));
}
/**
* Replaces the head segments with given segments.
* The current head segments are removed.
*
* @param {CodePathSegment[]} segments - Segments to add.
* @returns {void}
*/
replaceHead(segments) {
assert(segments.length >= this.count, `${segments.length} >= ${this.count}`);
this.segmentsList.splice(-1, 1, mergeExtraSegments(this, segments));
}
/**
* Adds all segments of a given fork context into this context.
*
* @param {ForkContext} context - A fork context to add.
* @returns {void}
*/
addAll(context) {
assert(context.count === this.count);
const source = context.segmentsList;
for (let i = 0; i < source.length; ++i) {
this.segmentsList.push(source[i]);
}
}
/**
* Clears all secments in this context.
*
* @returns {void}
*/
clear() {
this.segmentsList = [];
}
/**
* Creates the root fork context.
*
* @param {IdGenerator} idGenerator - An identifier generator for segments.
* @returns {ForkContext} New fork context.
*/
static newRoot(idGenerator) {
const context = new ForkContext(idGenerator, null, 1);
context.add([CodePathSegment.newRoot(idGenerator.next())]);
return context;
}
/**
* Creates an empty fork context preceded by a given context.
*
* @param {ForkContext} parentContext - The parent fork context.
* @param {boolean} forkLeavingPath - A flag which shows inside of `finally` block.
* @returns {ForkContext} New fork context.
*/
static newEmpty(parentContext, forkLeavingPath) {
return new ForkContext(
parentContext.idGenerator,
parentContext,
(forkLeavingPath ? 2 : 1) * parentContext.count);
}
}
module.exports = ForkContext;