Skip to content

Commit

Permalink
improve control-flow debug output
Browse files Browse the repository at this point in the history
  • Loading branch information
rbuckton committed Sep 30, 2019
1 parent 7463860 commit 0828674
Show file tree
Hide file tree
Showing 6 changed files with 374 additions and 202 deletions.
69 changes: 55 additions & 14 deletions src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,10 +209,11 @@ namespace ts {
let symbolCount = 0;

let Symbol: new (flags: SymbolFlags, name: __String) => Symbol;
let FlowNode: new (flags: FlowFlags) => FlowNodeBase;
let classifiableNames: UnderscoreEscapedMap<true>;

const unreachableFlow: FlowNode = { flags: FlowFlags.Unreachable };
const reportedUnreachableFlow: FlowNode = { flags: FlowFlags.Unreachable };
let unreachableFlow: FlowNode;
let reportedUnreachableFlow: FlowNode;

// state used to aggregate transform flags during bind.
let subtreeTransformFlags: TransformFlags = TransformFlags.None;
Expand All @@ -227,6 +228,17 @@ namespace ts {
return createDiagnosticForNodeInSourceFile(getSourceFileOfNode(node) || file, node, message, arg0, arg1, arg2);
}

function initializeBinder() {
const symbolConstructor = objectAllocator.getSymbolConstructor();
const flowNodeConstructor = objectAllocator.getFlowNodeConstructor();
if (Symbol !== symbolConstructor || FlowNode !== flowNodeConstructor) {
Symbol = symbolConstructor;
FlowNode = flowNodeConstructor;
unreachableFlow = new FlowNode(FlowFlags.Unreachable);
reportedUnreachableFlow = new FlowNode(FlowFlags.Unreachable);
}
}

function bindSourceFile(f: SourceFile, opts: CompilerOptions) {
file = f;
options = opts;
Expand All @@ -236,7 +248,7 @@ namespace ts {
symbolCount = 0;
skipTransformFlagAggregation = file.isDeclarationFile;

Symbol = objectAllocator.getSymbolConstructor();
initializeBinder();

if (!file.locals) {
bind(file);
Expand Down Expand Up @@ -623,7 +635,7 @@ 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 = createFlowStart();
if (containerFlags & (ContainerFlags.IsFunctionExpression | ContainerFlags.IsObjectLiteralOrClassExpressionMethod)) {
currentFlow.node = <FunctionExpression | ArrowFunction | MethodDeclaration>node;
}
Expand Down Expand Up @@ -917,11 +929,15 @@ namespace ts {
}

function createBranchLabel(): FlowLabel {
return { flags: FlowFlags.BranchLabel, antecedents: undefined };
const flow = new FlowNode(FlowFlags.BranchLabel) as FlowLabel;
flow.antecedents = undefined;
return flow;
}

function createLoopLabel(): FlowLabel {
return { flags: FlowFlags.LoopLabel, antecedents: undefined };
const flow = new FlowNode(FlowFlags.LoopLabel) as FlowLabel;
flow.antecedents = undefined;
return flow;
}

function setFlowNodeReferenced(flow: FlowNode) {
Expand All @@ -936,6 +952,10 @@ namespace ts {
}
}

function createFlowStart(): FlowStart {
return new FlowNode(FlowFlags.Start) as FlowStart;
}

function createFlowCondition(flags: FlowFlags, antecedent: FlowNode, expression: Expression | undefined): FlowNode {
if (antecedent.flags & FlowFlags.Unreachable) {
return antecedent;
Expand All @@ -953,30 +973,47 @@ namespace ts {
return antecedent;
}
setFlowNodeReferenced(antecedent);
return flowNodeCreated({ flags, antecedent, node: expression });
const flow = new FlowNode(flags) as FlowCondition;
flow.antecedent = antecedent;
flow.node = expression;
return flowNodeCreated(flow);
}

function createFlowSwitchClause(antecedent: FlowNode, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number): FlowNode {
if (!isNarrowingExpression(switchStatement.expression)) {
return antecedent;
}
setFlowNodeReferenced(antecedent);
return flowNodeCreated({ flags: FlowFlags.SwitchClause, antecedent, switchStatement, clauseStart, clauseEnd });
const flow = new FlowNode(FlowFlags.SwitchClause) as FlowSwitchClause;
flow.antecedent = antecedent;
flow.switchStatement = switchStatement;
flow.clauseStart = clauseStart;
flow.clauseEnd = clauseEnd;
return flowNodeCreated(flow);
}

function createFlowAssignment(antecedent: FlowNode, node: Expression | VariableDeclaration | BindingElement): FlowNode {
setFlowNodeReferenced(antecedent);
return flowNodeCreated({ flags: FlowFlags.Assignment, antecedent, node });
const flow = new FlowNode(FlowFlags.Assignment) as FlowAssignment;
flow.antecedent = antecedent;
flow.node = node;
return flowNodeCreated(flow);
}

function createFlowCall(antecedent: FlowNode, node: CallExpression): FlowNode {
setFlowNodeReferenced(antecedent);
return flowNodeCreated({ flags: FlowFlags.Call, antecedent, node });
const flow = new FlowNode(FlowFlags.Call) as FlowCall;
flow.antecedent = antecedent;
flow.node = node;
return flowNodeCreated(flow);
}

function createFlowArrayMutation(antecedent: FlowNode, node: CallExpression | BinaryExpression): FlowNode {
setFlowNodeReferenced(antecedent);
return flowNodeCreated({ flags: FlowFlags.ArrayMutation, antecedent, node });
const flow = new FlowNode(FlowFlags.ArrayMutation) as FlowArrayMutation;
flow.antecedent = antecedent;
flow.node = node;
return flowNodeCreated(flow);
}

function finishFlowLabel(flow: FlowLabel): FlowNode {
Expand Down Expand Up @@ -1259,7 +1296,9 @@ 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 = new FlowNode(FlowFlags.PreFinally) as PreFinallyFlow;
preFinallyFlow.antecedent = preFinallyPrior;
preFinallyFlow.lock = {};
addAntecedent(preFinallyLabel, preFinallyFlow);

currentFlow = finishFlowLabel(preFinallyLabel);
Expand All @@ -1278,7 +1317,9 @@ namespace ts {
}
}
if (!(currentFlow.flags & FlowFlags.Unreachable)) {
const afterFinallyFlow: AfterFinallyFlow = flowNodeCreated({ flags: FlowFlags.AfterFinally, antecedent: currentFlow });
const afterFinallyFlow = new FlowNode(FlowFlags.AfterFinally) as AfterFinallyFlow;
afterFinallyFlow.antecedent = currentFlow;
flowNodeCreated(afterFinallyFlow);
preFinallyFlow.lock = afterFinallyFlow;
currentFlow = afterFinallyFlow;
}
Expand Down Expand Up @@ -1986,7 +2027,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 = createFlowStart();
parent = typeAlias;
bind(typeAlias.typeExpression);
const declName = getNameOfDeclaration(typeAlias);
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18083,7 +18083,7 @@ namespace ts {
}

function getFlowNodeId(flow: FlowNode): number {
if (!flow.id) {
if (!flow.id || flow.id < 0) {
flow.id = nextFlowId;
nextFlowId++;
}
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1159,7 +1159,7 @@ namespace ts {
}
}

function stableSortIndices<T>(array: readonly T[], indices: number[], comparer: Comparer<T>) {
export function stableSortIndices<T>(array: readonly T[], indices: number[], comparer: Comparer<T>) {
// sort indices by value then position
indices.sort((x, y) => comparer(array[x], array[y]) || compareValues(x, y));
}
Expand Down
Loading

0 comments on commit 0828674

Please sign in to comment.