Skip to content

Commit da4985e

Browse files
committed
Reuse subtree transform flags for incrementally parsed nodes (#12088)
1 parent 8e8ec9f commit da4985e

File tree

4 files changed

+79
-69
lines changed

4 files changed

+79
-69
lines changed

src/compiler/binder.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,7 @@ namespace ts {
560560
skipTransformFlagAggregation = true;
561561
bindChildrenWorker(node);
562562
skipTransformFlagAggregation = false;
563+
subtreeTransformFlags |= node.transformFlags & ~getTransformFlagsSubtreeExclusions(node.kind);
563564
}
564565
else {
565566
const savedSubtreeTransformFlags = subtreeTransformFlags;
@@ -3209,4 +3210,65 @@ namespace ts {
32093210
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
32103211
return transformFlags & ~excludeFlags;
32113212
}
3213+
3214+
/**
3215+
* Gets the transform flags to exclude when unioning the transform flags of a subtree.
3216+
*
3217+
* NOTE: This needs to be kept up-to-date with the exclusions used in `computeTransformFlagsForNode`.
3218+
* For performance reasons, `computeTransformFlagsForNode` uses local constant values rather
3219+
* than calling this function.
3220+
*/
3221+
/* @internal */
3222+
export function getTransformFlagsSubtreeExclusions(kind: SyntaxKind) {
3223+
if (kind >= SyntaxKind.FirstTypeNode && kind <= SyntaxKind.LastTypeNode) {
3224+
return TransformFlags.TypeExcludes;
3225+
}
3226+
3227+
switch (kind) {
3228+
case SyntaxKind.CallExpression:
3229+
case SyntaxKind.NewExpression:
3230+
case SyntaxKind.ArrayLiteralExpression:
3231+
return TransformFlags.ArrayLiteralOrCallOrNewExcludes;
3232+
case SyntaxKind.ModuleDeclaration:
3233+
return TransformFlags.ModuleExcludes;
3234+
case SyntaxKind.Parameter:
3235+
return TransformFlags.ParameterExcludes;
3236+
case SyntaxKind.ArrowFunction:
3237+
return TransformFlags.ArrowFunctionExcludes;
3238+
case SyntaxKind.FunctionExpression:
3239+
case SyntaxKind.FunctionDeclaration:
3240+
return TransformFlags.FunctionExcludes;
3241+
case SyntaxKind.VariableDeclarationList:
3242+
return TransformFlags.VariableDeclarationListExcludes;
3243+
case SyntaxKind.ClassDeclaration:
3244+
case SyntaxKind.ClassExpression:
3245+
return TransformFlags.ClassExcludes;
3246+
case SyntaxKind.Constructor:
3247+
return TransformFlags.ConstructorExcludes;
3248+
case SyntaxKind.MethodDeclaration:
3249+
case SyntaxKind.GetAccessor:
3250+
case SyntaxKind.SetAccessor:
3251+
return TransformFlags.MethodOrAccessorExcludes;
3252+
case SyntaxKind.AnyKeyword:
3253+
case SyntaxKind.NumberKeyword:
3254+
case SyntaxKind.NeverKeyword:
3255+
case SyntaxKind.StringKeyword:
3256+
case SyntaxKind.BooleanKeyword:
3257+
case SyntaxKind.SymbolKeyword:
3258+
case SyntaxKind.VoidKeyword:
3259+
case SyntaxKind.TypeParameter:
3260+
case SyntaxKind.PropertySignature:
3261+
case SyntaxKind.MethodSignature:
3262+
case SyntaxKind.CallSignature:
3263+
case SyntaxKind.ConstructSignature:
3264+
case SyntaxKind.IndexSignature:
3265+
case SyntaxKind.InterfaceDeclaration:
3266+
case SyntaxKind.TypeAliasDeclaration:
3267+
return TransformFlags.TypeExcludes;
3268+
case SyntaxKind.ObjectLiteralExpression:
3269+
return TransformFlags.ObjectLiteralExcludes;
3270+
default:
3271+
return TransformFlags.NodeExcludes;
3272+
}
3273+
}
32123274
}

src/compiler/visitor.ts

Lines changed: 0 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1268,66 +1268,6 @@ namespace ts {
12681268
return transformFlags | aggregateTransformFlagsForNode(child);
12691269
}
12701270

1271-
/**
1272-
* Gets the transform flags to exclude when unioning the transform flags of a subtree.
1273-
*
1274-
* NOTE: This needs to be kept up-to-date with the exclusions used in `computeTransformFlagsForNode`.
1275-
* For performance reasons, `computeTransformFlagsForNode` uses local constant values rather
1276-
* than calling this function.
1277-
*/
1278-
function getTransformFlagsSubtreeExclusions(kind: SyntaxKind) {
1279-
if (kind >= SyntaxKind.FirstTypeNode && kind <= SyntaxKind.LastTypeNode) {
1280-
return TransformFlags.TypeExcludes;
1281-
}
1282-
1283-
switch (kind) {
1284-
case SyntaxKind.CallExpression:
1285-
case SyntaxKind.NewExpression:
1286-
case SyntaxKind.ArrayLiteralExpression:
1287-
return TransformFlags.ArrayLiteralOrCallOrNewExcludes;
1288-
case SyntaxKind.ModuleDeclaration:
1289-
return TransformFlags.ModuleExcludes;
1290-
case SyntaxKind.Parameter:
1291-
return TransformFlags.ParameterExcludes;
1292-
case SyntaxKind.ArrowFunction:
1293-
return TransformFlags.ArrowFunctionExcludes;
1294-
case SyntaxKind.FunctionExpression:
1295-
case SyntaxKind.FunctionDeclaration:
1296-
return TransformFlags.FunctionExcludes;
1297-
case SyntaxKind.VariableDeclarationList:
1298-
return TransformFlags.VariableDeclarationListExcludes;
1299-
case SyntaxKind.ClassDeclaration:
1300-
case SyntaxKind.ClassExpression:
1301-
return TransformFlags.ClassExcludes;
1302-
case SyntaxKind.Constructor:
1303-
return TransformFlags.ConstructorExcludes;
1304-
case SyntaxKind.MethodDeclaration:
1305-
case SyntaxKind.GetAccessor:
1306-
case SyntaxKind.SetAccessor:
1307-
return TransformFlags.MethodOrAccessorExcludes;
1308-
case SyntaxKind.AnyKeyword:
1309-
case SyntaxKind.NumberKeyword:
1310-
case SyntaxKind.NeverKeyword:
1311-
case SyntaxKind.StringKeyword:
1312-
case SyntaxKind.BooleanKeyword:
1313-
case SyntaxKind.SymbolKeyword:
1314-
case SyntaxKind.VoidKeyword:
1315-
case SyntaxKind.TypeParameter:
1316-
case SyntaxKind.PropertySignature:
1317-
case SyntaxKind.MethodSignature:
1318-
case SyntaxKind.CallSignature:
1319-
case SyntaxKind.ConstructSignature:
1320-
case SyntaxKind.IndexSignature:
1321-
case SyntaxKind.InterfaceDeclaration:
1322-
case SyntaxKind.TypeAliasDeclaration:
1323-
return TransformFlags.TypeExcludes;
1324-
case SyntaxKind.ObjectLiteralExpression:
1325-
return TransformFlags.ObjectLiteralExcludes;
1326-
default:
1327-
return TransformFlags.NodeExcludes;
1328-
}
1329-
}
1330-
13311271
export namespace Debug {
13321272
export const failNotOptional = shouldAssert(AssertionLevel.Normal)
13331273
? (message?: string) => assert(false, message || "Node not optional.")

src/harness/unittests/incrementalParser.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,10 @@ namespace ts {
4444

4545
// NOTE: 'reusedElements' is the expected count of elements reused from the old tree to the new
4646
// tree. It may change as we tweak the parser. If the count increases then that should always
47-
// be a good thing. If it decreases, that's not great (less reusability), but that may be
48-
// unavoidable. If it does decrease an investigation should be done to make sure that things
47+
// be a good thing. If it decreases, that's not great (less reusability), but that may be
48+
// unavoidable. If it does decrease an investigation should be done to make sure that things
4949
// are still ok and we're still appropriately reusing most of the tree.
50-
function compareTrees(oldText: IScriptSnapshot, newText: IScriptSnapshot, textChangeRange: TextChangeRange, expectedReusedElements: number, oldTree?: SourceFile): SourceFile {
50+
function compareTrees(oldText: IScriptSnapshot, newText: IScriptSnapshot, textChangeRange: TextChangeRange, expectedReusedElements: number, oldTree?: SourceFile) {
5151
oldTree = oldTree || createTree(oldText, /*version:*/ ".");
5252
Utils.assertInvariants(oldTree, /*parent:*/ undefined);
5353

@@ -76,7 +76,7 @@ namespace ts {
7676
assert.equal(actualReusedCount, expectedReusedElements, actualReusedCount + " !== " + expectedReusedElements);
7777
}
7878

79-
return incrementalNewTree;
79+
return { oldTree, newTree, incrementalNewTree };
8080
}
8181

8282
function reusedElements(oldNode: SourceFile, newNode: SourceFile): number {
@@ -103,7 +103,7 @@ namespace ts {
103103
for (let i = 0; i < repeat; i++) {
104104
const oldText = ScriptSnapshot.fromString(source);
105105
const newTextAndChange = withDelete(oldText, index, 1);
106-
const newTree = compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, -1, oldTree);
106+
const newTree = compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, -1, oldTree).incrementalNewTree;
107107

108108
source = newTextAndChange.text.getText(0, newTextAndChange.text.getLength());
109109
oldTree = newTree;
@@ -116,7 +116,7 @@ namespace ts {
116116
for (let i = 0; i < repeat; i++) {
117117
const oldText = ScriptSnapshot.fromString(source);
118118
const newTextAndChange = withInsert(oldText, index + i, toInsert.charAt(i));
119-
const newTree = compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, -1, oldTree);
119+
const newTree = compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, -1, oldTree).incrementalNewTree;
120120

121121
source = newTextAndChange.text.getText(0, newTextAndChange.text.getLength());
122122
oldTree = newTree;
@@ -639,7 +639,7 @@ module m3 { }\
639639
});
640640

641641
it("Unterminated comment after keyword converted to identifier", () => {
642-
// 'public' as a keyword should be incrementally unusable (because it has an
642+
// 'public' as a keyword should be incrementally unusable (because it has an
643643
// unterminated comment). When we convert it to an identifier, that shouldn't
644644
// change anything, and we should still get the same errors.
645645
const source = "return; a.public /*";
@@ -796,6 +796,16 @@ module m3 { }\
796796
compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 4);
797797
});
798798

799+
it("Reuse transformFlags of subtree during bind", () => {
800+
const source = `class Greeter { constructor(element: HTMLElement) { } }`;
801+
const oldText = ScriptSnapshot.fromString(source);
802+
const newTextAndChange = withChange(oldText, 15, 0, "\n");
803+
const { oldTree, incrementalNewTree } = compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, -1);
804+
bindSourceFile(oldTree, {});
805+
bindSourceFile(incrementalNewTree, {});
806+
assert.equal(oldTree.transformFlags, incrementalNewTree.transformFlags);
807+
});
808+
799809
// Simulated typing tests.
800810

801811
it("Type extends clause 1", () => {

src/services/services.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,13 @@ namespace ts {
4848
public jsDocComments: JSDoc[];
4949
public original: Node;
5050
public transformFlags: TransformFlags;
51-
public excludeTransformFlags: TransformFlags;
5251
private _children: Node[];
5352

5453
constructor(kind: SyntaxKind, pos: number, end: number) {
5554
this.pos = pos;
5655
this.end = end;
5756
this.flags = NodeFlags.None;
5857
this.transformFlags = undefined;
59-
this.excludeTransformFlags = undefined;
6058
this.parent = undefined;
6159
this.kind = kind;
6260
}

0 commit comments

Comments
 (0)