Skip to content

Commit e8391d2

Browse files
committed
Ensure work stacks are per-execution
1 parent b76b2d3 commit e8391d2

2 files changed

Lines changed: 95 additions & 123 deletions

File tree

src/compiler/binder.ts

Lines changed: 34 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1502,42 +1502,37 @@ namespace ts {
15021502
}
15031503

15041504
function createBindBinaryExpressionFlow() {
1505-
let stackIndex = -1;
1506-
let inStrictModeStack: (boolean | undefined)[];
1507-
let parentStack: (Node | undefined)[];
1508-
1509-
const trampoline = createBinaryExpressionTrampoline(onEnter, onLeft, onOperator, onRight, onExit, identity);
1510-
return (node: BinaryExpression) => {
1511-
const savedStackIndex = stackIndex;
1512-
const savedInStrictModeStack = inStrictModeStack;
1513-
const savedParentStack = parentStack;
1514-
stackIndex = -1;
1515-
inStrictModeStack = [undefined];
1516-
parentStack = [undefined];
1517-
trampoline(node);
1518-
stackIndex = savedStackIndex;
1519-
inStrictModeStack = savedInStrictModeStack;
1520-
parentStack = savedParentStack;
1521-
};
1505+
interface WorkArea {
1506+
stackIndex: number;
1507+
skip: boolean;
1508+
inStrictModeStack: (boolean | undefined)[];
1509+
parentStack: (Node | undefined)[];
1510+
}
1511+
1512+
return createBinaryExpressionTrampoline(onEnter, onLeft, onOperator, onRight, onExit, identity);
15221513

1523-
function onEnter(node: BinaryExpression, _: boolean | undefined) {
1524-
stackIndex++;
1525-
if (stackIndex > 0) {
1514+
function onEnter(node: BinaryExpression, state: WorkArea | undefined) {
1515+
if (state) {
1516+
state.stackIndex++;
15261517
// Emulate the work that `bind` does before reaching `bindChildren`. A normal call to
15271518
// `bindBinaryExpressionFlow` will already have done this work.
15281519
setParent(node, parent);
15291520
const saveInStrictMode = inStrictMode;
15301521
bindWorker(node);
15311522
const saveParent = parent;
15321523
parent = node;
1533-
inStrictModeStack[stackIndex] = saveInStrictMode;
1534-
parentStack[stackIndex] = saveParent;
1524+
state.skip = false;
1525+
state.inStrictModeStack[state.stackIndex] = saveInStrictMode;
1526+
state.parentStack[state.stackIndex] = saveParent;
15351527
}
15361528
else {
1537-
inStrictModeStack[stackIndex] = undefined;
1538-
parentStack[stackIndex] = undefined;
1529+
state = {
1530+
stackIndex: 0,
1531+
skip: false,
1532+
inStrictModeStack: [undefined],
1533+
parentStack: [undefined]
1534+
};
15391535
}
1540-
15411536
// TODO: bindLogicalExpression is recursive - if we want to handle deeply nested `&&` expressions
15421537
// we'll need to handle the `bindLogicalExpression` scenarios in this state machine, too
15431538
// For now, though, since the common cases are chained `+`, leaving it recursive is fine
@@ -1554,34 +1549,34 @@ namespace ts {
15541549
else {
15551550
bindLogicalLikeExpression(node, currentTrueTarget!, currentFalseTarget!);
15561551
}
1557-
return true;
1552+
state.skip = true;
15581553
}
1559-
return false;
1554+
return state;
15601555
}
15611556

1562-
function onLeft(left: Expression, skip: boolean, _node: BinaryExpression) {
1563-
if (!skip) {
1557+
function onLeft(left: Expression, state: WorkArea, _node: BinaryExpression) {
1558+
if (!state.skip) {
15641559
return maybeBind(left);
15651560
}
15661561
}
15671562

1568-
function onOperator(operatorToken: BinaryOperatorToken, skip: boolean, node: BinaryExpression) {
1569-
if (!skip) {
1563+
function onOperator(operatorToken: BinaryOperatorToken, state: WorkArea, node: BinaryExpression) {
1564+
if (!state.skip) {
15701565
if (operatorToken.kind === SyntaxKind.CommaToken) {
15711566
maybeBindExpressionFlowIfCall(node.left);
15721567
}
15731568
bind(operatorToken);
15741569
}
15751570
}
15761571

1577-
function onRight(right: Expression, skip: boolean, _node: BinaryExpression) {
1578-
if (!skip) {
1572+
function onRight(right: Expression, state: WorkArea, _node: BinaryExpression) {
1573+
if (!state.skip) {
15791574
return maybeBind(right);
15801575
}
15811576
}
15821577

1583-
function onExit(node: BinaryExpression, skip: boolean) {
1584-
if (!skip) {
1578+
function onExit(node: BinaryExpression, state: WorkArea) {
1579+
if (!state.skip) {
15851580
const operator = node.operatorToken.kind;
15861581
if (isAssignmentOperator(operator) && !isAssignmentTarget(node)) {
15871582
bindAssignmentTargetFlow(node.left);
@@ -1593,15 +1588,16 @@ namespace ts {
15931588
}
15941589
}
15951590
}
1596-
const savedInStrictMode = inStrictModeStack[stackIndex];
1597-
const savedParent = parentStack[stackIndex];
1591+
const savedInStrictMode = state.inStrictModeStack[state.stackIndex];
1592+
const savedParent = state.parentStack[state.stackIndex];
15981593
if (savedInStrictMode !== undefined) {
15991594
inStrictMode = savedInStrictMode;
16001595
}
16011596
if (savedParent !== undefined) {
16021597
parent = savedParent;
16031598
}
1604-
stackIndex--;
1599+
state.skip = false;
1600+
state.stackIndex--;
16051601
}
16061602

16071603
function maybeBind(node: Node) {

src/compiler/emitter.ts

Lines changed: 61 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1199,21 +1199,17 @@ namespace ts {
11991199
!isUnparsedPrepend(node);
12001200
}
12011201

1202-
function emitWithContext<T extends Node>(node: T, emitCallback: (node: T) => void) {
1202+
function beforeEmitWithContext(node: Node, shouldEmitComments: boolean, shouldEmitSourceMaps: boolean) {
12031203
onBeforeEmitNode?.(node);
12041204

12051205
const emitFlags = getEmitFlags(node);
1206-
const savedPreserveSourceNewlines = preserveSourceNewlines;
1206+
const commentRange = shouldEmitComments ? getCommentRange(node) : undefined;
1207+
const sourceMapRange = shouldEmitSourceMaps ? getSourceMapRange(node) : undefined;
1208+
12071209
if (preserveSourceNewlines && (emitFlags & EmitFlags.IgnoreSourceNewlines)) {
12081210
preserveSourceNewlines = false;
12091211
}
12101212

1211-
const savedContainerPos = containerPos;
1212-
const savedContainerEnd = containerEnd;
1213-
const savedDeclarationListContainerEnd = declarationListContainerEnd;
1214-
const commentRange = shouldEmitComments(node) ? getCommentRange(node) : undefined;
1215-
const sourceMapRange = shouldEmitSourceMaps(node) ? getSourceMapRange(node) : undefined;
1216-
12171213
// Emit leading comments
12181214
if (commentRange) {
12191215
emitLeadingCommentsOfNode(node, emitFlags, commentRange.pos, commentRange.end);
@@ -1250,8 +1246,12 @@ namespace ts {
12501246
}
12511247
}
12521248
}
1249+
}
12531250

1254-
emitCallback(node);
1251+
function afterEmitWithContext(node: Node, shouldEmitComments: boolean, shouldEmitSourceMaps: boolean, savedContainerPos: number, savedContainerEnd: number, savedDeclarationListContainerEnd: number, savedPreserveSourceNewlines: boolean | undefined) {
1252+
const emitFlags = getEmitFlags(node);
1253+
const commentRange = shouldEmitComments ? getCommentRange(node) : undefined;
1254+
const sourceMapRange = shouldEmitSourceMaps ? getSourceMapRange(node) : undefined;
12551255

12561256
// Emit trailing sourcemap
12571257
if (sourceMapRange) {
@@ -1275,9 +1275,20 @@ namespace ts {
12751275
emitTrailingCommentsOfNode(node, emitFlags, commentRange.pos, commentRange.end, savedContainerPos, savedContainerEnd, savedDeclarationListContainerEnd);
12761276
}
12771277

1278+
preserveSourceNewlines = savedPreserveSourceNewlines;
12781279
onAfterEmitNode?.(node);
1280+
}
12791281

1280-
preserveSourceNewlines = savedPreserveSourceNewlines;
1282+
function emitWithContext<T extends Node>(node: T, emitCallback: (node: T) => void) {
1283+
const savedPreserveSourceNewlines = preserveSourceNewlines;
1284+
const savedContainerPos = containerPos;
1285+
const savedContainerEnd = containerEnd;
1286+
const savedDeclarationListContainerEnd = declarationListContainerEnd;
1287+
const emitComments = shouldEmitComments(node);
1288+
const emitSourceMaps = shouldEmitSourceMaps(node);
1289+
beforeEmitWithContext(node, emitComments, emitSourceMaps);
1290+
emitCallback(node);
1291+
afterEmitWithContext(node, emitComments, emitSourceMaps, savedContainerPos, savedContainerEnd, savedDeclarationListContainerEnd, savedPreserveSourceNewlines);
12811292
}
12821293

12831294
function emitWorker(node: Node): void {
@@ -2533,46 +2544,18 @@ namespace ts {
25332544

25342545
return createBinaryExpressionTrampoline(noop, maybeEmitExpression, onOperator, maybeEmitExpression, onExit, identity);
25352546

2536-
// function onEnter(_node: BinaryExpression, _prev: EmitBinaryExpressionState | undefined) {
2547+
// function onEnter(node: BinaryExpression, prev: EmitBinaryExpressionState | undefined) {
25372548
// const state = new EmitBinaryExpressionState();
25382549
// if (prev) {
25392550
// // `prev` is only defined when recuring. We can use this fact to indicate
25402551
// // we are entering into a nested binary expression and can replicate the
25412552
// // leading comment and sourcemap emit performed by `emitWithContext`.
25422553
// state.nested = true;
2543-
// onBeforeEmitNode?.(node);
2544-
2545-
// state.emitFlags = getEmitFlags(node);
25462554
// state.preserveSourceNewlines = preserveSourceNewlines;
2547-
// if (preserveSourceNewlines && (state.emitFlags & EmitFlags.IgnoreSourceNewlines)) {
2548-
// preserveSourceNewlines = false;
2549-
// }
2550-
25512555
// state.containerPos = containerPos;
25522556
// state.containerEnd = containerEnd;
25532557
// state.declarationListContainerEnd = declarationListContainerEnd;
2554-
// state.commentRange = shouldEmitComments(node) ? getCommentRange(node) : undefined;
2555-
// state.sourceMapRange = shouldEmitSourceMaps(node) ? getSourceMapRange(node) : undefined;
2556-
2557-
// // Emit leading comments
2558-
// if (state.commentRange) {
2559-
// emitLeadingCommentsOfNode(node, state.emitFlags, state.commentRange.pos, state.commentRange.end);
2560-
// if (state.emitFlags & EmitFlags.NoNestedComments) {
2561-
// commentsDisabled = true;
2562-
// }
2563-
// }
2564-
2565-
// // Emit leading sourcemap
2566-
// if (state.sourceMapRange) {
2567-
// const source = state.sourceMapRange.source || sourceMapSource;
2568-
// if ((state.emitFlags & EmitFlags.NoLeadingSourceMap) === 0
2569-
// && state.sourceMapRange.pos >= 0) {
2570-
// emitSourcePos(state.sourceMapRange.source || sourceMapSource, skipSourceTrivia(source, state.sourceMapRange.pos));
2571-
// }
2572-
// if (state.emitFlags & EmitFlags.NoNestedSourceMaps) {
2573-
// sourceMapsDisabled = true;
2574-
// }
2575-
// }
2558+
// beforeEmitWithContext(node);
25762559
// }
25772560
// return state;
25782561
// }
@@ -2592,34 +2575,8 @@ namespace ts {
25922575
const linesBeforeOperator = getLinesBetweenNodes(node, node.left, node.operatorToken);
25932576
const linesAfterOperator = getLinesBetweenNodes(node, node.operatorToken, node.right);
25942577
decreaseIndentIf(linesBeforeOperator, linesAfterOperator);
2595-
25962578
// if (state.nested) {
2597-
// // If we are marked as nested, we are recurring. We can use this fact to indicate
2598-
// // we are exiting from a nested binary expression and can replicate the trailing
2599-
// // comment and sourcemap emit performed by `emitWithContext`.
2600-
2601-
// // Emit trailing sourcemap
2602-
// if (state.sourceMapRange) {
2603-
// if (state.emitFlags & EmitFlags.NoNestedSourceMaps) {
2604-
// sourceMapsDisabled = false;
2605-
// }
2606-
// if ((state.emitFlags & EmitFlags.NoTrailingSourceMap) === 0
2607-
// && state.sourceMapRange.end >= 0) {
2608-
// emitSourcePos(state.sourceMapRange.source || sourceMapSource, state.sourceMapRange.end);
2609-
// }
2610-
// }
2611-
2612-
// // Emit trailing comments
2613-
// if (state.commentRange) {
2614-
// if (state.emitFlags & EmitFlags.NoNestedComments) {
2615-
// commentsDisabled = false;
2616-
// }
2617-
// emitTrailingCommentsOfNode(node, state.emitFlags, state.commentRange.pos, state.commentRange.end, state.containerPos, state.containerEnd, state.declarationListContainerEnd);
2618-
// }
2619-
2620-
// onAfterEmitNode?.(node);
2621-
2622-
// preserveSourceNewlines = state.preserveSourceNewlines;
2579+
// afterEmitWithContext(node, state.containerPos, state.containerEnd, state.declarationListContainerEnd, state.preserveSourceNewlines);
26232580
// }
26242581
}
26252582

@@ -6658,42 +6615,61 @@ namespace ts {
66586615
}
66596616

66606617
function createPreprintBinaryExpression() {
6661-
class PreprintBinaryExpressionState {
6662-
constructor(
6663-
public left: Expression,
6664-
public operator: BinaryOperatorToken,
6665-
public right: Expression
6666-
) {}
6618+
interface WorkArea {
6619+
stackIndex: number;
6620+
leftStack: Expression[];
6621+
operatorStack: BinaryOperatorToken[];
6622+
rightStack: Expression[];
66676623
}
66686624

66696625
return createBinaryExpressionTrampoline(onEnter, onLeft, onOperator, onRight, onExit, foldState);
66706626

6671-
function onEnter(node: BinaryExpression) {
6672-
return new PreprintBinaryExpressionState(node.left, node.operatorToken, node.right);
6627+
function onEnter(node: BinaryExpression, state: WorkArea | undefined) {
6628+
if (state) {
6629+
state.stackIndex++;
6630+
state.leftStack[state.stackIndex] = node.left;
6631+
state.operatorStack[state.stackIndex] = node.operatorToken;
6632+
state.rightStack[state.stackIndex] = node.right;
6633+
}
6634+
else {
6635+
state = {
6636+
stackIndex: 0,
6637+
leftStack: [node.left],
6638+
operatorStack: [node.operatorToken],
6639+
rightStack: [node.right],
6640+
};
6641+
}
6642+
return state;
66736643
}
66746644

6675-
function onLeft(left: Expression, state: PreprintBinaryExpressionState) {
6645+
function onLeft(left: Expression, state: WorkArea, _node: BinaryExpression) {
66766646
return maybeVisitExpression(left, state, "left");
66776647
}
66786648

6679-
function onOperator(operator: BinaryOperatorToken, state: PreprintBinaryExpressionState) {
6680-
state.operator = visit(operator, isBinaryOperatorToken);
6649+
function onOperator(operator: BinaryOperatorToken, state: WorkArea, _node: BinaryExpression) {
6650+
state.operatorStack[state.stackIndex] = visit(operator, isBinaryOperatorToken);
66816651
}
66826652

6683-
function onRight(right: Expression, state: PreprintBinaryExpressionState) {
6653+
function onRight(right: Expression, state: WorkArea, _node: BinaryExpression) {
66846654
return maybeVisitExpression(right, state, "right");
66856655
}
66866656

6687-
function onExit(node: BinaryExpression, state: PreprintBinaryExpressionState) {
6688-
return factory.updateBinaryExpression(node, state.left, state.operator, state.right);
6657+
function onExit(node: BinaryExpression, state: WorkArea) {
6658+
const left = state.leftStack[state.stackIndex];
6659+
const operator = state.operatorStack[state.stackIndex];
6660+
const right = state.rightStack[state.stackIndex];
6661+
if (state.stackIndex > 0) {
6662+
state.stackIndex--;
6663+
}
6664+
return factory.updateBinaryExpression(node, left, operator, right);
66896665
}
66906666

6691-
function foldState(state: PreprintBinaryExpressionState, result: BinaryExpression, side: "left" | "right") {
6692-
state[side] = result;
6667+
function foldState(state: WorkArea, result: BinaryExpression, side: "left" | "right") {
6668+
(side === "left" ? state.leftStack : state.rightStack)[state.stackIndex] = result;
66936669
return state;
66946670
}
66956671

6696-
function maybeVisitExpression(node: Expression, state: PreprintBinaryExpressionState, side: "left" | "right") {
6672+
function maybeVisitExpression(node: Expression, state: WorkArea, side: "left" | "right") {
66976673
// Get the first supported pipeline phase for this node. We can skip several stack
66986674
// frames if we aren't doing emit notification, so we check for substitution and
66996675
// direct callbacks and execute those immediately.
@@ -6711,7 +6687,7 @@ namespace ts {
67116687
}
67126688
else {
67136689
// Visit the expression and store the result on whichever side we are currently visiting.
6714-
state[side] = visitExpression(node, isExpression);
6690+
(side === "left" ? state.leftStack : state.rightStack)[state.stackIndex] = visitExpression(node, isExpression);
67156691
}
67166692
}
67176693
}

0 commit comments

Comments
 (0)