Skip to content

Commit ddc4ae7

Browse files
rbucktonmhegazy
authored andcommitted
Reuse subtree transform flags for incrementally parsed nodes (#12088)
1 parent be2e8e8 commit ddc4ae7

File tree

4 files changed

+81
-71
lines changed

4 files changed

+81
-71
lines changed

src/compiler/binder.ts

Lines changed: 64 additions & 2 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;
@@ -895,8 +896,8 @@ namespace ts {
895896
const enclosingLabeledStatement = node.parent.kind === SyntaxKind.LabeledStatement
896897
? lastOrUndefined(activeLabels)
897898
: undefined;
898-
// if do statement is wrapped in labeled statement then target labels for break/continue with or without
899-
// label should be the same
899+
// if do statement is wrapped in labeled statement then target labels for break/continue with or without
900+
// label should be the same
900901
const preConditionLabel = enclosingLabeledStatement ? enclosingLabeledStatement.continueTarget : createBranchLabel();
901902
const postDoLabel = enclosingLabeledStatement ? enclosingLabeledStatement.breakTarget : createBranchLabel();
902903
addAntecedent(preDoLabel, currentFlow);
@@ -3206,4 +3207,65 @@ namespace ts {
32063207
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
32073208
return transformFlags & ~excludeFlags;
32083209
}
3210+
3211+
/**
3212+
* Gets the transform flags to exclude when unioning the transform flags of a subtree.
3213+
*
3214+
* NOTE: This needs to be kept up-to-date with the exclusions used in `computeTransformFlagsForNode`.
3215+
* For performance reasons, `computeTransformFlagsForNode` uses local constant values rather
3216+
* than calling this function.
3217+
*/
3218+
/* @internal */
3219+
export function getTransformFlagsSubtreeExclusions(kind: SyntaxKind) {
3220+
if (kind >= SyntaxKind.FirstTypeNode && kind <= SyntaxKind.LastTypeNode) {
3221+
return TransformFlags.TypeExcludes;
3222+
}
3223+
3224+
switch (kind) {
3225+
case SyntaxKind.CallExpression:
3226+
case SyntaxKind.NewExpression:
3227+
case SyntaxKind.ArrayLiteralExpression:
3228+
return TransformFlags.ArrayLiteralOrCallOrNewExcludes;
3229+
case SyntaxKind.ModuleDeclaration:
3230+
return TransformFlags.ModuleExcludes;
3231+
case SyntaxKind.Parameter:
3232+
return TransformFlags.ParameterExcludes;
3233+
case SyntaxKind.ArrowFunction:
3234+
return TransformFlags.ArrowFunctionExcludes;
3235+
case SyntaxKind.FunctionExpression:
3236+
case SyntaxKind.FunctionDeclaration:
3237+
return TransformFlags.FunctionExcludes;
3238+
case SyntaxKind.VariableDeclarationList:
3239+
return TransformFlags.VariableDeclarationListExcludes;
3240+
case SyntaxKind.ClassDeclaration:
3241+
case SyntaxKind.ClassExpression:
3242+
return TransformFlags.ClassExcludes;
3243+
case SyntaxKind.Constructor:
3244+
return TransformFlags.ConstructorExcludes;
3245+
case SyntaxKind.MethodDeclaration:
3246+
case SyntaxKind.GetAccessor:
3247+
case SyntaxKind.SetAccessor:
3248+
return TransformFlags.MethodOrAccessorExcludes;
3249+
case SyntaxKind.AnyKeyword:
3250+
case SyntaxKind.NumberKeyword:
3251+
case SyntaxKind.NeverKeyword:
3252+
case SyntaxKind.StringKeyword:
3253+
case SyntaxKind.BooleanKeyword:
3254+
case SyntaxKind.SymbolKeyword:
3255+
case SyntaxKind.VoidKeyword:
3256+
case SyntaxKind.TypeParameter:
3257+
case SyntaxKind.PropertySignature:
3258+
case SyntaxKind.MethodSignature:
3259+
case SyntaxKind.CallSignature:
3260+
case SyntaxKind.ConstructSignature:
3261+
case SyntaxKind.IndexSignature:
3262+
case SyntaxKind.InterfaceDeclaration:
3263+
case SyntaxKind.TypeAliasDeclaration:
3264+
return TransformFlags.TypeExcludes;
3265+
case SyntaxKind.ObjectLiteralExpression:
3266+
return TransformFlags.ObjectLiteralExcludes;
3267+
default:
3268+
return TransformFlags.NodeExcludes;
3269+
}
3270+
}
32093271
}

src/compiler/visitor.ts

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

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