Skip to content

Commit 39daa13

Browse files
authored
Monomorphic flow nodes (#57977)
1 parent cf4bb58 commit 39daa13

File tree

4 files changed

+131
-109
lines changed

4 files changed

+131
-109
lines changed

src/compiler/binder.ts

+38-33
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,15 @@ import {
6565
Expression,
6666
ExpressionStatement,
6767
findAncestor,
68+
FlowArrayMutation,
69+
FlowAssignment,
70+
FlowCall,
71+
FlowCondition,
6872
FlowFlags,
6973
FlowLabel,
7074
FlowNode,
7175
FlowReduceLabel,
76+
FlowSwitchClause,
7277
forEach,
7378
forEachChild,
7479
ForInOrOfStatement,
@@ -494,9 +499,9 @@ export const enum ContainerFlags {
494499
IsObjectLiteralOrClassExpressionMethodOrAccessor = 1 << 7,
495500
}
496501

497-
function initFlowNode<T extends FlowNode>(node: T) {
498-
Debug.attachFlowNodeDebugInfo(node);
499-
return node;
502+
/** @internal */
503+
export function createFlowNode(flags: FlowFlags, node: unknown, antecedent: FlowNode | FlowNode[] | undefined): FlowNode {
504+
return Debug.attachFlowNodeDebugInfo({ flags, id: 0, node, antecedent } as FlowNode);
500505
}
501506

502507
const binder = /* @__PURE__ */ createBinder();
@@ -557,8 +562,8 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
557562
var Symbol: new (flags: SymbolFlags, name: __String) => Symbol;
558563
var classifiableNames: Set<__String>;
559564

560-
var unreachableFlow: FlowNode = { flags: FlowFlags.Unreachable };
561-
var reportedUnreachableFlow: FlowNode = { flags: FlowFlags.Unreachable };
565+
var unreachableFlow = createFlowNode(FlowFlags.Unreachable, /*node*/ undefined, /*antecedent*/ undefined);
566+
var reportedUnreachableFlow = createFlowNode(FlowFlags.Unreachable, /*node*/ undefined, /*antecedent*/ undefined);
562567
var bindBinaryExpressionFlow = createBindBinaryExpressionFlow();
563568
/* eslint-enable no-var */
564569

@@ -1013,7 +1018,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
10131018
// A non-async, non-generator IIFE is considered part of the containing control flow. Return statements behave
10141019
// similarly to break statements that exit to a label just past the statement body.
10151020
if (!isImmediatelyInvoked) {
1016-
currentFlow = initFlowNode({ flags: FlowFlags.Start });
1021+
currentFlow = createFlowNode(FlowFlags.Start, /*node*/ undefined, /*antecedent*/ undefined);
10171022
if (containerFlags & (ContainerFlags.IsFunctionExpression | ContainerFlags.IsObjectLiteralOrClassExpressionMethodOrAccessor)) {
10181023
currentFlow.node = node as FunctionExpression | ArrowFunction | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration;
10191024
}
@@ -1332,16 +1337,16 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
13321337
return containsNarrowableReference(expr);
13331338
}
13341339

1335-
function createBranchLabel(): FlowLabel {
1336-
return initFlowNode({ flags: FlowFlags.BranchLabel, antecedents: undefined });
1340+
function createBranchLabel() {
1341+
return createFlowNode(FlowFlags.BranchLabel, /*node*/ undefined, /*antecedent*/ undefined) as FlowLabel;
13371342
}
13381343

1339-
function createLoopLabel(): FlowLabel {
1340-
return initFlowNode({ flags: FlowFlags.LoopLabel, antecedents: undefined });
1344+
function createLoopLabel() {
1345+
return createFlowNode(FlowFlags.LoopLabel, /*node*/ undefined, /*antecedent*/ undefined) as FlowLabel;
13411346
}
13421347

1343-
function createReduceLabel(target: FlowLabel, antecedents: FlowNode[], antecedent: FlowNode): FlowReduceLabel {
1344-
return initFlowNode({ flags: FlowFlags.ReduceLabel, target, antecedents, antecedent });
1348+
function createReduceLabel(target: FlowLabel, antecedents: FlowNode[], antecedent: FlowNode) {
1349+
return createFlowNode(FlowFlags.ReduceLabel, { target, antecedents }, antecedent) as FlowReduceLabel;
13451350
}
13461351

13471352
function setFlowNodeReferenced(flow: FlowNode) {
@@ -1350,13 +1355,13 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
13501355
}
13511356

13521357
function addAntecedent(label: FlowLabel, antecedent: FlowNode): void {
1353-
if (!(antecedent.flags & FlowFlags.Unreachable) && !contains(label.antecedents, antecedent)) {
1354-
(label.antecedents || (label.antecedents = [])).push(antecedent);
1358+
if (!(antecedent.flags & FlowFlags.Unreachable) && !contains(label.antecedent, antecedent)) {
1359+
(label.antecedent || (label.antecedent = [])).push(antecedent);
13551360
setFlowNodeReferenced(antecedent);
13561361
}
13571362
}
13581363

1359-
function createFlowCondition(flags: FlowFlags, antecedent: FlowNode, expression: Expression | undefined): FlowNode {
1364+
function createFlowCondition(flags: FlowFlags.TrueCondition | FlowFlags.FalseCondition, antecedent: FlowNode, expression: Expression | undefined) {
13601365
if (antecedent.flags & FlowFlags.Unreachable) {
13611366
return antecedent;
13621367
}
@@ -1374,32 +1379,32 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
13741379
return antecedent;
13751380
}
13761381
setFlowNodeReferenced(antecedent);
1377-
return initFlowNode({ flags, antecedent, node: expression });
1382+
return createFlowNode(flags, expression, antecedent) as FlowCondition;
13781383
}
13791384

1380-
function createFlowSwitchClause(antecedent: FlowNode, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number): FlowNode {
1385+
function createFlowSwitchClause(antecedent: FlowNode, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number) {
13811386
setFlowNodeReferenced(antecedent);
1382-
return initFlowNode({ flags: FlowFlags.SwitchClause, antecedent, switchStatement, clauseStart, clauseEnd });
1387+
return createFlowNode(FlowFlags.SwitchClause, { switchStatement, clauseStart, clauseEnd }, antecedent) as FlowSwitchClause;
13831388
}
13841389

1385-
function createFlowMutation(flags: FlowFlags, antecedent: FlowNode, node: Expression | VariableDeclaration | ArrayBindingElement): FlowNode {
1390+
function createFlowMutation(flags: FlowFlags.Assignment | FlowFlags.ArrayMutation, antecedent: FlowNode, node: Expression | VariableDeclaration | ArrayBindingElement) {
13861391
setFlowNodeReferenced(antecedent);
13871392
hasFlowEffects = true;
1388-
const result = initFlowNode({ flags, antecedent, node });
1393+
const result = createFlowNode(flags, node, antecedent) as FlowAssignment | FlowArrayMutation;
13891394
if (currentExceptionTarget) {
13901395
addAntecedent(currentExceptionTarget, result);
13911396
}
13921397
return result;
13931398
}
13941399

1395-
function createFlowCall(antecedent: FlowNode, node: CallExpression): FlowNode {
1400+
function createFlowCall(antecedent: FlowNode, node: CallExpression) {
13961401
setFlowNodeReferenced(antecedent);
13971402
hasFlowEffects = true;
1398-
return initFlowNode({ flags: FlowFlags.Call, antecedent, node });
1403+
return createFlowNode(FlowFlags.Call, node, antecedent) as FlowCall;
13991404
}
14001405

14011406
function finishFlowLabel(flow: FlowLabel): FlowNode {
1402-
const antecedents = flow.antecedents;
1407+
const antecedents = flow.antecedent;
14031408
if (!antecedents) {
14041409
return unreachableFlow;
14051410
}
@@ -1658,7 +1663,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
16581663
// set of antecedents for the pre-finally label. As control flow analysis passes by a ReduceLabel
16591664
// node, the pre-finally label is temporarily switched to the reduced antecedent set.
16601665
const finallyLabel = createBranchLabel();
1661-
finallyLabel.antecedents = concatenate(concatenate(normalExitLabel.antecedents, exceptionLabel.antecedents), returnLabel.antecedents);
1666+
finallyLabel.antecedent = concatenate(concatenate(normalExitLabel.antecedent, exceptionLabel.antecedent), returnLabel.antecedent);
16621667
currentFlow = finallyLabel;
16631668
bind(node.finallyBlock);
16641669
if (currentFlow.flags & FlowFlags.Unreachable) {
@@ -1668,18 +1673,18 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
16681673
else {
16691674
// If we have an IIFE return target and return statements in the try or catch blocks, add a control
16701675
// flow that goes back through the finally block and back through only the return statements.
1671-
if (currentReturnTarget && returnLabel.antecedents) {
1672-
addAntecedent(currentReturnTarget, createReduceLabel(finallyLabel, returnLabel.antecedents, currentFlow));
1676+
if (currentReturnTarget && returnLabel.antecedent) {
1677+
addAntecedent(currentReturnTarget, createReduceLabel(finallyLabel, returnLabel.antecedent, currentFlow));
16731678
}
16741679
// If we have an outer exception target (i.e. a containing try-finally or try-catch-finally), add a
16751680
// control flow that goes back through the finally blok and back through each possible exception source.
1676-
if (currentExceptionTarget && exceptionLabel.antecedents) {
1677-
addAntecedent(currentExceptionTarget, createReduceLabel(finallyLabel, exceptionLabel.antecedents, currentFlow));
1681+
if (currentExceptionTarget && exceptionLabel.antecedent) {
1682+
addAntecedent(currentExceptionTarget, createReduceLabel(finallyLabel, exceptionLabel.antecedent, currentFlow));
16781683
}
16791684
// If the end of the finally block is reachable, but the end of the try and catch blocks are not,
16801685
// convert the current flow to unreachable. For example, 'try { return 1; } finally { ... }' should
16811686
// result in an unreachable current control flow.
1682-
currentFlow = normalExitLabel.antecedents ? createReduceLabel(finallyLabel, normalExitLabel.antecedents, currentFlow) : unreachableFlow;
1687+
currentFlow = normalExitLabel.antecedent ? createReduceLabel(finallyLabel, normalExitLabel.antecedent, currentFlow) : unreachableFlow;
16831688
}
16841689
}
16851690
else {
@@ -1700,7 +1705,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
17001705
// We mark a switch statement as possibly exhaustive if it has no default clause and if all
17011706
// case clauses have unreachable end points (e.g. they all return). Note, we no longer need
17021707
// this property in control flow analysis, it's there only for backwards compatibility.
1703-
node.possiblyExhaustive = !hasDefault && !postSwitchLabel.antecedents;
1708+
node.possiblyExhaustive = !hasDefault && !postSwitchLabel.antecedent;
17041709
if (!hasDefault) {
17051710
addAntecedent(postSwitchLabel, createFlowSwitchClause(preSwitchCaseFlow, node, 0, 0));
17061711
}
@@ -1712,7 +1717,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
17121717
function bindCaseBlock(node: CaseBlock): void {
17131718
const clauses = node.clauses;
17141719
const isNarrowingSwitch = node.parent.expression.kind === SyntaxKind.TrueKeyword || isNarrowingExpression(node.parent.expression);
1715-
let fallthroughFlow = unreachableFlow;
1720+
let fallthroughFlow: FlowNode = unreachableFlow;
17161721

17171722
for (let i = 0; i < clauses.length; i++) {
17181723
const clauseStart = i;
@@ -2432,7 +2437,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
24322437
const host = typeAlias.parent.parent;
24332438
container = (getEnclosingContainer(host) as IsContainer | undefined) || file;
24342439
blockScopeContainer = (getEnclosingBlockScopeContainer(host) as IsBlockScopedContainer | undefined) || file;
2435-
currentFlow = initFlowNode({ flags: FlowFlags.Start });
2440+
currentFlow = createFlowNode(FlowFlags.Start, /*node*/ undefined, /*antecedent*/ undefined);
24362441
parent = typeAlias;
24372442
bind(typeAlias.typeExpression);
24382443
const declName = getNameOfDeclaration(typeAlias);
@@ -2504,7 +2509,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
25042509
const enclosingBlockScopeContainer = host ? getEnclosingBlockScopeContainer(host) as IsBlockScopedContainer | undefined : undefined;
25052510
container = enclosingContainer || file;
25062511
blockScopeContainer = enclosingBlockScopeContainer || file;
2507-
currentFlow = initFlowNode({ flags: FlowFlags.Start });
2512+
currentFlow = createFlowNode(FlowFlags.Start, /*node*/ undefined, /*antecedent*/ undefined);
25082513
parent = jsDocImportTag;
25092514
bind(jsDocImportTag.importClause);
25102515
}

0 commit comments

Comments
 (0)