Make flow nodes more monomorphic

This commit is contained in:
Anders Hejlsberg
2019-08-09 10:15:47 +02:00
parent df02ad6e59
commit 99ab53edcd
3 changed files with 50 additions and 33 deletions

View File

@@ -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) {

View File

@@ -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;
}

View File

@@ -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;
}