Skip to content

Commit 207932a

Browse files
authored
Move incremental state to WeakSets (#58087)
1 parent 17e420d commit 207932a

File tree

1 file changed

+45
-42
lines changed

1 file changed

+45
-42
lines changed

src/compiler/parser.ts

Lines changed: 45 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,6 @@ import {
318318
QuestionToken,
319319
ReadonlyKeyword,
320320
ReadonlyPragmaMap,
321-
ReadonlyTextRange,
322321
ResolutionMode,
323322
RestTypeNode,
324323
ReturnStatement,
@@ -1950,7 +1949,7 @@ namespace Parser {
19501949
function currentNode(position: number) {
19511950
const node = baseSyntaxCursor.currentNode(position);
19521951
if (topLevel && node && containsPossibleTopLevelAwait(node)) {
1953-
node.intersectsChange = true;
1952+
markAsIntersectingIncrementalChange(node);
19541953
}
19551954
return node;
19561955
}
@@ -3120,7 +3119,7 @@ namespace Parser {
31203119
// Can't reuse a node that intersected the change range.
31213120
// Can't reuse a node that contains a parse error. This is necessary so that we
31223121
// produce the same set of errors again.
3123-
if (nodeIsMissing(node) || node.intersectsChange || containsParseError(node)) {
3122+
if (nodeIsMissing(node) || intersectsIncrementalChange(node) || containsParseError(node)) {
31243123
return undefined;
31253124
}
31263125

@@ -9844,6 +9843,25 @@ namespace Parser {
98449843
}
98459844
}
98469845

9846+
const incrementallyParsedFiles = new WeakSet<SourceFile>();
9847+
9848+
function markAsIncrementallyParsed(sourceFile: SourceFile) {
9849+
if (incrementallyParsedFiles.has(sourceFile)) {
9850+
Debug.fail("Source file has already been incrementally parsed");
9851+
}
9852+
incrementallyParsedFiles.add(sourceFile);
9853+
}
9854+
9855+
const intersectingChangeSet = new WeakSet<Node | NodeArray<Node>>();
9856+
9857+
function intersectsIncrementalChange(node: Node | NodeArray<Node>): boolean {
9858+
return intersectingChangeSet.has(node);
9859+
}
9860+
9861+
function markAsIntersectingIncrementalChange(node: Node | NodeArray<Node>) {
9862+
intersectingChangeSet.add(node);
9863+
}
9864+
98479865
namespace IncrementalParser {
98489866
export function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks: boolean): SourceFile {
98499867
aggressiveChecks = aggressiveChecks || Debug.shouldAssert(AssertionLevel.Aggressive);
@@ -9866,10 +9884,8 @@ namespace IncrementalParser {
98669884
// This is because we do incremental parsing in-place. i.e. we take nodes from the old
98679885
// tree and give them new positions and parents. From that point on, trusting the old
98689886
// tree at all is not possible as far too much of it may violate invariants.
9869-
const incrementalSourceFile = sourceFile as Node as IncrementalNode;
9870-
Debug.assert(!incrementalSourceFile.hasBeenIncrementallyParsed);
9871-
incrementalSourceFile.hasBeenIncrementallyParsed = true;
9872-
Parser.fixupParentReferences(incrementalSourceFile);
9887+
markAsIncrementallyParsed(sourceFile);
9888+
Parser.fixupParentReferences(sourceFile);
98739889
const oldText = sourceFile.text;
98749890
const syntaxCursor = createSyntaxCursor(sourceFile);
98759891

@@ -9908,7 +9924,7 @@ namespace IncrementalParser {
99089924
//
99099925
// Also, mark any syntax elements that intersect the changed span. We know, up front,
99109926
// that we cannot reuse these elements.
9911-
updateTokenPositionsAndMarkElements(incrementalSourceFile, changeRange.span.start, textSpanEnd(changeRange.span), textSpanEnd(textChangeRangeNewSpan(changeRange)), delta, oldText, newText, aggressiveChecks);
9927+
updateTokenPositionsAndMarkElements(sourceFile, changeRange.span.start, textSpanEnd(changeRange.span), textSpanEnd(textChangeRangeNewSpan(changeRange)), delta, oldText, newText, aggressiveChecks);
99129928

99139929
// Now that we've set up our internal incremental state just proceed and parse the
99149930
// source file in the normal fashion. When possible the parser will retrieve and
@@ -9984,18 +10000,18 @@ namespace IncrementalParser {
998410000
}
998510001
}
998610002

9987-
function moveElementEntirelyPastChangeRange(element: IncrementalNode, isArray: false, delta: number, oldText: string, newText: string, aggressiveChecks: boolean): void;
9988-
function moveElementEntirelyPastChangeRange(element: IncrementalNodeArray, isArray: true, delta: number, oldText: string, newText: string, aggressiveChecks: boolean): void;
9989-
function moveElementEntirelyPastChangeRange(element: IncrementalNode | IncrementalNodeArray, isArray: boolean, delta: number, oldText: string, newText: string, aggressiveChecks: boolean) {
10003+
function moveElementEntirelyPastChangeRange(element: Node, isArray: false, delta: number, oldText: string, newText: string, aggressiveChecks: boolean): void;
10004+
function moveElementEntirelyPastChangeRange(element: NodeArray<Node>, isArray: true, delta: number, oldText: string, newText: string, aggressiveChecks: boolean): void;
10005+
function moveElementEntirelyPastChangeRange(element: Node | NodeArray<Node>, isArray: boolean, delta: number, oldText: string, newText: string, aggressiveChecks: boolean) {
999010006
if (isArray) {
9991-
visitArray(element as IncrementalNodeArray);
10007+
visitArray(element as NodeArray<Node>);
999210008
}
999310009
else {
9994-
visitNode(element as IncrementalNode);
10010+
visitNode(element as Node);
999510011
}
999610012
return;
999710013

9998-
function visitNode(node: IncrementalNode) {
10014+
function visitNode(node: Node) {
999910015
let text = "";
1000010016
if (aggressiveChecks && shouldCheckNode(node)) {
1000110017
text = oldText.substring(node.pos, node.end);
@@ -10014,13 +10030,13 @@ namespace IncrementalParser {
1001410030
forEachChild(node, visitNode as (node: Node) => void, visitArray as (nodes: NodeArray<Node>) => void);
1001510031
if (hasJSDocNodes(node)) {
1001610032
for (const jsDocComment of node.jsDoc!) {
10017-
visitNode(jsDocComment as Node as IncrementalNode);
10033+
visitNode(jsDocComment);
1001810034
}
1001910035
}
1002010036
checkNodePositions(node, aggressiveChecks);
1002110037
}
1002210038

10023-
function visitArray(array: IncrementalNodeArray) {
10039+
function visitArray(array: NodeArray<Node>) {
1002410040
setTextRangePosEnd(array, array.pos + delta, array.end + delta);
1002510041

1002610042
for (const node of array) {
@@ -10040,7 +10056,7 @@ namespace IncrementalParser {
1004010056
return false;
1004110057
}
1004210058

10043-
function adjustIntersectingElement(element: IncrementalElement, changeStart: number, changeRangeOldEnd: number, changeRangeNewEnd: number, delta: number) {
10059+
function adjustIntersectingElement(element: Node | NodeArray<Node>, changeStart: number, changeRangeOldEnd: number, changeRangeNewEnd: number, delta: number) {
1004410060
Debug.assert(element.end >= changeStart, "Adjusting an element that was entirely before the change range");
1004510061
Debug.assert(element.pos <= changeRangeOldEnd, "Adjusting an element that was entirely after the change range");
1004610062
Debug.assert(element.pos <= element.end);
@@ -10106,9 +10122,10 @@ namespace IncrementalParser {
1010610122
Math.min(element.end, changeRangeNewEnd);
1010710123

1010810124
Debug.assert(pos <= end);
10109-
if (element.parent) {
10110-
Debug.assertGreaterThanOrEqual(pos, element.parent.pos);
10111-
Debug.assertLessThanOrEqual(end, element.parent.end);
10125+
if ((element as any).parent) {
10126+
const parent = (element as any).parent as Node;
10127+
Debug.assertGreaterThanOrEqual(pos, parent.pos);
10128+
Debug.assertLessThanOrEqual(end, parent.end);
1011210129
}
1011310130

1011410131
setTextRangePosEnd(element, pos, end);
@@ -10132,7 +10149,7 @@ namespace IncrementalParser {
1013210149
}
1013310150

1013410151
function updateTokenPositionsAndMarkElements(
10135-
sourceFile: IncrementalNode,
10152+
sourceFile: SourceFile,
1013610153
changeStart: number,
1013710154
changeRangeOldEnd: number,
1013810155
changeRangeNewEnd: number,
@@ -10144,7 +10161,7 @@ namespace IncrementalParser {
1014410161
visitNode(sourceFile);
1014510162
return;
1014610163

10147-
function visitNode(child: IncrementalNode) {
10164+
function visitNode(child: Node) {
1014810165
Debug.assert(child.pos <= child.end);
1014910166
if (child.pos > changeRangeOldEnd) {
1015010167
// Node is entirely past the change range. We need to move both its pos and
@@ -10158,15 +10175,15 @@ namespace IncrementalParser {
1015810175
// be able to use.
1015910176
const fullEnd = child.end;
1016010177
if (fullEnd >= changeStart) {
10161-
child.intersectsChange = true;
10178+
markAsIntersectingIncrementalChange(child);
1016210179
unsetNodeChildren(child);
1016310180

1016410181
// Adjust the pos or end (or both) of the intersecting element accordingly.
1016510182
adjustIntersectingElement(child, changeStart, changeRangeOldEnd, changeRangeNewEnd, delta);
1016610183
forEachChild(child, visitNode as (node: Node) => void, visitArray as (nodes: NodeArray<Node>) => void);
1016710184
if (hasJSDocNodes(child)) {
1016810185
for (const jsDocComment of child.jsDoc!) {
10169-
visitNode(jsDocComment as Node as IncrementalNode);
10186+
visitNode(jsDocComment);
1017010187
}
1017110188
}
1017210189
checkNodePositions(child, aggressiveChecks);
@@ -10177,7 +10194,7 @@ namespace IncrementalParser {
1017710194
Debug.assert(fullEnd < changeStart);
1017810195
}
1017910196

10180-
function visitArray(array: IncrementalNodeArray) {
10197+
function visitArray(array: NodeArray<Node>) {
1018110198
Debug.assert(array.pos <= array.end);
1018210199
if (array.pos > changeRangeOldEnd) {
1018310200
// Array is entirely after the change range. We need to move it, and move any of
@@ -10191,7 +10208,7 @@ namespace IncrementalParser {
1019110208
// be able to use.
1019210209
const fullEnd = array.end;
1019310210
if (fullEnd >= changeStart) {
10194-
array.intersectsChange = true;
10211+
markAsIntersectingIncrementalChange(array);
1019510212

1019610213
// Adjust the pos or end (or both) of the intersecting array accordingly.
1019710214
adjustIntersectingElement(array, changeStart, changeRangeOldEnd, changeRangeNewEnd, delta);
@@ -10340,25 +10357,11 @@ namespace IncrementalParser {
1034010357
}
1034110358
}
1034210359

10343-
interface IncrementalElement extends ReadonlyTextRange {
10344-
readonly parent: Node;
10345-
intersectsChange: boolean;
10346-
length?: number;
10347-
}
10348-
10349-
export interface IncrementalNode extends Node, IncrementalElement {
10350-
hasBeenIncrementallyParsed: boolean;
10351-
}
10352-
10353-
interface IncrementalNodeArray extends NodeArray<IncrementalNode>, IncrementalElement {
10354-
length: number;
10355-
}
10356-
1035710360
// Allows finding nodes in the source file at a certain position in an efficient manner.
1035810361
// The implementation takes advantage of the calling pattern it knows the parser will
1035910362
// make in order to optimize finding nodes as quickly as possible.
1036010363
export interface SyntaxCursor {
10361-
currentNode(position: number): IncrementalNode;
10364+
currentNode(position: number): Node;
1036210365
}
1036310366

1036410367
export function createSyntaxCursor(sourceFile: SourceFile): SyntaxCursor {
@@ -10400,7 +10403,7 @@ namespace IncrementalParser {
1040010403

1040110404
// Either we don'd have a node, or we have a node at the position being asked for.
1040210405
Debug.assert(!current || current.pos === position);
10403-
return current as IncrementalNode;
10406+
return current;
1040410407
},
1040510408
};
1040610409

0 commit comments

Comments
 (0)