mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-06-14 16:56:06 -05:00
Make flow nodes more monomorphic
This commit is contained in:
@@ -148,8 +148,8 @@ namespace ts {
|
||||
let Symbol: new (flags: SymbolFlags, name: __String) => Symbol; // tslint:disable-line variable-name
|
||||
let classifiableNames: UnderscoreEscapedMap<true>;
|
||||
|
||||
const unreachableFlow: FlowNode = { flags: FlowFlags.Unreachable };
|
||||
const reportedUnreachableFlow: FlowNode = { flags: FlowFlags.Unreachable };
|
||||
const unreachableFlow = createFlowNode(FlowFlags.Unreachable, /*antecedent*/ undefined, /*node*/ undefined);
|
||||
const reportedUnreachableFlow: FlowNode = createFlowNode(FlowFlags.Unreachable, /*antecedent*/ undefined, /*node*/ undefined);
|
||||
|
||||
// state used to aggregate transform flags during bind.
|
||||
let subtreeTransformFlags: TransformFlags = TransformFlags.None;
|
||||
@@ -560,9 +560,9 @@ namespace ts {
|
||||
// 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 (!isIIFE) {
|
||||
currentFlow = { flags: FlowFlags.Start };
|
||||
currentFlow = createFlowNode(FlowFlags.Start, /*antecedent*/ undefined, /*node*/ undefined) as FlowStart;
|
||||
if (containerFlags & (ContainerFlags.IsFunctionExpression | ContainerFlags.IsObjectLiteralOrClassExpressionMethod)) {
|
||||
currentFlow.container = <FunctionExpression | ArrowFunction | MethodDeclaration>node;
|
||||
currentFlow.node = <FunctionExpression | ArrowFunction | MethodDeclaration>node;
|
||||
}
|
||||
}
|
||||
// We create a return control flow graph for IIFEs and constructors. For constructors
|
||||
@@ -842,18 +842,22 @@ namespace ts {
|
||||
return isNarrowableReference(expr);
|
||||
}
|
||||
|
||||
function createBranchLabel(): FlowLabel {
|
||||
function createFlowNode(flags: FlowFlags, antecedent: FlowNode | undefined, node: Node | undefined) {
|
||||
return {
|
||||
flags: FlowFlags.BranchLabel,
|
||||
antecedents: undefined
|
||||
};
|
||||
flags,
|
||||
antecedent,
|
||||
node,
|
||||
id: undefined,
|
||||
antecedents: undefined,
|
||||
} as FlowNode;
|
||||
}
|
||||
|
||||
function createBranchLabel(): FlowLabel {
|
||||
return createFlowNode(FlowFlags.BranchLabel, /*antecedent*/ undefined, /*node*/ undefined) as FlowLabel;
|
||||
}
|
||||
|
||||
function createLoopLabel(): FlowLabel {
|
||||
return {
|
||||
flags: FlowFlags.LoopLabel,
|
||||
antecedents: undefined
|
||||
};
|
||||
return createFlowNode(FlowFlags.LoopLabel, /*antecedent*/ undefined, /*node*/ undefined) as FlowLabel;
|
||||
}
|
||||
|
||||
function setFlowNodeReferenced(flow: FlowNode) {
|
||||
@@ -883,7 +887,7 @@ namespace ts {
|
||||
return antecedent;
|
||||
}
|
||||
setFlowNodeReferenced(antecedent);
|
||||
return flowNodeCreated({ flags, expression, antecedent });
|
||||
return flowNodeCreated(createFlowNode(flags, antecedent, expression));
|
||||
}
|
||||
|
||||
function createFlowSwitchClause(antecedent: FlowNode, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number): FlowNode {
|
||||
@@ -891,23 +895,26 @@ namespace ts {
|
||||
return antecedent;
|
||||
}
|
||||
setFlowNodeReferenced(antecedent);
|
||||
return flowNodeCreated({ flags: FlowFlags.SwitchClause, switchStatement, clauseStart, clauseEnd, antecedent });
|
||||
const result = createFlowNode(FlowFlags.SwitchClause, antecedent, /*node*/ undefined) as FlowSwitchClause;
|
||||
result.switchStatement = switchStatement;
|
||||
result.clauseStart = clauseStart;
|
||||
result.clauseEnd = clauseEnd;
|
||||
return flowNodeCreated(result);
|
||||
}
|
||||
|
||||
function createFlowAssignment(antecedent: FlowNode, node: Expression | VariableDeclaration | BindingElement): FlowNode {
|
||||
setFlowNodeReferenced(antecedent);
|
||||
return flowNodeCreated({ flags: FlowFlags.Assignment, antecedent, node });
|
||||
return flowNodeCreated(createFlowNode(FlowFlags.Assignment, antecedent, node));
|
||||
}
|
||||
|
||||
function createFlowCall(antecedent: FlowNode, node: CallExpression): FlowNode {
|
||||
setFlowNodeReferenced(antecedent);
|
||||
return flowNodeCreated({ flags: FlowFlags.Call, antecedent, node });
|
||||
return flowNodeCreated(createFlowNode(FlowFlags.Call, antecedent, node));
|
||||
}
|
||||
|
||||
function createFlowArrayMutation(antecedent: FlowNode, node: CallExpression | BinaryExpression): FlowNode {
|
||||
setFlowNodeReferenced(antecedent);
|
||||
const res: FlowArrayMutation = flowNodeCreated({ flags: FlowFlags.ArrayMutation, antecedent, node });
|
||||
return res;
|
||||
return flowNodeCreated(createFlowNode(FlowFlags.ArrayMutation, antecedent, node));
|
||||
}
|
||||
|
||||
function finishFlowLabel(flow: FlowLabel): FlowNode {
|
||||
@@ -1185,7 +1192,8 @@ namespace ts {
|
||||
//
|
||||
// extra edges that we inject allows to control this behavior
|
||||
// if when walking the flow we step on post-finally edge - we can mark matching pre-finally edge as locked so it will be skipped.
|
||||
const preFinallyFlow: PreFinallyFlow = { flags: FlowFlags.PreFinally, antecedent: preFinallyPrior, lock: {} };
|
||||
const preFinallyFlow = createFlowNode(FlowFlags.PreFinally, preFinallyPrior, /*node*/ undefined) as PreFinallyFlow;
|
||||
preFinallyFlow.lock = {};
|
||||
addAntecedent(preFinallyLabel, preFinallyFlow);
|
||||
|
||||
currentFlow = finishFlowLabel(preFinallyLabel);
|
||||
@@ -1204,7 +1212,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
if (!(currentFlow.flags & FlowFlags.Unreachable)) {
|
||||
const afterFinallyFlow: AfterFinallyFlow = flowNodeCreated({ flags: FlowFlags.AfterFinally, antecedent: currentFlow });
|
||||
const afterFinallyFlow = flowNodeCreated(createFlowNode(FlowFlags.AfterFinally, currentFlow, /*node*/ undefined) as AfterFinallyFlow);
|
||||
preFinallyFlow.lock = afterFinallyFlow;
|
||||
currentFlow = afterFinallyFlow;
|
||||
}
|
||||
@@ -1828,7 +1836,7 @@ namespace ts {
|
||||
const host = getJSDocHost(typeAlias);
|
||||
container = findAncestor(host.parent, n => !!(getContainerFlags(n) & ContainerFlags.IsContainer)) || file;
|
||||
blockScopeContainer = getEnclosingBlockScopeContainer(host) || file;
|
||||
currentFlow = { flags: FlowFlags.Start };
|
||||
currentFlow = createFlowNode(FlowFlags.Start, /*antecedent*/ undefined, /*node*/ undefined);
|
||||
parent = typeAlias;
|
||||
bind(typeAlias.typeExpression);
|
||||
if (isJSDocEnumTag(typeAlias) || !typeAlias.fullName || typeAlias.fullName.kind === SyntaxKind.Identifier) {
|
||||
|
||||
@@ -17001,7 +17001,7 @@ namespace ts {
|
||||
}
|
||||
else if (flags & FlowFlags.Start) {
|
||||
// Check if we should continue with the control flow of the containing function.
|
||||
const container = (<FlowStart>flow).container;
|
||||
const container = (<FlowStart>flow).node;
|
||||
if (container && container !== flowContainer &&
|
||||
reference.kind !== SyntaxKind.PropertyAccessExpression &&
|
||||
reference.kind !== SyntaxKind.ElementAccessExpression &&
|
||||
@@ -17150,7 +17150,7 @@ namespace ts {
|
||||
// *only* place a silent never type is ever generated.
|
||||
const assumeTrue = (flow.flags & FlowFlags.TrueCondition) !== 0;
|
||||
const nonEvolvingType = finalizeEvolvingArrayType(type);
|
||||
const narrowedType = narrowType(nonEvolvingType, flow.expression, assumeTrue);
|
||||
const narrowedType = narrowType(nonEvolvingType, flow.node, assumeTrue);
|
||||
if (narrowedType === nonEvolvingType) {
|
||||
return flowType;
|
||||
}
|
||||
|
||||
@@ -2565,6 +2565,22 @@ namespace ts {
|
||||
Condition = TrueCondition | FalseCondition
|
||||
}
|
||||
|
||||
export type FlowNode =
|
||||
| AfterFinallyFlow
|
||||
| PreFinallyFlow
|
||||
| FlowStart
|
||||
| FlowLabel
|
||||
| FlowAssignment
|
||||
| FlowCall
|
||||
| FlowCondition
|
||||
| FlowSwitchClause
|
||||
| FlowArrayMutation;
|
||||
|
||||
export interface FlowNodeBase {
|
||||
flags: FlowFlags;
|
||||
id: number | undefined; // Node id used by flow type cache in checker
|
||||
}
|
||||
|
||||
export interface FlowLock {
|
||||
locked?: boolean;
|
||||
}
|
||||
@@ -2578,18 +2594,11 @@ namespace ts {
|
||||
lock: FlowLock;
|
||||
}
|
||||
|
||||
export type FlowNode =
|
||||
| AfterFinallyFlow | PreFinallyFlow | FlowStart | FlowLabel | FlowAssignment | FlowCall | FlowCondition | FlowSwitchClause | FlowArrayMutation;
|
||||
export interface FlowNodeBase {
|
||||
flags: FlowFlags;
|
||||
id?: number; // Node id used by flow type cache in checker
|
||||
}
|
||||
|
||||
// FlowStart represents the start of a control flow. For a function expression or arrow
|
||||
// function, the container property references the function (which in turn has a flowNode
|
||||
// function, the node property references the function (which in turn has a flowNode
|
||||
// property for the containing control flow).
|
||||
export interface FlowStart extends FlowNodeBase {
|
||||
container?: FunctionExpression | ArrowFunction | MethodDeclaration;
|
||||
node?: FunctionExpression | ArrowFunction | MethodDeclaration;
|
||||
}
|
||||
|
||||
// FlowLabel represents a junction with multiple possible preceding control flows.
|
||||
@@ -2612,7 +2621,7 @@ namespace ts {
|
||||
// FlowCondition represents a condition that is known to be true or false at the
|
||||
// node's location in the control flow.
|
||||
export interface FlowCondition extends FlowNodeBase {
|
||||
expression: Expression;
|
||||
node: Expression;
|
||||
antecedent: FlowNode;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user