Skip to content

Commit e28a76c

Browse files
TypeScript BotDanielRosenwasser
TypeScript Bot
andauthored
🤖 Pick PR #59154 (Create a SourceFile-level indirec...) into release-5.5 (#59211)
Co-authored-by: Daniel Rosenwasser <[email protected]>
1 parent 6c7df1f commit e28a76c

File tree

6 files changed

+67
-23
lines changed

6 files changed

+67
-23
lines changed
Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,61 @@
11
import {
2+
Debug,
23
emptyArray,
34
isNodeKind,
45
Node,
6+
SourceFileLike,
7+
SyntaxKind,
8+
SyntaxList,
59
} from "../_namespaces/ts.js";
610

7-
const nodeChildren = new WeakMap<Node, readonly Node[] | undefined>();
11+
const sourceFileToNodeChildren = new WeakMap<SourceFileLike, WeakMap<Node, readonly Node[] | undefined>>();
812

913
/** @internal */
10-
export function getNodeChildren(node: Node): readonly Node[] | undefined {
11-
if (!isNodeKind(node.kind)) return emptyArray;
14+
export function getNodeChildren(node: Node, sourceFile: SourceFileLike): readonly Node[] | undefined {
15+
const kind = node.kind;
16+
if (!isNodeKind(kind)) {
17+
return emptyArray;
18+
}
19+
if (kind === SyntaxKind.SyntaxList) {
20+
return (node as SyntaxList)._children;
21+
}
1222

13-
return nodeChildren.get(node);
23+
return sourceFileToNodeChildren.get(sourceFile)?.get(node);
1424
}
1525

1626
/** @internal */
17-
export function setNodeChildren(node: Node, children: readonly Node[]): readonly Node[] {
18-
nodeChildren.set(node, children);
27+
export function setNodeChildren(node: Node, sourceFile: SourceFileLike, children: readonly Node[]): readonly Node[] {
28+
if (node.kind === SyntaxKind.SyntaxList) {
29+
// SyntaxList children are always eagerly created in the process of
30+
// creating their parent's `children` list. We shouldn't need to set them here.
31+
Debug.fail("Should not need to re-set the children of a SyntaxList.");
32+
}
33+
34+
let map = sourceFileToNodeChildren.get(sourceFile);
35+
if (map === undefined) {
36+
map = new WeakMap();
37+
sourceFileToNodeChildren.set(sourceFile, map);
38+
}
39+
map.set(node, children);
1940
return children;
2041
}
2142

2243
/** @internal */
23-
export function unsetNodeChildren(node: Node) {
24-
nodeChildren.delete(node);
44+
export function unsetNodeChildren(node: Node, origSourceFile: SourceFileLike) {
45+
if (node.kind === SyntaxKind.SyntaxList) {
46+
// Syntax lists are synthesized and we store their children directly on them.
47+
// They are a special case where we expect incremental parsing to toss them away entirely
48+
// if a change intersects with their containing parents.
49+
Debug.fail("Did not expect to unset the children of a SyntaxList.");
50+
}
51+
sourceFileToNodeChildren.get(origSourceFile)?.delete(node);
52+
}
53+
54+
/** @internal */
55+
export function transferSourceFileChildren(sourceFile: SourceFileLike, targetSourceFile: SourceFileLike) {
56+
const map = sourceFileToNodeChildren.get(sourceFile);
57+
if (map !== undefined) {
58+
sourceFileToNodeChildren.delete(sourceFile);
59+
sourceFileToNodeChildren.set(targetSourceFile, map);
60+
}
2561
}

‎src/compiler/factory/nodeFactory.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,6 @@ import {
387387
setEmitFlags,
388388
setIdentifierAutoGenerate,
389389
setIdentifierTypeArguments,
390-
setNodeChildren,
391390
setParent,
392391
setTextRange,
393392
ShorthandPropertyAssignment,
@@ -6211,7 +6210,7 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode
62116210
// @api
62126211
function createSyntaxList(children: readonly Node[]) {
62136212
const node = createBaseNode<SyntaxList>(SyntaxKind.SyntaxList);
6214-
setNodeChildren(node, children);
6213+
node._children = children;
62156214
return node;
62166215
}
62176216

‎src/compiler/parser.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,7 @@ import {
369369
tokenIsIdentifierOrKeywordOrGreaterThan,
370370
tokenToString,
371371
tracing,
372+
transferSourceFileChildren,
372373
TransformFlags,
373374
TryStatement,
374375
TupleTypeNode,
@@ -9949,6 +9950,7 @@ namespace IncrementalParser {
99499950
aggressiveChecks,
99509951
);
99519952
result.impliedNodeFormat = sourceFile.impliedNodeFormat;
9953+
transferSourceFileChildren(sourceFile, result);
99529954
return result;
99539955
}
99549956

@@ -10001,9 +10003,9 @@ namespace IncrementalParser {
1000110003
}
1000210004
}
1000310005

10004-
function moveElementEntirelyPastChangeRange(element: Node, isArray: false, delta: number, oldText: string, newText: string, aggressiveChecks: boolean): void;
10005-
function moveElementEntirelyPastChangeRange(element: NodeArray<Node>, isArray: true, delta: number, oldText: string, newText: string, aggressiveChecks: boolean): void;
10006-
function moveElementEntirelyPastChangeRange(element: Node | NodeArray<Node>, isArray: boolean, delta: number, oldText: string, newText: string, aggressiveChecks: boolean) {
10006+
function moveElementEntirelyPastChangeRange(element: Node, origSourceFile: SourceFile, isArray: false, delta: number, oldText: string, newText: string, aggressiveChecks: boolean): void;
10007+
function moveElementEntirelyPastChangeRange(element: NodeArray<Node>, origSourceFile: SourceFile, isArray: true, delta: number, oldText: string, newText: string, aggressiveChecks: boolean): void;
10008+
function moveElementEntirelyPastChangeRange(element: Node | NodeArray<Node>, origSourceFile: SourceFile, isArray: boolean, delta: number, oldText: string, newText: string, aggressiveChecks: boolean) {
1000710009
if (isArray) {
1000810010
visitArray(element as NodeArray<Node>);
1000910011
}
@@ -10020,7 +10022,7 @@ namespace IncrementalParser {
1002010022

1002110023
// Ditch any existing LS children we may have created. This way we can avoid
1002210024
// moving them forward.
10023-
unsetNodeChildren(node);
10025+
unsetNodeChildren(node, origSourceFile);
1002410026

1002510027
setTextRangePosEnd(node, node.pos + delta, node.end + delta);
1002610028

@@ -10167,7 +10169,7 @@ namespace IncrementalParser {
1016710169
if (child.pos > changeRangeOldEnd) {
1016810170
// Node is entirely past the change range. We need to move both its pos and
1016910171
// end, forward or backward appropriately.
10170-
moveElementEntirelyPastChangeRange(child, /*isArray*/ false, delta, oldText, newText, aggressiveChecks);
10172+
moveElementEntirelyPastChangeRange(child, sourceFile, /*isArray*/ false, delta, oldText, newText, aggressiveChecks);
1017110173
return;
1017210174
}
1017310175

@@ -10177,7 +10179,7 @@ namespace IncrementalParser {
1017710179
const fullEnd = child.end;
1017810180
if (fullEnd >= changeStart) {
1017910181
markAsIntersectingIncrementalChange(child);
10180-
unsetNodeChildren(child);
10182+
unsetNodeChildren(child, sourceFile);
1018110183

1018210184
// Adjust the pos or end (or both) of the intersecting element accordingly.
1018310185
adjustIntersectingElement(child, changeStart, changeRangeOldEnd, changeRangeNewEnd, delta);
@@ -10200,7 +10202,7 @@ namespace IncrementalParser {
1020010202
if (array.pos > changeRangeOldEnd) {
1020110203
// Array is entirely after the change range. We need to move it, and move any of
1020210204
// its children.
10203-
moveElementEntirelyPastChangeRange(array, /*isArray*/ true, delta, oldText, newText, aggressiveChecks);
10205+
moveElementEntirelyPastChangeRange(array, sourceFile, /*isArray*/ true, delta, oldText, newText, aggressiveChecks);
1020410206
return;
1020510207
}
1020610208

‎src/compiler/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9814,6 +9814,12 @@ export interface DiagnosticCollection {
98149814
// SyntaxKind.SyntaxList
98159815
export interface SyntaxList extends Node {
98169816
kind: SyntaxKind.SyntaxList;
9817+
9818+
// Unlike other nodes which may or may not have their child nodes calculated,
9819+
// the entire purpose of a SyntaxList is to hold child nodes.
9820+
// Instead of using the WeakMap machinery in `nodeChildren.ts`,
9821+
// we just store the children directly on the SyntaxList.
9822+
/** @internal */ _children: readonly Node[];
98179823
}
98189824

98199825
// dprint-ignore

‎src/compiler/utilities.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1178,7 +1178,7 @@ export function getTokenPosOfNode(node: Node, sourceFile?: SourceFileLike, inclu
11781178

11791179
if (isJSDocNode(node) || node.kind === SyntaxKind.JsxText) {
11801180
// JsxText cannot actually contain comments, even though the scanner will think it sees comments
1181-
return skipTrivia((sourceFile || getSourceFileOfNode(node)).text, node.pos, /*stopAfterLineBreak*/ false, /*stopAtComments*/ true);
1181+
return skipTrivia((sourceFile ?? getSourceFileOfNode(node)).text, node.pos, /*stopAfterLineBreak*/ false, /*stopAtComments*/ true);
11821182
}
11831183

11841184
if (includeJsDoc && hasJSDocNodes(node)) {
@@ -1190,14 +1190,15 @@ export function getTokenPosOfNode(node: Node, sourceFile?: SourceFileLike, inclu
11901190
// trivia for the list, we may have skipped the JSDocComment as well. So we should process its
11911191
// first child to determine the actual position of its first token.
11921192
if (node.kind === SyntaxKind.SyntaxList) {
1193-
const first = firstOrUndefined(getNodeChildren(node));
1193+
sourceFile ??= getSourceFileOfNode(node);
1194+
const first = firstOrUndefined(getNodeChildren(node, sourceFile));
11941195
if (first) {
11951196
return getTokenPosOfNode(first, sourceFile, includeJsDoc);
11961197
}
11971198
}
11981199

11991200
return skipTrivia(
1200-
(sourceFile || getSourceFileOfNode(node)).text,
1201+
(sourceFile ?? getSourceFileOfNode(node)).text,
12011202
node.pos,
12021203
/*stopAfterLineBreak*/ false,
12031204
/*stopAtComments*/ false,

‎src/services/services.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -442,9 +442,9 @@ class NodeObject<TKind extends SyntaxKind> implements Node {
442442
return this.getChildren(sourceFile)[index];
443443
}
444444

445-
public getChildren(sourceFile?: SourceFileLike): readonly Node[] {
445+
public getChildren(sourceFile: SourceFileLike = getSourceFileOfNode(this)): readonly Node[] {
446446
this.assertHasRealPosition("Node without a real position cannot be scanned and thus has no token nodes - use forEachChild and collect the result if that's fine");
447-
return getNodeChildren(this) ?? setNodeChildren(this, createChildren(this, sourceFile));
447+
return getNodeChildren(this, sourceFile) ?? setNodeChildren(this, sourceFile, createChildren(this, sourceFile));
448448
}
449449

450450
public getFirstToken(sourceFile?: SourceFileLike): Node | undefined {
@@ -543,7 +543,7 @@ function createSyntaxList(nodes: NodeArray<Node>, parent: Node): Node {
543543
pos = node.end;
544544
}
545545
addSyntheticNodes(children, pos, nodes.end, parent);
546-
setNodeChildren(list, children);
546+
list._children = children;
547547
return list;
548548
}
549549

0 commit comments

Comments
 (0)