Convert TreeProcessor to a class

This commit is contained in:
Steve Gravrock
2025-08-12 17:43:54 -07:00
parent 8eee6ebb91
commit b009cd2922
2 changed files with 352 additions and 320 deletions
+177 -161
View File
@@ -1712,7 +1712,7 @@ getJasmineRequireObj().Env = function(j$) {
totalSpecsDefined: () => suiteBuilder.totalSpecsDefined, totalSpecsDefined: () => suiteBuilder.totalSpecsDefined,
focusedRunables: () => suiteBuilder.focusedRunables, focusedRunables: () => suiteBuilder.focusedRunables,
runableResources, runableResources,
reporter: reportDispatcher, reportDispatcher,
runQueue, runQueue,
TreeProcessor: j$.TreeProcessor, TreeProcessor: j$.TreeProcessor,
globalErrors, globalErrors,
@@ -9421,7 +9421,7 @@ getJasmineRequireObj().Runner = function(j$) {
this.#runQueue = options.runQueue; this.#runQueue = options.runQueue;
this.#TreeProcessor = options.TreeProcessor; this.#TreeProcessor = options.TreeProcessor;
this.#globalErrors = options.globalErrors; this.#globalErrors = options.globalErrors;
this.#reportDispatcher = options.reporter; this.#reportDispatcher = options.reportDispatcher;
this.#getConfig = options.getConfig; this.#getConfig = options.getConfig;
this.#reportSpecDone = options.reportSpecDone; this.#reportSpecDone = options.reportSpecDone;
this.hasFailures = false; this.hasFailures = false;
@@ -11305,81 +11305,97 @@ getJasmineRequireObj().Timer = function() {
}; };
getJasmineRequireObj().TreeProcessor = function() { getJasmineRequireObj().TreeProcessor = function() {
function TreeProcessor(attrs) { const defaultMin = Infinity;
const tree = attrs.tree; const defaultMax = 1 - Infinity;
const runnableIds = attrs.runnableIds;
const runQueue = attrs.runQueue;
const nodeStart = attrs.nodeStart || function() {};
const nodeComplete = attrs.nodeComplete || function() {};
const failSpecWithNoExpectations = !!attrs.failSpecWithNoExpectations;
const detectLateRejectionHandling = !!attrs.detectLateRejectionHandling;
const globalErrors = attrs.globalErrors;
const orderChildren = class TreeProcessor {
attrs.orderChildren || #tree;
function(node) { #runQueue;
return node.children; #runnableIds;
}; #nodeStart;
const excludeNode = #nodeComplete;
attrs.excludeNode || #failSpecWithNoExpectations;
function(node) { #detectLateRejectionHandling;
return false; #globalErrors;
}; #orderChildren;
let stats = { valid: true }; #excludeNode;
let processed = false; #stats;
const defaultMin = Infinity; #processed;
const defaultMax = 1 - Infinity;
this.processTree = function() { constructor(attrs) {
processNode(tree, true); this.#tree = attrs.tree;
processed = true; this.#runnableIds = attrs.runnableIds;
return stats; this.#runQueue = attrs.runQueue;
}; this.#nodeStart = attrs.nodeStart || function() {};
this.#nodeComplete = attrs.nodeComplete || function() {};
this.#failSpecWithNoExpectations = !!attrs.failSpecWithNoExpectations;
this.#detectLateRejectionHandling = !!attrs.detectLateRejectionHandling;
this.#globalErrors = attrs.globalErrors;
this.execute = async function() { this.#orderChildren =
if (!processed) { attrs.orderChildren ||
function(node) {
return node.children;
};
this.#excludeNode =
attrs.excludeNode ||
function(node) {
return false;
};
this.#stats = { valid: true };
this.#processed = false;
}
processTree() {
this.#processNode(this.#tree, true);
this.#processed = true;
return this.#stats;
}
async execute() {
if (!this.#processed) {
this.processTree(); this.processTree();
} }
if (!stats.valid) { if (!this.#stats.valid) {
throw 'invalid order'; throw 'invalid order';
} }
const childFns = wrapChildren(tree, 0); const childFns = this.#wrapChildren(this.#tree, 0);
await new Promise(function(resolve) { await new Promise(resolve => {
runQueue({ this.#runQueue({
queueableFns: childFns, queueableFns: childFns,
userContext: tree.sharedUserContext(), userContext: this.#tree.sharedUserContext(),
onException: function() { onException: function() {
tree.handleException.apply(tree, arguments); this.#tree.handleException.apply(this.#tree, arguments);
}, }.bind(this),
onComplete: resolve, onComplete: resolve,
onMultipleDone: tree.onMultipleDone onMultipleDone: this.#tree.onMultipleDone
? tree.onMultipleDone.bind(tree) ? this.#tree.onMultipleDone.bind(this.#tree)
: null : null
}); });
}); });
}; }
function runnableIndex(id) { #runnableIndex(id) {
for (let i = 0; i < runnableIds.length; i++) { for (let i = 0; i < this.#runnableIds.length; i++) {
if (runnableIds[i] === id) { if (this.#runnableIds[i] === id) {
return i; return i;
} }
} }
} }
function processNode(node, parentExcluded) { #processNode(node, parentExcluded) {
const executableIndex = runnableIndex(node.id); const executableIndex = this.#runnableIndex(node.id);
if (executableIndex !== undefined) { if (executableIndex !== undefined) {
parentExcluded = false; parentExcluded = false;
} }
if (!node.children) { if (!node.children) {
const excluded = parentExcluded || excludeNode(node); const excluded = parentExcluded || this.#excludeNode(node);
stats[node.id] = { this.#stats[node.id] = {
excluded: excluded, excluded: excluded,
willExecute: !excluded && !node.markedPending, willExecute: !excluded && !node.markedPending,
segments: [ segments: [
@@ -11395,138 +11411,76 @@ getJasmineRequireObj().TreeProcessor = function() {
} else { } else {
let hasExecutableChild = false; let hasExecutableChild = false;
const orderedChildren = orderChildren(node); const orderedChildren = this.#orderChildren(node);
for (let i = 0; i < orderedChildren.length; i++) { for (let i = 0; i < orderedChildren.length; i++) {
const child = orderedChildren[i]; const child = orderedChildren[i];
processNode(child, parentExcluded); this.#processNode(child, parentExcluded);
if (!stats.valid) { if (!this.#stats.valid) {
return; return;
} }
const childStats = stats[child.id]; const childStats = this.#stats[child.id];
hasExecutableChild = hasExecutableChild || childStats.willExecute; hasExecutableChild = hasExecutableChild || childStats.willExecute;
} }
stats[node.id] = { this.#stats[node.id] = {
excluded: parentExcluded, excluded: parentExcluded,
willExecute: hasExecutableChild willExecute: hasExecutableChild
}; };
segmentChildren(node, orderedChildren, stats[node.id], executableIndex); segmentChildren(node, orderedChildren, this.#stats, executableIndex);
if (!node.canBeReentered() && stats[node.id].segments.length > 1) { if (
stats = { valid: false }; !node.canBeReentered() &&
this.#stats[node.id].segments.length > 1
) {
this.#stats = { valid: false };
} }
} }
} }
function startingMin(executableIndex) { #wrapChildren(node, segmentNumber) {
return executableIndex === undefined ? defaultMin : executableIndex; const result = [],
} segmentChildren = this.#stats[node.id].segments[segmentNumber].nodes;
function startingMax(executableIndex) { for (let i = 0; i < segmentChildren.length; i++) {
return executableIndex === undefined ? defaultMax : executableIndex; result.push(
} this.#executeNode(segmentChildren[i].owner, segmentChildren[i].index)
function segmentChildren(
node,
orderedChildren,
nodeStats,
executableIndex
) {
let currentSegment = {
index: 0,
owner: node,
nodes: [],
min: startingMin(executableIndex),
max: startingMax(executableIndex)
},
result = [currentSegment],
lastMax = defaultMax,
orderedChildSegments = orderChildSegments(orderedChildren);
function isSegmentBoundary(minIndex) {
return (
lastMax !== defaultMax &&
minIndex !== defaultMin &&
lastMax < minIndex - 1
); );
} }
for (let i = 0; i < orderedChildSegments.length; i++) { if (!this.#stats[node.id].willExecute) {
const childSegment = orderedChildSegments[i], return result;
maxIndex = childSegment.max,
minIndex = childSegment.min;
if (isSegmentBoundary(minIndex)) {
currentSegment = {
index: result.length,
owner: node,
nodes: [],
min: defaultMin,
max: defaultMax
};
result.push(currentSegment);
}
currentSegment.nodes.push(childSegment);
currentSegment.min = Math.min(currentSegment.min, minIndex);
currentSegment.max = Math.max(currentSegment.max, maxIndex);
lastMax = maxIndex;
} }
nodeStats.segments = result; return node.beforeAllFns.concat(result).concat(node.afterAllFns);
} }
function orderChildSegments(children) { #executeNode(node, segmentNumber) {
const specifiedOrder = [],
unspecifiedOrder = [];
for (let i = 0; i < children.length; i++) {
const child = children[i],
segments = stats[child.id].segments;
for (let j = 0; j < segments.length; j++) {
const seg = segments[j];
if (seg.min === defaultMin) {
unspecifiedOrder.push(seg);
} else {
specifiedOrder.push(seg);
}
}
}
specifiedOrder.sort(function(a, b) {
return a.min - b.min;
});
return specifiedOrder.concat(unspecifiedOrder);
}
function executeNode(node, segmentNumber) {
if (node.children) { if (node.children) {
return { return {
fn: function(done) { fn: function(done) {
const onStart = { const onStart = {
fn: function(next) { fn: next => {
nodeStart(node, next); this.#nodeStart(node, next);
} }
}; };
runQueue({ this.#runQueue({
onComplete: function() { onComplete: function() {
const args = Array.prototype.slice.call(arguments, [0]); const args = Array.prototype.slice.call(arguments, [0]);
node.cleanupBeforeAfter(); node.cleanupBeforeAfter();
nodeComplete(node, node.getResult(), function() { this.#nodeComplete(node, node.getResult(), () => {
done.apply(undefined, args); done.apply(undefined, args);
}); });
}, }.bind(this),
queueableFns: [onStart].concat(wrapChildren(node, segmentNumber)), queueableFns: [onStart].concat(
this.#wrapChildren(node, segmentNumber)
),
userContext: node.sharedUserContext(), userContext: node.sharedUserContext(),
onException: function() { onException: function() {
node.handleException.apply(node, arguments); node.handleException.apply(node, arguments);
@@ -11535,40 +11489,102 @@ getJasmineRequireObj().TreeProcessor = function() {
? node.onMultipleDone.bind(node) ? node.onMultipleDone.bind(node)
: null : null
}); });
} }.bind(this)
}; };
} else { } else {
return { return {
fn: function(done) { fn: done => {
node.execute( node.execute(
runQueue, this.#runQueue,
globalErrors, this.#globalErrors,
done, done,
stats[node.id].excluded, this.#stats[node.id].excluded,
failSpecWithNoExpectations, this.#failSpecWithNoExpectations,
detectLateRejectionHandling this.#detectLateRejectionHandling
); );
} }
}; };
} }
} }
}
function wrapChildren(node, segmentNumber) { function segmentChildren(node, orderedChildren, stats, executableIndex) {
const result = [], let currentSegment = {
segmentChildren = stats[node.id].segments[segmentNumber].nodes; index: 0,
owner: node,
nodes: [],
min: startingMin(executableIndex),
max: startingMax(executableIndex)
},
result = [currentSegment],
lastMax = defaultMax,
orderedChildSegments = orderChildSegments(orderedChildren, stats);
for (let i = 0; i < segmentChildren.length; i++) { function isSegmentBoundary(minIndex) {
result.push( return (
executeNode(segmentChildren[i].owner, segmentChildren[i].index) lastMax !== defaultMax &&
); minIndex !== defaultMin &&
} lastMax < minIndex - 1
);
if (!stats[node.id].willExecute) {
return result;
}
return node.beforeAllFns.concat(result).concat(node.afterAllFns);
} }
for (let i = 0; i < orderedChildSegments.length; i++) {
const childSegment = orderedChildSegments[i],
maxIndex = childSegment.max,
minIndex = childSegment.min;
if (isSegmentBoundary(minIndex)) {
currentSegment = {
index: result.length,
owner: node,
nodes: [],
min: defaultMin,
max: defaultMax
};
result.push(currentSegment);
}
currentSegment.nodes.push(childSegment);
currentSegment.min = Math.min(currentSegment.min, minIndex);
currentSegment.max = Math.max(currentSegment.max, maxIndex);
lastMax = maxIndex;
}
stats[node.id].segments = result;
}
function orderChildSegments(children, stats) {
const specifiedOrder = [],
unspecifiedOrder = [];
for (let i = 0; i < children.length; i++) {
const child = children[i],
segments = stats[child.id].segments;
for (let j = 0; j < segments.length; j++) {
const seg = segments[j];
if (seg.min === defaultMin) {
unspecifiedOrder.push(seg);
} else {
specifiedOrder.push(seg);
}
}
}
specifiedOrder.sort(function(a, b) {
return a.min - b.min;
});
return specifiedOrder.concat(unspecifiedOrder);
}
function startingMin(executableIndex) {
return executableIndex === undefined ? defaultMin : executableIndex;
}
function startingMax(executableIndex) {
return executableIndex === undefined ? defaultMax : executableIndex;
} }
return TreeProcessor; return TreeProcessor;
+175 -159
View File
@@ -1,79 +1,95 @@
getJasmineRequireObj().TreeProcessor = function() { getJasmineRequireObj().TreeProcessor = function() {
function TreeProcessor(attrs) { const defaultMin = Infinity;
const tree = attrs.tree; const defaultMax = 1 - Infinity;
const runnableIds = attrs.runnableIds;
const runQueue = attrs.runQueue;
const nodeStart = attrs.nodeStart || function() {};
const nodeComplete = attrs.nodeComplete || function() {};
const failSpecWithNoExpectations = !!attrs.failSpecWithNoExpectations;
const detectLateRejectionHandling = !!attrs.detectLateRejectionHandling;
const globalErrors = attrs.globalErrors;
const orderChildren = class TreeProcessor {
attrs.orderChildren || #tree;
function(node) { #runQueue;
return node.children; #runnableIds;
}; #nodeStart;
const excludeNode = #nodeComplete;
attrs.excludeNode || #failSpecWithNoExpectations;
function(node) { #detectLateRejectionHandling;
return false; #globalErrors;
}; #orderChildren;
let stats = { valid: true }; #excludeNode;
let processed = false; #stats;
const defaultMin = Infinity; #processed;
const defaultMax = 1 - Infinity;
this.processTree = function() { constructor(attrs) {
processNode(tree, true); this.#tree = attrs.tree;
processed = true; this.#runnableIds = attrs.runnableIds;
return stats; this.#runQueue = attrs.runQueue;
}; this.#nodeStart = attrs.nodeStart || function() {};
this.#nodeComplete = attrs.nodeComplete || function() {};
this.#failSpecWithNoExpectations = !!attrs.failSpecWithNoExpectations;
this.#detectLateRejectionHandling = !!attrs.detectLateRejectionHandling;
this.#globalErrors = attrs.globalErrors;
this.execute = async function() { this.#orderChildren =
if (!processed) { attrs.orderChildren ||
function(node) {
return node.children;
};
this.#excludeNode =
attrs.excludeNode ||
function(node) {
return false;
};
this.#stats = { valid: true };
this.#processed = false;
}
processTree() {
this.#processNode(this.#tree, true);
this.#processed = true;
return this.#stats;
}
async execute() {
if (!this.#processed) {
this.processTree(); this.processTree();
} }
if (!stats.valid) { if (!this.#stats.valid) {
throw 'invalid order'; throw 'invalid order';
} }
const childFns = wrapChildren(tree, 0); const childFns = this.#wrapChildren(this.#tree, 0);
await new Promise(function(resolve) { await new Promise(resolve => {
runQueue({ this.#runQueue({
queueableFns: childFns, queueableFns: childFns,
userContext: tree.sharedUserContext(), userContext: this.#tree.sharedUserContext(),
onException: function() { onException: function() {
tree.handleException.apply(tree, arguments); this.#tree.handleException.apply(this.#tree, arguments);
}, }.bind(this),
onComplete: resolve, onComplete: resolve,
onMultipleDone: tree.onMultipleDone onMultipleDone: this.#tree.onMultipleDone
? tree.onMultipleDone.bind(tree) ? this.#tree.onMultipleDone.bind(this.#tree)
: null : null
}); });
}); });
}; }
function runnableIndex(id) { #runnableIndex(id) {
for (let i = 0; i < runnableIds.length; i++) { for (let i = 0; i < this.#runnableIds.length; i++) {
if (runnableIds[i] === id) { if (this.#runnableIds[i] === id) {
return i; return i;
} }
} }
} }
function processNode(node, parentExcluded) { #processNode(node, parentExcluded) {
const executableIndex = runnableIndex(node.id); const executableIndex = this.#runnableIndex(node.id);
if (executableIndex !== undefined) { if (executableIndex !== undefined) {
parentExcluded = false; parentExcluded = false;
} }
if (!node.children) { if (!node.children) {
const excluded = parentExcluded || excludeNode(node); const excluded = parentExcluded || this.#excludeNode(node);
stats[node.id] = { this.#stats[node.id] = {
excluded: excluded, excluded: excluded,
willExecute: !excluded && !node.markedPending, willExecute: !excluded && !node.markedPending,
segments: [ segments: [
@@ -89,138 +105,76 @@ getJasmineRequireObj().TreeProcessor = function() {
} else { } else {
let hasExecutableChild = false; let hasExecutableChild = false;
const orderedChildren = orderChildren(node); const orderedChildren = this.#orderChildren(node);
for (let i = 0; i < orderedChildren.length; i++) { for (let i = 0; i < orderedChildren.length; i++) {
const child = orderedChildren[i]; const child = orderedChildren[i];
processNode(child, parentExcluded); this.#processNode(child, parentExcluded);
if (!stats.valid) { if (!this.#stats.valid) {
return; return;
} }
const childStats = stats[child.id]; const childStats = this.#stats[child.id];
hasExecutableChild = hasExecutableChild || childStats.willExecute; hasExecutableChild = hasExecutableChild || childStats.willExecute;
} }
stats[node.id] = { this.#stats[node.id] = {
excluded: parentExcluded, excluded: parentExcluded,
willExecute: hasExecutableChild willExecute: hasExecutableChild
}; };
segmentChildren(node, orderedChildren, stats[node.id], executableIndex); segmentChildren(node, orderedChildren, this.#stats, executableIndex);
if (!node.canBeReentered() && stats[node.id].segments.length > 1) { if (
stats = { valid: false }; !node.canBeReentered() &&
this.#stats[node.id].segments.length > 1
) {
this.#stats = { valid: false };
} }
} }
} }
function startingMin(executableIndex) { #wrapChildren(node, segmentNumber) {
return executableIndex === undefined ? defaultMin : executableIndex; const result = [],
} segmentChildren = this.#stats[node.id].segments[segmentNumber].nodes;
function startingMax(executableIndex) { for (let i = 0; i < segmentChildren.length; i++) {
return executableIndex === undefined ? defaultMax : executableIndex; result.push(
} this.#executeNode(segmentChildren[i].owner, segmentChildren[i].index)
function segmentChildren(
node,
orderedChildren,
nodeStats,
executableIndex
) {
let currentSegment = {
index: 0,
owner: node,
nodes: [],
min: startingMin(executableIndex),
max: startingMax(executableIndex)
},
result = [currentSegment],
lastMax = defaultMax,
orderedChildSegments = orderChildSegments(orderedChildren);
function isSegmentBoundary(minIndex) {
return (
lastMax !== defaultMax &&
minIndex !== defaultMin &&
lastMax < minIndex - 1
); );
} }
for (let i = 0; i < orderedChildSegments.length; i++) { if (!this.#stats[node.id].willExecute) {
const childSegment = orderedChildSegments[i], return result;
maxIndex = childSegment.max,
minIndex = childSegment.min;
if (isSegmentBoundary(minIndex)) {
currentSegment = {
index: result.length,
owner: node,
nodes: [],
min: defaultMin,
max: defaultMax
};
result.push(currentSegment);
}
currentSegment.nodes.push(childSegment);
currentSegment.min = Math.min(currentSegment.min, minIndex);
currentSegment.max = Math.max(currentSegment.max, maxIndex);
lastMax = maxIndex;
} }
nodeStats.segments = result; return node.beforeAllFns.concat(result).concat(node.afterAllFns);
} }
function orderChildSegments(children) { #executeNode(node, segmentNumber) {
const specifiedOrder = [],
unspecifiedOrder = [];
for (let i = 0; i < children.length; i++) {
const child = children[i],
segments = stats[child.id].segments;
for (let j = 0; j < segments.length; j++) {
const seg = segments[j];
if (seg.min === defaultMin) {
unspecifiedOrder.push(seg);
} else {
specifiedOrder.push(seg);
}
}
}
specifiedOrder.sort(function(a, b) {
return a.min - b.min;
});
return specifiedOrder.concat(unspecifiedOrder);
}
function executeNode(node, segmentNumber) {
if (node.children) { if (node.children) {
return { return {
fn: function(done) { fn: function(done) {
const onStart = { const onStart = {
fn: function(next) { fn: next => {
nodeStart(node, next); this.#nodeStart(node, next);
} }
}; };
runQueue({ this.#runQueue({
onComplete: function() { onComplete: function() {
const args = Array.prototype.slice.call(arguments, [0]); const args = Array.prototype.slice.call(arguments, [0]);
node.cleanupBeforeAfter(); node.cleanupBeforeAfter();
nodeComplete(node, node.getResult(), function() { this.#nodeComplete(node, node.getResult(), () => {
done.apply(undefined, args); done.apply(undefined, args);
}); });
}, }.bind(this),
queueableFns: [onStart].concat(wrapChildren(node, segmentNumber)), queueableFns: [onStart].concat(
this.#wrapChildren(node, segmentNumber)
),
userContext: node.sharedUserContext(), userContext: node.sharedUserContext(),
onException: function() { onException: function() {
node.handleException.apply(node, arguments); node.handleException.apply(node, arguments);
@@ -229,40 +183,102 @@ getJasmineRequireObj().TreeProcessor = function() {
? node.onMultipleDone.bind(node) ? node.onMultipleDone.bind(node)
: null : null
}); });
} }.bind(this)
}; };
} else { } else {
return { return {
fn: function(done) { fn: done => {
node.execute( node.execute(
runQueue, this.#runQueue,
globalErrors, this.#globalErrors,
done, done,
stats[node.id].excluded, this.#stats[node.id].excluded,
failSpecWithNoExpectations, this.#failSpecWithNoExpectations,
detectLateRejectionHandling this.#detectLateRejectionHandling
); );
} }
}; };
} }
} }
}
function wrapChildren(node, segmentNumber) { function segmentChildren(node, orderedChildren, stats, executableIndex) {
const result = [], let currentSegment = {
segmentChildren = stats[node.id].segments[segmentNumber].nodes; index: 0,
owner: node,
nodes: [],
min: startingMin(executableIndex),
max: startingMax(executableIndex)
},
result = [currentSegment],
lastMax = defaultMax,
orderedChildSegments = orderChildSegments(orderedChildren, stats);
for (let i = 0; i < segmentChildren.length; i++) { function isSegmentBoundary(minIndex) {
result.push( return (
executeNode(segmentChildren[i].owner, segmentChildren[i].index) lastMax !== defaultMax &&
); minIndex !== defaultMin &&
} lastMax < minIndex - 1
);
if (!stats[node.id].willExecute) {
return result;
}
return node.beforeAllFns.concat(result).concat(node.afterAllFns);
} }
for (let i = 0; i < orderedChildSegments.length; i++) {
const childSegment = orderedChildSegments[i],
maxIndex = childSegment.max,
minIndex = childSegment.min;
if (isSegmentBoundary(minIndex)) {
currentSegment = {
index: result.length,
owner: node,
nodes: [],
min: defaultMin,
max: defaultMax
};
result.push(currentSegment);
}
currentSegment.nodes.push(childSegment);
currentSegment.min = Math.min(currentSegment.min, minIndex);
currentSegment.max = Math.max(currentSegment.max, maxIndex);
lastMax = maxIndex;
}
stats[node.id].segments = result;
}
function orderChildSegments(children, stats) {
const specifiedOrder = [],
unspecifiedOrder = [];
for (let i = 0; i < children.length; i++) {
const child = children[i],
segments = stats[child.id].segments;
for (let j = 0; j < segments.length; j++) {
const seg = segments[j];
if (seg.min === defaultMin) {
unspecifiedOrder.push(seg);
} else {
specifiedOrder.push(seg);
}
}
}
specifiedOrder.sort(function(a, b) {
return a.min - b.min;
});
return specifiedOrder.concat(unspecifiedOrder);
}
function startingMin(executableIndex) {
return executableIndex === undefined ? defaultMin : executableIndex;
}
function startingMax(executableIndex) {
return executableIndex === undefined ? defaultMax : executableIndex;
} }
return TreeProcessor; return TreeProcessor;