From 1e478345f1f7327dc47af7703fac760a4bb30818 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Thu, 17 Nov 2022 22:09:52 -0800 Subject: [PATCH] Binder factory experiment --- src/compiler/binder.ts | 108 ++++++++++++++++++++++++++++++++++++----- src/compiler/debug.ts | 4 +- src/compiler/types.ts | 9 ++++ 3 files changed, 107 insertions(+), 14 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 64fbdbce0e572..eca5d9086c321 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -62,10 +62,17 @@ import { Expression, ExpressionStatement, findAncestor, + FlowArrayMutation, + FlowAssignment, + FlowCall, + FlowCondition, FlowFlags, FlowLabel, FlowNode, + FlowNodeBase, FlowReduceLabel, + FlowStart, + FlowSwitchClause, forEach, forEachChild, ForInOrOfStatement, @@ -464,11 +471,88 @@ const enum ContainerFlags { IsObjectLiteralOrClassExpressionMethodOrAccessor = 1 << 7, } -function initFlowNode(node: T) { - Debug.attachFlowNodeDebugInfo(node); - return node; +function createFlowNode( + flags: FlowFlags, + node?: Node, + antecedent?: FlowNode, + antecedents?: FlowNode[], + target?: FlowLabel, + switchStatement?: SwitchStatement, + clauseStart?: number, + clauseEnd?: number, +): T { + // Create all flow nodes in a predictable order. If FlowNode changes, add more + // properties here. + const flowNode: FlowNodeBase = { + flags, + id: undefined, + node, + antecedent, + antecedents, + target, + switchStatement, + clauseStart, + clauseEnd, + }; + Debug.attachFlowNodeDebugInfo(flowNode); + return flowNode as T; } +function createFlowStartNode(flags: FlowFlags): FlowStart { + return createFlowNode(flags); +} + +function createFlowLabelNode( + flags: FlowFlags, + antecedents: FlowNode[] | undefined, +): FlowLabel { + return createFlowNode(flags, /*node*/ undefined, /*antecedent*/ undefined, antecedents); +} + +function createFlowMutationNode( + flags: FlowFlags, + node: FlowAssignment["node"] | FlowArrayMutation["node"], + antecedent: FlowNode, +): FlowAssignment | FlowArrayMutation { + return createFlowNode(flags, node, antecedent); +} + +function createFlowConditionNode( + flags: FlowFlags, + node: FlowCondition["node"], + antecedent: FlowNode, +): FlowCondition { + return createFlowNode(flags, node, antecedent); +} + +function createFlowSwitchClauseNode( + flags: FlowFlags, + switchStatement: SwitchStatement, + clauseStart: number, + clauseEnd: number, + antecedent: FlowNode, +): FlowSwitchClause { + return createFlowNode(flags, /*node*/ undefined, antecedent, /*antecedents*/ undefined, /*target*/ undefined, switchStatement, clauseStart, clauseEnd); +} + +function createFlowCallNode( + flags: FlowFlags, + node: FlowCall["node"], + antecedent: FlowNode, +): FlowCall { + return createFlowNode(flags, node, antecedent); +} + +function createFlowReduceLabelNode( + flags: FlowFlags, + antecedent: FlowNode, + antecedents: FlowNode[], + target: FlowLabel, +): FlowReduceLabel { + return createFlowNode(flags, /*node*/ undefined, antecedent, antecedents, target); +} + + const binder = createBinder(); /** @internal */ @@ -965,7 +1049,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { // A non-async, non-generator IIFE is considered part of the containing control flow. Return statements behave // similarly to break statements that exit to a label just past the statement body. if (!isImmediatelyInvoked) { - currentFlow = initFlowNode({ flags: FlowFlags.Start }); + currentFlow = createFlowStartNode(FlowFlags.Start); if (containerFlags & (ContainerFlags.IsFunctionExpression | ContainerFlags.IsObjectLiteralOrClassExpressionMethodOrAccessor)) { currentFlow.node = node as FunctionExpression | ArrowFunction | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration; } @@ -1261,15 +1345,15 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { } function createBranchLabel(): FlowLabel { - return initFlowNode({ flags: FlowFlags.BranchLabel, antecedents: undefined }); + return createFlowLabelNode(FlowFlags.BranchLabel, /*antecedents*/ undefined); } function createLoopLabel(): FlowLabel { - return initFlowNode({ flags: FlowFlags.LoopLabel, antecedents: undefined }); + return createFlowLabelNode(FlowFlags.LoopLabel, /*antecedents*/ undefined); } function createReduceLabel(target: FlowLabel, antecedents: FlowNode[], antecedent: FlowNode): FlowReduceLabel { - return initFlowNode({ flags: FlowFlags.ReduceLabel, target, antecedents, antecedent }); + return createFlowReduceLabelNode(FlowFlags.ReduceLabel, antecedent, antecedents, target); } function setFlowNodeReferenced(flow: FlowNode) { @@ -1300,17 +1384,17 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { return antecedent; } setFlowNodeReferenced(antecedent); - return initFlowNode({ flags, antecedent, node: expression }); + return createFlowConditionNode(flags, expression, antecedent); } function createFlowSwitchClause(antecedent: FlowNode, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number): FlowNode { setFlowNodeReferenced(antecedent); - return initFlowNode({ flags: FlowFlags.SwitchClause, antecedent, switchStatement, clauseStart, clauseEnd }); + return createFlowSwitchClauseNode(FlowFlags.SwitchClause, switchStatement, clauseStart, clauseEnd, antecedent); } function createFlowMutation(flags: FlowFlags, antecedent: FlowNode, node: Expression | VariableDeclaration | ArrayBindingElement): FlowNode { setFlowNodeReferenced(antecedent); - const result = initFlowNode({ flags, antecedent, node }); + const result = createFlowMutationNode(flags, node, antecedent); if (currentExceptionTarget) { addAntecedent(currentExceptionTarget, result); } @@ -1319,7 +1403,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { function createFlowCall(antecedent: FlowNode, node: CallExpression): FlowNode { setFlowNodeReferenced(antecedent); - return initFlowNode({ flags: FlowFlags.Call, antecedent, node }); + return createFlowCallNode(FlowFlags.Call, node, antecedent); } function finishFlowLabel(flow: FlowLabel): FlowNode { @@ -2415,7 +2499,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { const host = typeAlias.parent.parent; container = findAncestor(host.parent, n => !!(getContainerFlags(n) & ContainerFlags.IsContainer)) || file; blockScopeContainer = getEnclosingBlockScopeContainer(host) || file; - currentFlow = initFlowNode({ flags: FlowFlags.Start }); + currentFlow = createFlowStartNode(FlowFlags.Start); parent = typeAlias; bind(typeAlias.typeExpression); const declName = getNameOfDeclaration(typeAlias); diff --git a/src/compiler/debug.ts b/src/compiler/debug.ts index 9bd8471f531b5..5bbccc78151ec 100644 --- a/src/compiler/debug.ts +++ b/src/compiler/debug.ts @@ -519,7 +519,7 @@ export namespace Debug { } }, __debugFlowFlags: { get(this: FlowNodeBase) { return formatEnum(this.flags, (ts as any).FlowFlags, /*isFlags*/ true); } }, - __debugToString: { value(this: FlowNodeBase) { return formatControlFlowGraph(this); } } + __debugToString: { value(this: FlowNodeBase) { return formatControlFlowGraph(this as FlowNode); } } }); } } @@ -912,7 +912,7 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n") return !!(f.flags & hasAntecedentFlags); } - function hasNode(f: FlowNode): f is Extract { + function hasNode(f: FlowNode): f is Extract { return !!(f.flags & hasNodeFlags); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index c9516c77df0f9..360ea5611d541 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3889,6 +3889,15 @@ export type FlowNode = export interface FlowNodeBase { flags: FlowFlags; id?: number; // Node id used by flow type cache in checker + + // Declared here so we can construct flow nodes with properties in a consistent order. + /** @internal */ node?: Node; + /** @internal */ antecedent?: FlowNode; + /** @internal */ antecedents?: FlowNode[]; + /** @internal */ target?: FlowLabel; + /** @internal */ switchStatement?: SwitchStatement; + /** @internal */ clauseStart?: number; + /** @internal */ clauseEnd?: number; } // FlowStart represents the start of a control flow. For a function expression or arrow