From 79db146e32a13185a566f5439cd42bb2c74f7d5c Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 4 May 2016 12:44:27 -0700 Subject: [PATCH 1/6] Refactor from FlowKind to FlowFlags --- src/compiler/binder.ts | 90 ++++++++++++++++++++--------------------- src/compiler/checker.ts | 47 ++++++++++----------- src/compiler/types.ts | 20 ++++----- 3 files changed, 80 insertions(+), 77 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 10e11e66eb0..9d5b1e39888 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -129,8 +129,8 @@ namespace ts { let Symbol: { new (flags: SymbolFlags, name: string): Symbol }; let classifiableNames: Map; - const unreachableFlow: FlowNode = { kind: FlowKind.Unreachable }; - const reportedUnreachableFlow: FlowNode = { kind: FlowKind.Unreachable }; + const unreachableFlow: FlowNode = { flags: FlowFlags.Unreachable }; + const reportedUnreachableFlow: FlowNode = { flags: FlowFlags.Unreachable }; function bindSourceFile(f: SourceFile, opts: CompilerOptions) { file = f; @@ -471,7 +471,7 @@ namespace ts { savedActiveLabels = activeLabels; hasExplicitReturn = false; - currentFlow = { kind: FlowKind.Start }; + currentFlow = { flags: FlowFlags.Start }; currentBreakTarget = undefined; currentContinueTarget = undefined; activeLabels = undefined; @@ -483,7 +483,7 @@ namespace ts { bindReachableStatement(node); - if (currentFlow.kind !== FlowKind.Unreachable && isFunctionLikeKind(kind) && nodeIsPresent((node).body)) { + if (!(currentFlow.flags & FlowFlags.Unreachable) && isFunctionLikeKind(kind) && nodeIsPresent((node).body)) { flags |= NodeFlags.HasImplicitReturn; if (hasExplicitReturn) { flags |= NodeFlags.HasExplicitReturn; @@ -639,50 +639,50 @@ namespace ts { return false; } - function createFlowLabel(): FlowLabel { + function createBranchLabel(): FlowLabel { return { - kind: FlowKind.Label, + flags: FlowFlags.BranchLabel, antecedents: undefined }; } - function createFlowLoopLabel(): FlowLabel { + function createLoopLabel(): FlowLabel { return { - kind: FlowKind.LoopLabel, + flags: FlowFlags.LoopLabel, antecedents: undefined }; } function addAntecedent(label: FlowLabel, antecedent: FlowNode): void { - if (antecedent.kind !== FlowKind.Unreachable && !contains(label.antecedents, antecedent)) { + if (!(antecedent.flags & FlowFlags.Unreachable) && !contains(label.antecedents, antecedent)) { (label.antecedents || (label.antecedents = [])).push(antecedent); } } - function createFlowCondition(antecedent: FlowNode, expression: Expression, assumeTrue: boolean): FlowNode { - if (antecedent.kind === FlowKind.Unreachable) { + function createFlowCondition(flags: FlowFlags, antecedent: FlowNode, expression: Expression): FlowNode { + if (antecedent.flags & FlowFlags.Unreachable) { return antecedent; } if (!expression) { - return assumeTrue ? antecedent : unreachableFlow; + return flags & FlowFlags.TrueCondition ? antecedent : unreachableFlow; } - if (expression.kind === SyntaxKind.TrueKeyword && !assumeTrue || expression.kind === SyntaxKind.FalseKeyword && assumeTrue) { + if (expression.kind === SyntaxKind.TrueKeyword && flags & FlowFlags.FalseCondition || + expression.kind === SyntaxKind.FalseKeyword && flags & FlowFlags.TrueCondition) { return unreachableFlow; } if (!isNarrowingExpression(expression)) { return antecedent; } return { - kind: FlowKind.Condition, + flags, antecedent, expression, - assumeTrue }; } function createFlowAssignment(antecedent: FlowNode, node: Expression | VariableDeclaration | BindingElement): FlowNode { return { - kind: FlowKind.Assignment, + flags: FlowFlags.Assignment, antecedent, node }; @@ -747,8 +747,8 @@ namespace ts { currentTrueTarget = saveTrueTarget; currentFalseTarget = saveFalseTarget; if (!node || !isLogicalExpression(node)) { - addAntecedent(trueTarget, createFlowCondition(currentFlow, node, /*assumeTrue*/ true)); - addAntecedent(falseTarget, createFlowCondition(currentFlow, node, /*assumeTrue*/ false)); + addAntecedent(trueTarget, createFlowCondition(FlowFlags.TrueCondition, currentFlow, node)); + addAntecedent(falseTarget, createFlowCondition(FlowFlags.FalseCondition, currentFlow, node)); } } @@ -763,9 +763,9 @@ namespace ts { } function bindWhileStatement(node: WhileStatement): void { - const preWhileLabel = createFlowLoopLabel(); - const preBodyLabel = createFlowLabel(); - const postWhileLabel = createFlowLabel(); + const preWhileLabel = createLoopLabel(); + const preBodyLabel = createBranchLabel(); + const postWhileLabel = createBranchLabel(); addAntecedent(preWhileLabel, currentFlow); currentFlow = preWhileLabel; bindCondition(node.expression, preBodyLabel, postWhileLabel); @@ -776,9 +776,9 @@ namespace ts { } function bindDoStatement(node: DoStatement): void { - const preDoLabel = createFlowLoopLabel(); - const preConditionLabel = createFlowLabel(); - const postDoLabel = createFlowLabel(); + const preDoLabel = createLoopLabel(); + const preConditionLabel = createBranchLabel(); + const postDoLabel = createBranchLabel(); addAntecedent(preDoLabel, currentFlow); currentFlow = preDoLabel; bindIterativeStatement(node.statement, postDoLabel, preConditionLabel); @@ -789,9 +789,9 @@ namespace ts { } function bindForStatement(node: ForStatement): void { - const preLoopLabel = createFlowLoopLabel(); - const preBodyLabel = createFlowLabel(); - const postLoopLabel = createFlowLabel(); + const preLoopLabel = createLoopLabel(); + const preBodyLabel = createBranchLabel(); + const postLoopLabel = createBranchLabel(); bind(node.initializer); addAntecedent(preLoopLabel, currentFlow); currentFlow = preLoopLabel; @@ -804,8 +804,8 @@ namespace ts { } function bindForInOrForOfStatement(node: ForInStatement | ForOfStatement): void { - const preLoopLabel = createFlowLoopLabel(); - const postLoopLabel = createFlowLabel(); + const preLoopLabel = createLoopLabel(); + const postLoopLabel = createBranchLabel(); addAntecedent(preLoopLabel, currentFlow); currentFlow = preLoopLabel; bind(node.expression); @@ -820,9 +820,9 @@ namespace ts { } function bindIfStatement(node: IfStatement): void { - const thenLabel = createFlowLabel(); - const elseLabel = createFlowLabel(); - const postIfLabel = createFlowLabel(); + const thenLabel = createBranchLabel(); + const elseLabel = createBranchLabel(); + const postIfLabel = createBranchLabel(); bindCondition(node.expression, thenLabel, elseLabel); currentFlow = finishFlowLabel(thenLabel); bind(node.thenStatement); @@ -875,7 +875,7 @@ namespace ts { } function bindTryStatement(node: TryStatement): void { - const postFinallyLabel = createFlowLabel(); + const postFinallyLabel = createBranchLabel(); const preTryFlow = currentFlow; // TODO: Every statement in try block is potentially an exit point! bind(node.tryBlock); @@ -893,7 +893,7 @@ namespace ts { } function bindSwitchStatement(node: SwitchStatement): void { - const postSwitchLabel = createFlowLabel(); + const postSwitchLabel = createBranchLabel(); bind(node.expression); const saveBreakTarget = currentBreakTarget; const savePreSwitchCaseFlow = preSwitchCaseFlow; @@ -915,17 +915,17 @@ namespace ts { for (let i = 0; i < clauses.length; i++) { const clause = clauses[i]; if (clause.statements.length) { - if (currentFlow.kind === FlowKind.Unreachable) { + if (currentFlow.flags & FlowFlags.Unreachable) { currentFlow = preSwitchCaseFlow; } else { - const preCaseLabel = createFlowLabel(); + const preCaseLabel = createBranchLabel(); addAntecedent(preCaseLabel, preSwitchCaseFlow); addAntecedent(preCaseLabel, currentFlow); currentFlow = finishFlowLabel(preCaseLabel); } bind(clause); - if (currentFlow.kind !== FlowKind.Unreachable && i !== clauses.length - 1 && options.noFallthroughCasesInSwitch) { + if (!(currentFlow.flags & FlowFlags.Unreachable) && i !== clauses.length - 1 && options.noFallthroughCasesInSwitch) { errorOnFirstToken(clause, Diagnostics.Fallthrough_case_in_switch); } } @@ -951,8 +951,8 @@ namespace ts { } function bindLabeledStatement(node: LabeledStatement): void { - const preStatementLabel = createFlowLoopLabel(); - const postStatementLabel = createFlowLabel(); + const preStatementLabel = createLoopLabel(); + const postStatementLabel = createBranchLabel(); bind(node.label); addAntecedent(preStatementLabel, currentFlow); const activeLabel = pushActiveLabel(node.label.text, postStatementLabel, preStatementLabel); @@ -1001,7 +1001,7 @@ namespace ts { } function bindLogicalExpression(node: BinaryExpression, trueTarget: FlowLabel, falseTarget: FlowLabel) { - const preRightLabel = createFlowLabel(); + const preRightLabel = createBranchLabel(); if (node.operatorToken.kind === SyntaxKind.AmpersandAmpersandToken) { bindCondition(node.left, preRightLabel, falseTarget); } @@ -1031,7 +1031,7 @@ namespace ts { const operator = node.operatorToken.kind; if (operator === SyntaxKind.AmpersandAmpersandToken || operator === SyntaxKind.BarBarToken) { if (isTopLevelLogicalExpression(node)) { - const postExpressionLabel = createFlowLabel(); + const postExpressionLabel = createBranchLabel(); bindLogicalExpression(node, postExpressionLabel, postExpressionLabel); currentFlow = finishFlowLabel(postExpressionLabel); } @@ -1048,9 +1048,9 @@ namespace ts { } function bindConditionalExpressionFlow(node: ConditionalExpression) { - const trueLabel = createFlowLabel(); - const falseLabel = createFlowLabel(); - const postExpressionLabel = createFlowLabel(); + const trueLabel = createBranchLabel(); + const falseLabel = createBranchLabel(); + const postExpressionLabel = createBranchLabel(); bindCondition(node.condition, trueLabel, falseLabel); currentFlow = finishFlowLabel(trueLabel); bind(node.whenTrue); @@ -2065,7 +2065,7 @@ namespace ts { } function checkUnreachable(node: Node): boolean { - if (currentFlow.kind !== FlowKind.Unreachable) { + if (!(currentFlow.flags & FlowFlags.Unreachable)) { return false; } if (currentFlow === unreachableFlow) { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 504cb17def2..c067bffbf66 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7585,27 +7585,28 @@ namespace ts { function getTypeAtFlowNode(flow: FlowNode): Type { while (true) { - switch (flow.kind) { - case FlowKind.Assignment: - const type = getTypeAtFlowAssignment(flow); - if (!type) { - flow = (flow).antecedent; - continue; - } - return type; - case FlowKind.Condition: - return getTypeAtFlowCondition(flow); - case FlowKind.Label: - case FlowKind.LoopLabel: - if ((flow).antecedents.length === 1) { - flow = (flow).antecedents[0]; - continue; - } - return getTypeAtFlowLabel(flow); - case FlowKind.Unreachable: - // Unreachable code errors are reported in the binding phase. Here we - // simply return the declared type to reduce follow-on errors. - return declaredType; + if (flow.flags & FlowFlags.Assignment) { + const type = getTypeAtFlowAssignment(flow); + if (!type) { + flow = (flow).antecedent; + continue; + } + return type; + } + if (flow.flags & FlowFlags.Condition) { + return getTypeAtFlowCondition(flow); + } + if (flow.flags & FlowFlags.Label) { + if ((flow).antecedents.length === 1) { + flow = (flow).antecedents[0]; + continue; + } + return getTypeAtFlowLabel(flow); + } + if (flow.flags & FlowFlags.Unreachable) { + // Unreachable code errors are reported in the binding phase. Here we + // simply return the declared type to reduce follow-on errors. + return declaredType; } // At the top of the flow we have the initial type return initialType; @@ -7644,7 +7645,7 @@ namespace ts { function getTypeAtFlowCondition(flow: FlowCondition) { const type = getTypeAtFlowNode(flow.antecedent); - return type && narrowType(type, flow.expression, flow.assumeTrue); + return type && narrowType(type, flow.expression, (flow.flags & FlowFlags.TrueCondition) !== 0); } function getTypeAtFlowNodeCached(flow: FlowNode) { @@ -7676,7 +7677,7 @@ namespace ts { function getTypeAtFlowLabel(flow: FlowLabel) { const antecedentTypes: Type[] = []; for (const antecedent of flow.antecedents) { - const type = flow.kind === FlowKind.LoopLabel ? + const type = flow.flags & FlowFlags.LoopLabel ? getTypeAtFlowNodeCached(antecedent) : getTypeAtFlowNode(antecedent); if (!type) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index d0d411a710e..02f451d1d61 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1515,17 +1515,20 @@ namespace ts { isBracketed: boolean; } - export const enum FlowKind { - Unreachable, - Start, - Label, - LoopLabel, - Assignment, - Condition + export const enum FlowFlags { + Unreachable = 1 << 0, + Start = 1 << 1, + BranchLabel = 1 << 2, + LoopLabel = 1 << 3, + Assignment = 1 << 4, + TrueCondition = 1 << 5, + FalseCondition = 1 << 6, + Label = BranchLabel | LoopLabel, + Condition = TrueCondition | FalseCondition } export interface FlowNode { - kind: FlowKind; // Node kind + flags: FlowFlags; id?: number; // Node id used by flow type cache in checker } @@ -1545,7 +1548,6 @@ namespace ts { // node's location in the control flow. export interface FlowCondition extends FlowNode { expression: Expression; - assumeTrue: boolean; antecedent: FlowNode; } From e49773a7e90022c4c5539151595513ef8873d21f Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 4 May 2016 13:24:05 -0700 Subject: [PATCH 2/6] Local caching of types of shared flow nodes --- src/compiler/binder.ts | 7 +++++++ src/compiler/checker.ts | 40 ++++++++++++++++++++++++++++------------ src/compiler/types.ts | 2 ++ 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 9d5b1e39888..45a3bfe3e5a 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -653,9 +653,14 @@ namespace ts { }; } + function setFlowNodeReferenced(flow: FlowNode) { + flow.flags |= flow.flags & FlowFlags.Referenced ? FlowFlags.Shared : FlowFlags.Referenced; + } + function addAntecedent(label: FlowLabel, antecedent: FlowNode): void { if (!(antecedent.flags & FlowFlags.Unreachable) && !contains(label.antecedents, antecedent)) { (label.antecedents || (label.antecedents = [])).push(antecedent); + setFlowNodeReferenced(antecedent); } } @@ -673,6 +678,7 @@ namespace ts { if (!isNarrowingExpression(expression)) { return antecedent; } + setFlowNodeReferenced(antecedent); return { flags, antecedent, @@ -681,6 +687,7 @@ namespace ts { } function createFlowAssignment(antecedent: FlowNode, node: Expression | VariableDeclaration | BindingElement): FlowNode { + setFlowNodeReferenced(antecedent); return { flags: FlowFlags.Assignment, antecedent, diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c067bffbf66..0964ed3f11a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7378,12 +7378,17 @@ namespace ts { return false; } - function getFlowTypeCache(flow: FlowNode): Map { + function getFlowNodeId(flow: FlowNode): number { if (!flow.id) { flow.id = nextFlowId; nextFlowId++; } - return flowTypeCaches[flow.id] || (flowTypeCaches[flow.id] = {}); + return flow.id; + } + + function getFlowTypeCache(flow: FlowNode): Map { + const id = getFlowNodeId(flow); + return flowTypeCaches[id] || (flowTypeCaches[id] = {}); } function typeMaybeAssignableTo(source: Type, target: Type) { @@ -7578,6 +7583,7 @@ namespace ts { function getFlowTypeOfReference(reference: Node, declaredType: Type, initialType: Type) { let key: string; + let cachedTypes: Type[]; if (!reference.flowNode || declaredType === initialType && !(declaredType.flags & TypeFlags.Narrowable)) { return declaredType; } @@ -7585,31 +7591,41 @@ namespace ts { function getTypeAtFlowNode(flow: FlowNode): Type { while (true) { + let type: Type; + let id = flow.flags & FlowFlags.Shared ? getFlowNodeId(flow) : 0; + if (id && cachedTypes && (type = cachedTypes[id])) { + return type; + } if (flow.flags & FlowFlags.Assignment) { - const type = getTypeAtFlowAssignment(flow); + type = getTypeAtFlowAssignment(flow); if (!type) { flow = (flow).antecedent; continue; } - return type; } - if (flow.flags & FlowFlags.Condition) { - return getTypeAtFlowCondition(flow); + else if (flow.flags & FlowFlags.Condition) { + type = getTypeAtFlowCondition(flow); } - if (flow.flags & FlowFlags.Label) { + else if (flow.flags & FlowFlags.Label) { if ((flow).antecedents.length === 1) { flow = (flow).antecedents[0]; continue; } - return getTypeAtFlowLabel(flow); + type = getTypeAtFlowLabel(flow); } - if (flow.flags & FlowFlags.Unreachable) { + else if (flow.flags & FlowFlags.Unreachable) { // Unreachable code errors are reported in the binding phase. Here we // simply return the declared type to reduce follow-on errors. - return declaredType; + type = declaredType; } - // At the top of the flow we have the initial type - return initialType; + else { + // At the top of the flow we have the initial type + type = initialType; + } + if (id) { + (cachedTypes || (cachedTypes = []))[id] = type; + } + return type; } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 02f451d1d61..ecf75b116ff 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1523,6 +1523,8 @@ namespace ts { Assignment = 1 << 4, TrueCondition = 1 << 5, FalseCondition = 1 << 6, + Referenced = 1 << 7, + Shared = 1 << 8, Label = BranchLabel | LoopLabel, Condition = TrueCondition | FalseCondition } From 9fa5971af08c0075648556cb65ed0620c277fe5d Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 4 May 2016 15:17:39 -0700 Subject: [PATCH 3/6] Skip simple conditional flows in control flow graph --- src/compiler/binder.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 45a3bfe3e5a..8234075612e 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -695,6 +695,23 @@ namespace ts { }; } + function skipSimpleConditionalFlow(flow: FlowNode) { + // We skip over simple conditional flows of the form 'x ? aaa : bbb', where 'aaa' and 'bbb' contain + // no constructs that affect control flow type analysis. Such simple flows have no effect on the + // code paths that follow and ignoring them means we'll do less work. + if (flow.flags & FlowFlags.BranchLabel && (flow).antecedents.length === 2) { + const a = (flow).antecedents[0]; + const b = (flow).antecedents[1]; + if ((a.flags & FlowFlags.TrueCondition && b.flags & FlowFlags.FalseCondition || + a.flags & FlowFlags.FalseCondition && b.flags & FlowFlags.TrueCondition) && + (a).antecedent === (b).antecedent && + (a).expression === (b).expression) { + return (a).antecedent; + } + } + return flow; + } + function finishFlowLabel(flow: FlowLabel): FlowNode { const antecedents = flow.antecedents; if (!antecedents) { @@ -703,7 +720,7 @@ namespace ts { if (antecedents.length === 1) { return antecedents[0]; } - return flow; + return skipSimpleConditionalFlow(flow); } function isStatementCondition(node: Node) { From 3a4b56d6b7d2dab03f909a26b549d140b9273978 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 4 May 2016 15:18:57 -0700 Subject: [PATCH 4/6] Cache visited nodes and types in shared arrays to reduce object allocation --- src/compiler/binder.ts | 1 + src/compiler/checker.ts | 41 +++++++++++++++++++++++++---------------- src/compiler/types.ts | 18 +++++++++--------- 3 files changed, 35 insertions(+), 25 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 8234075612e..fb24f4cc45e 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -654,6 +654,7 @@ namespace ts { } function setFlowNodeReferenced(flow: FlowNode) { + // On first reference we set the Referenced flag, thereafter we set the Shared flag flow.flags |= flow.flags & FlowFlags.Referenced ? FlowFlags.Shared : FlowFlags.Referenced; } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0964ed3f11a..b03aa99e7d9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -183,6 +183,7 @@ namespace ts { let flowStackStart = 0; let flowStackCount = 0; + let visitedFlowCount = 0; const tupleTypes: Map = {}; const unionTypes: Map = {}; @@ -199,6 +200,8 @@ namespace ts { const flowTypeCaches: Map[] = []; const flowStackNodes: FlowNode[] = []; const flowStackCacheKeys: string[] = []; + const visitedFlowNodes: FlowNode[] = []; + const visitedFlowTypes: Type[] = []; const potentialThisCollisions: Node[] = []; const awaitedTypeStack: number[] = []; @@ -7378,17 +7381,12 @@ namespace ts { return false; } - function getFlowNodeId(flow: FlowNode): number { + function getFlowTypeCache(flow: FlowNode): Map { if (!flow.id) { flow.id = nextFlowId; nextFlowId++; } - return flow.id; - } - - function getFlowTypeCache(flow: FlowNode): Map { - const id = getFlowNodeId(flow); - return flowTypeCaches[id] || (flowTypeCaches[id] = {}); + return flowTypeCaches[flow.id] || (flowTypeCaches[flow.id] = {}); } function typeMaybeAssignableTo(source: Type, target: Type) { @@ -7583,19 +7581,27 @@ namespace ts { function getFlowTypeOfReference(reference: Node, declaredType: Type, initialType: Type) { let key: string; - let cachedTypes: Type[]; if (!reference.flowNode || declaredType === initialType && !(declaredType.flags & TypeFlags.Narrowable)) { return declaredType; } - return getTypeAtFlowNode(reference.flowNode); + const visitedFlowStart = visitedFlowCount; + const result = getTypeAtFlowNode(reference.flowNode); + visitedFlowCount = visitedFlowStart; + return result; function getTypeAtFlowNode(flow: FlowNode): Type { while (true) { - let type: Type; - let id = flow.flags & FlowFlags.Shared ? getFlowNodeId(flow) : 0; - if (id && cachedTypes && (type = cachedTypes[id])) { - return type; + if (flow.flags & FlowFlags.Shared) { + // We cache results of flow type resolution for shared nodes that were previously visited in + // the same getFlowTypeOfReference invocation. A node is considered shared when it is the + // antecedent of more than one node. + for (let i = visitedFlowStart; i < visitedFlowCount; i++) { + if (visitedFlowNodes[i] === flow) { + return visitedFlowTypes[i]; + } + } } + let type: Type; if (flow.flags & FlowFlags.Assignment) { type = getTypeAtFlowAssignment(flow); if (!type) { @@ -7619,11 +7625,14 @@ namespace ts { type = declaredType; } else { - // At the top of the flow we have the initial type + // At the top of the flow we have the initial type. type = initialType; } - if (id) { - (cachedTypes || (cachedTypes = []))[id] = type; + if (flow.flags & FlowFlags.Shared) { + // Record visited node and the associated type in the cache. + visitedFlowNodes[visitedFlowCount] = flow; + visitedFlowTypes[visitedFlowCount] = type; + visitedFlowCount++; } return type; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index ecf75b116ff..db9bd2469db 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1516,15 +1516,15 @@ namespace ts { } export const enum FlowFlags { - Unreachable = 1 << 0, - Start = 1 << 1, - BranchLabel = 1 << 2, - LoopLabel = 1 << 3, - Assignment = 1 << 4, - TrueCondition = 1 << 5, - FalseCondition = 1 << 6, - Referenced = 1 << 7, - Shared = 1 << 8, + Unreachable = 1 << 0, // Unreachable code + Start = 1 << 1, // Start of flow graph + BranchLabel = 1 << 2, // Non-looping junction + LoopLabel = 1 << 3, // Looping junction + Assignment = 1 << 4, // Assignment + TrueCondition = 1 << 5, // Condition known to be true + FalseCondition = 1 << 6, // Condition known to be false + Referenced = 1 << 7, // Referenced as antecedent once + Shared = 1 << 8, // Referenced as antecedent more than once Label = BranchLabel | LoopLabel, Condition = TrueCondition | FalseCondition } From 615210f1332c3ad45e8cb6d05fc928da609563ae Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 4 May 2016 15:26:04 -0700 Subject: [PATCH 5/6] Accepting new baselines --- .../reference/controlFlowBinaryOrExpression.types | 4 ++-- tests/baselines/reference/typeGuardEnums.types | 4 ++-- tests/baselines/reference/typeGuardNesting.types | 8 ++++---- .../reference/typeGuardTautologicalConsistiency.types | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/baselines/reference/controlFlowBinaryOrExpression.types b/tests/baselines/reference/controlFlowBinaryOrExpression.types index 8c1cd32d8d9..c7651a09d1e 100644 --- a/tests/baselines/reference/controlFlowBinaryOrExpression.types +++ b/tests/baselines/reference/controlFlowBinaryOrExpression.types @@ -87,7 +87,7 @@ if (isNodeList(sourceObj)) { if (isHTMLCollection(sourceObj)) { >isHTMLCollection(sourceObj) : boolean >isHTMLCollection : (sourceObj: any) => sourceObj is HTMLCollection ->sourceObj : HTMLCollection | { a: string; } +>sourceObj : { a: string; } | HTMLCollection sourceObj.length; >sourceObj.length : number @@ -99,7 +99,7 @@ if (isNodeList(sourceObj) || isHTMLCollection(sourceObj)) { >isNodeList(sourceObj) || isHTMLCollection(sourceObj) : boolean >isNodeList(sourceObj) : boolean >isNodeList : (sourceObj: any) => sourceObj is NodeList ->sourceObj : HTMLCollection | { a: string; } +>sourceObj : { a: string; } | HTMLCollection >isHTMLCollection(sourceObj) : boolean >isHTMLCollection : (sourceObj: any) => sourceObj is HTMLCollection >sourceObj : { a: string; } diff --git a/tests/baselines/reference/typeGuardEnums.types b/tests/baselines/reference/typeGuardEnums.types index 2bef6915046..1d39a81d78a 100644 --- a/tests/baselines/reference/typeGuardEnums.types +++ b/tests/baselines/reference/typeGuardEnums.types @@ -27,7 +27,7 @@ else { if (typeof x !== "number") { >typeof x !== "number" : boolean >typeof x : string ->x : number | string +>x : number | string | E | V >"number" : string x; // string @@ -35,6 +35,6 @@ if (typeof x !== "number") { } else { x; // number|E|V ->x : number +>x : number | E | V } diff --git a/tests/baselines/reference/typeGuardNesting.types b/tests/baselines/reference/typeGuardNesting.types index 2b18e232412..afd060284d9 100644 --- a/tests/baselines/reference/typeGuardNesting.types +++ b/tests/baselines/reference/typeGuardNesting.types @@ -34,7 +34,7 @@ if ((typeof strOrBool === 'boolean' && !strOrBool) || typeof strOrBool === 'stri >(typeof strOrBool === 'boolean') : boolean >typeof strOrBool === 'boolean' : boolean >typeof strOrBool : string ->strOrBool : string | boolean +>strOrBool : boolean | string >'boolean' : string >strOrBool : boolean >false : boolean @@ -56,7 +56,7 @@ if ((typeof strOrBool === 'boolean' && !strOrBool) || typeof strOrBool === 'stri >(typeof strOrBool !== 'string') : boolean >typeof strOrBool !== 'string' : boolean >typeof strOrBool : string ->strOrBool : string | boolean +>strOrBool : boolean | string >'string' : string >strOrBool : boolean >false : boolean @@ -94,7 +94,7 @@ if ((typeof strOrBool !== 'string' && !strOrBool) || typeof strOrBool !== 'boole >(typeof strOrBool === 'boolean') : boolean >typeof strOrBool === 'boolean' : boolean >typeof strOrBool : string ->strOrBool : string | boolean +>strOrBool : boolean | string >'boolean' : string >strOrBool : boolean >false : boolean @@ -116,7 +116,7 @@ if ((typeof strOrBool !== 'string' && !strOrBool) || typeof strOrBool !== 'boole >(typeof strOrBool !== 'string') : boolean >typeof strOrBool !== 'string' : boolean >typeof strOrBool : string ->strOrBool : string | boolean +>strOrBool : boolean | string >'string' : string >strOrBool : boolean >false : boolean diff --git a/tests/baselines/reference/typeGuardTautologicalConsistiency.types b/tests/baselines/reference/typeGuardTautologicalConsistiency.types index cf9e82d9fd2..256eaec8293 100644 --- a/tests/baselines/reference/typeGuardTautologicalConsistiency.types +++ b/tests/baselines/reference/typeGuardTautologicalConsistiency.types @@ -23,7 +23,7 @@ if (typeof stringOrNumber === "number" && typeof stringOrNumber !== "number") { >typeof stringOrNumber === "number" && typeof stringOrNumber !== "number" : boolean >typeof stringOrNumber === "number" : boolean >typeof stringOrNumber : string ->stringOrNumber : number | string +>stringOrNumber : string | number >"number" : string >typeof stringOrNumber !== "number" : boolean >typeof stringOrNumber : string From 65891766e568db630dadd3ba3f71e59a7cab780c Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 4 May 2016 15:43:30 -0700 Subject: [PATCH 6/6] Adding regression test --- .../baselines/reference/controlFlowCaching.js | 120 ++++ .../reference/controlFlowCaching.symbols | 279 ++++++++ .../reference/controlFlowCaching.types | 607 ++++++++++++++++++ tests/cases/compiler/controlFlowCaching.ts | 68 ++ 4 files changed, 1074 insertions(+) create mode 100644 tests/baselines/reference/controlFlowCaching.js create mode 100644 tests/baselines/reference/controlFlowCaching.symbols create mode 100644 tests/baselines/reference/controlFlowCaching.types create mode 100644 tests/cases/compiler/controlFlowCaching.ts diff --git a/tests/baselines/reference/controlFlowCaching.js b/tests/baselines/reference/controlFlowCaching.js new file mode 100644 index 00000000000..fc102b8e823 --- /dev/null +++ b/tests/baselines/reference/controlFlowCaching.js @@ -0,0 +1,120 @@ +//// [controlFlowCaching.ts] + +// Repro for #8401 + +function f(dim, offsets, arr, acommon, centerAnchorLimit, g, has, lin) { + var isRtl = this._isRtl(); // chart mirroring + // prepare variable + var o = this.opt, ta = this.chart.theme.axis, position = o.position, + leftBottom = position !== "rightOrTop", rotation = o.rotation % 360, + start, stop, titlePos, titleRotation = 0, titleOffset, axisVector, tickVector, anchorOffset, labelOffset, labelAlign, + labelGap = this.chart.theme.axis.tick.labelGap, + taFont = o.font || (ta.majorTick && ta.majorTick.font) || (ta.tick && ta.tick.font), + taTitleFont = o.titleFont || (ta.title && ta.title.font), + taFontColor = o.fontColor || (ta.majorTick && ta.majorTick.fontColor) || (ta.tick && ta.tick.fontColor) || "black", + taTitleFontColor = o.titleFontColor || (ta.title && ta.title.fontColor) || "black", + taTitleGap = (o.titleGap == 0) ? 0 : o.titleGap || (ta.title && ta.title.gap) || 15, + taTitleOrientation = o.titleOrientation || (ta.title && ta.title.orientation) || "axis", + taMajorTick = this.chart.theme.getTick("major", o), + taMinorTick = this.chart.theme.getTick("minor", o), + taMicroTick = this.chart.theme.getTick("micro", o), + + taStroke = "stroke" in o ? o.stroke : ta.stroke, + size = taFont ? g.normalizedLength(g.splitFontString(taFont).size) : 0, + cosr = Math.abs(Math.cos(rotation * Math.PI / 180)), + sinr = Math.abs(Math.sin(rotation * Math.PI / 180)), + tsize = taTitleFont ? g.normalizedLength(g.splitFontString(taTitleFont).size) : 0; + if (rotation < 0) { + rotation += 360; + } + var cachedLabelW = this._getMaxLabelSize(); + cachedLabelW = cachedLabelW && cachedLabelW.majLabelW; + titleOffset = size * cosr + (cachedLabelW || 0) * sinr + labelGap + Math.max(taMajorTick.length > 0 ? taMajorTick.length : 0, + taMinorTick.length > 0 ? taMinorTick.length : 0) + + tsize + taTitleGap; + axisVector = { x: isRtl ? -1 : 1, y: 0 }; // chart mirroring + switch (rotation) { + default: + if (rotation < (90 - centerAnchorLimit)) { + labelOffset.y = leftBottom ? size : 0; + } else if (rotation < (90 + centerAnchorLimit)) { + labelOffset.x = -size * 0.4; + } else if (rotation < 180) { + labelOffset.y = leftBottom ? 0 : -size; + } else if (rotation < (270 - centerAnchorLimit)) { + labelOffset.y = leftBottom ? 0 : -size; + } else if (rotation < (270 + centerAnchorLimit)) { + labelOffset.y = leftBottom ? size * 0.4 : 0; + } else { + labelOffset.y = leftBottom ? size : 0; + } + } + + titleRotation = (taTitleOrientation && taTitleOrientation == "away") ? 180 : 0; + titlePos.y = offsets.t - titleOffset + (titleRotation ? 0 : tsize); + switch (labelAlign) { + case "start": + labelAlign = "end"; + break; + case "end": + labelAlign = "start"; + break; + case "middle": + labelOffset.y -= size; + break; + } + + let _ = rotation; +} + + +//// [controlFlowCaching.js] +// Repro for #8401 +function f(dim, offsets, arr, acommon, centerAnchorLimit, g, has, lin) { + var isRtl = this._isRtl(); // chart mirroring + // prepare variable + var o = this.opt, ta = this.chart.theme.axis, position = o.position, leftBottom = position !== "rightOrTop", rotation = o.rotation % 360, start, stop, titlePos, titleRotation = 0, titleOffset, axisVector, tickVector, anchorOffset, labelOffset, labelAlign, labelGap = this.chart.theme.axis.tick.labelGap, taFont = o.font || (ta.majorTick && ta.majorTick.font) || (ta.tick && ta.tick.font), taTitleFont = o.titleFont || (ta.title && ta.title.font), taFontColor = o.fontColor || (ta.majorTick && ta.majorTick.fontColor) || (ta.tick && ta.tick.fontColor) || "black", taTitleFontColor = o.titleFontColor || (ta.title && ta.title.fontColor) || "black", taTitleGap = (o.titleGap == 0) ? 0 : o.titleGap || (ta.title && ta.title.gap) || 15, taTitleOrientation = o.titleOrientation || (ta.title && ta.title.orientation) || "axis", taMajorTick = this.chart.theme.getTick("major", o), taMinorTick = this.chart.theme.getTick("minor", o), taMicroTick = this.chart.theme.getTick("micro", o), taStroke = "stroke" in o ? o.stroke : ta.stroke, size = taFont ? g.normalizedLength(g.splitFontString(taFont).size) : 0, cosr = Math.abs(Math.cos(rotation * Math.PI / 180)), sinr = Math.abs(Math.sin(rotation * Math.PI / 180)), tsize = taTitleFont ? g.normalizedLength(g.splitFontString(taTitleFont).size) : 0; + if (rotation < 0) { + rotation += 360; + } + var cachedLabelW = this._getMaxLabelSize(); + cachedLabelW = cachedLabelW && cachedLabelW.majLabelW; + titleOffset = size * cosr + (cachedLabelW || 0) * sinr + labelGap + Math.max(taMajorTick.length > 0 ? taMajorTick.length : 0, taMinorTick.length > 0 ? taMinorTick.length : 0) + + tsize + taTitleGap; + axisVector = { x: isRtl ? -1 : 1, y: 0 }; // chart mirroring + switch (rotation) { + default: + if (rotation < (90 - centerAnchorLimit)) { + labelOffset.y = leftBottom ? size : 0; + } + else if (rotation < (90 + centerAnchorLimit)) { + labelOffset.x = -size * 0.4; + } + else if (rotation < 180) { + labelOffset.y = leftBottom ? 0 : -size; + } + else if (rotation < (270 - centerAnchorLimit)) { + labelOffset.y = leftBottom ? 0 : -size; + } + else if (rotation < (270 + centerAnchorLimit)) { + labelOffset.y = leftBottom ? size * 0.4 : 0; + } + else { + labelOffset.y = leftBottom ? size : 0; + } + } + titleRotation = (taTitleOrientation && taTitleOrientation == "away") ? 180 : 0; + titlePos.y = offsets.t - titleOffset + (titleRotation ? 0 : tsize); + switch (labelAlign) { + case "start": + labelAlign = "end"; + break; + case "end": + labelAlign = "start"; + break; + case "middle": + labelOffset.y -= size; + break; + } + var _ = rotation; +} diff --git a/tests/baselines/reference/controlFlowCaching.symbols b/tests/baselines/reference/controlFlowCaching.symbols new file mode 100644 index 00000000000..74534ce75a1 --- /dev/null +++ b/tests/baselines/reference/controlFlowCaching.symbols @@ -0,0 +1,279 @@ +=== tests/cases/compiler/controlFlowCaching.ts === + +// Repro for #8401 + +function f(dim, offsets, arr, acommon, centerAnchorLimit, g, has, lin) { +>f : Symbol(f, Decl(controlFlowCaching.ts, 0, 0)) +>dim : Symbol(dim, Decl(controlFlowCaching.ts, 3, 11)) +>offsets : Symbol(offsets, Decl(controlFlowCaching.ts, 3, 15)) +>arr : Symbol(arr, Decl(controlFlowCaching.ts, 3, 24)) +>acommon : Symbol(acommon, Decl(controlFlowCaching.ts, 3, 29)) +>centerAnchorLimit : Symbol(centerAnchorLimit, Decl(controlFlowCaching.ts, 3, 38)) +>g : Symbol(g, Decl(controlFlowCaching.ts, 3, 57)) +>has : Symbol(has, Decl(controlFlowCaching.ts, 3, 60)) +>lin : Symbol(lin, Decl(controlFlowCaching.ts, 3, 65)) + + var isRtl = this._isRtl(); // chart mirroring +>isRtl : Symbol(isRtl, Decl(controlFlowCaching.ts, 4, 7)) + + // prepare variable + var o = this.opt, ta = this.chart.theme.axis, position = o.position, +>o : Symbol(o, Decl(controlFlowCaching.ts, 6, 7)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) +>position : Symbol(position, Decl(controlFlowCaching.ts, 6, 49)) +>o : Symbol(o, Decl(controlFlowCaching.ts, 6, 7)) + + leftBottom = position !== "rightOrTop", rotation = o.rotation % 360, +>leftBottom : Symbol(leftBottom, Decl(controlFlowCaching.ts, 6, 72)) +>position : Symbol(position, Decl(controlFlowCaching.ts, 6, 49)) +>rotation : Symbol(rotation, Decl(controlFlowCaching.ts, 7, 47)) +>o : Symbol(o, Decl(controlFlowCaching.ts, 6, 7)) + + start, stop, titlePos, titleRotation = 0, titleOffset, axisVector, tickVector, anchorOffset, labelOffset, labelAlign, +>start : Symbol(start, Decl(controlFlowCaching.ts, 7, 76)) +>stop : Symbol(stop, Decl(controlFlowCaching.ts, 8, 14)) +>titlePos : Symbol(titlePos, Decl(controlFlowCaching.ts, 8, 20)) +>titleRotation : Symbol(titleRotation, Decl(controlFlowCaching.ts, 8, 30)) +>titleOffset : Symbol(titleOffset, Decl(controlFlowCaching.ts, 8, 49)) +>axisVector : Symbol(axisVector, Decl(controlFlowCaching.ts, 8, 62)) +>tickVector : Symbol(tickVector, Decl(controlFlowCaching.ts, 8, 74)) +>anchorOffset : Symbol(anchorOffset, Decl(controlFlowCaching.ts, 8, 86)) +>labelOffset : Symbol(labelOffset, Decl(controlFlowCaching.ts, 8, 100)) +>labelAlign : Symbol(labelAlign, Decl(controlFlowCaching.ts, 8, 113)) + + labelGap = this.chart.theme.axis.tick.labelGap, +>labelGap : Symbol(labelGap, Decl(controlFlowCaching.ts, 8, 125)) + + taFont = o.font || (ta.majorTick && ta.majorTick.font) || (ta.tick && ta.tick.font), +>taFont : Symbol(taFont, Decl(controlFlowCaching.ts, 9, 55)) +>o : Symbol(o, Decl(controlFlowCaching.ts, 6, 7)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) + + taTitleFont = o.titleFont || (ta.title && ta.title.font), +>taTitleFont : Symbol(taTitleFont, Decl(controlFlowCaching.ts, 10, 92)) +>o : Symbol(o, Decl(controlFlowCaching.ts, 6, 7)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) + + taFontColor = o.fontColor || (ta.majorTick && ta.majorTick.fontColor) || (ta.tick && ta.tick.fontColor) || "black", +>taFontColor : Symbol(taFontColor, Decl(controlFlowCaching.ts, 11, 65)) +>o : Symbol(o, Decl(controlFlowCaching.ts, 6, 7)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) + + taTitleFontColor = o.titleFontColor || (ta.title && ta.title.fontColor) || "black", +>taTitleFontColor : Symbol(taTitleFontColor, Decl(controlFlowCaching.ts, 12, 123)) +>o : Symbol(o, Decl(controlFlowCaching.ts, 6, 7)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) + + taTitleGap = (o.titleGap == 0) ? 0 : o.titleGap || (ta.title && ta.title.gap) || 15, +>taTitleGap : Symbol(taTitleGap, Decl(controlFlowCaching.ts, 13, 91)) +>o : Symbol(o, Decl(controlFlowCaching.ts, 6, 7)) +>o : Symbol(o, Decl(controlFlowCaching.ts, 6, 7)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) + + taTitleOrientation = o.titleOrientation || (ta.title && ta.title.orientation) || "axis", +>taTitleOrientation : Symbol(taTitleOrientation, Decl(controlFlowCaching.ts, 14, 92)) +>o : Symbol(o, Decl(controlFlowCaching.ts, 6, 7)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) + + taMajorTick = this.chart.theme.getTick("major", o), +>taMajorTick : Symbol(taMajorTick, Decl(controlFlowCaching.ts, 15, 96)) +>o : Symbol(o, Decl(controlFlowCaching.ts, 6, 7)) + + taMinorTick = this.chart.theme.getTick("minor", o), +>taMinorTick : Symbol(taMinorTick, Decl(controlFlowCaching.ts, 16, 59)) +>o : Symbol(o, Decl(controlFlowCaching.ts, 6, 7)) + + taMicroTick = this.chart.theme.getTick("micro", o), +>taMicroTick : Symbol(taMicroTick, Decl(controlFlowCaching.ts, 17, 59)) +>o : Symbol(o, Decl(controlFlowCaching.ts, 6, 7)) + + taStroke = "stroke" in o ? o.stroke : ta.stroke, +>taStroke : Symbol(taStroke, Decl(controlFlowCaching.ts, 18, 59)) +>o : Symbol(o, Decl(controlFlowCaching.ts, 6, 7)) +>o : Symbol(o, Decl(controlFlowCaching.ts, 6, 7)) +>ta : Symbol(ta, Decl(controlFlowCaching.ts, 6, 21)) + + size = taFont ? g.normalizedLength(g.splitFontString(taFont).size) : 0, +>size : Symbol(size, Decl(controlFlowCaching.ts, 20, 56)) +>taFont : Symbol(taFont, Decl(controlFlowCaching.ts, 9, 55)) +>g : Symbol(g, Decl(controlFlowCaching.ts, 3, 57)) +>g : Symbol(g, Decl(controlFlowCaching.ts, 3, 57)) +>taFont : Symbol(taFont, Decl(controlFlowCaching.ts, 9, 55)) + + cosr = Math.abs(Math.cos(rotation * Math.PI / 180)), +>cosr : Symbol(cosr, Decl(controlFlowCaching.ts, 21, 79)) +>Math.abs : Symbol(Math.abs, Decl(lib.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>abs : Symbol(Math.abs, Decl(lib.d.ts, --, --)) +>Math.cos : Symbol(Math.cos, Decl(lib.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>cos : Symbol(Math.cos, Decl(lib.d.ts, --, --)) +>rotation : Symbol(rotation, Decl(controlFlowCaching.ts, 7, 47)) +>Math.PI : Symbol(Math.PI, Decl(lib.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>PI : Symbol(Math.PI, Decl(lib.d.ts, --, --)) + + sinr = Math.abs(Math.sin(rotation * Math.PI / 180)), +>sinr : Symbol(sinr, Decl(controlFlowCaching.ts, 22, 60)) +>Math.abs : Symbol(Math.abs, Decl(lib.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>abs : Symbol(Math.abs, Decl(lib.d.ts, --, --)) +>Math.sin : Symbol(Math.sin, Decl(lib.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>sin : Symbol(Math.sin, Decl(lib.d.ts, --, --)) +>rotation : Symbol(rotation, Decl(controlFlowCaching.ts, 7, 47)) +>Math.PI : Symbol(Math.PI, Decl(lib.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>PI : Symbol(Math.PI, Decl(lib.d.ts, --, --)) + + tsize = taTitleFont ? g.normalizedLength(g.splitFontString(taTitleFont).size) : 0; +>tsize : Symbol(tsize, Decl(controlFlowCaching.ts, 23, 60)) +>taTitleFont : Symbol(taTitleFont, Decl(controlFlowCaching.ts, 10, 92)) +>g : Symbol(g, Decl(controlFlowCaching.ts, 3, 57)) +>g : Symbol(g, Decl(controlFlowCaching.ts, 3, 57)) +>taTitleFont : Symbol(taTitleFont, Decl(controlFlowCaching.ts, 10, 92)) + + if (rotation < 0) { +>rotation : Symbol(rotation, Decl(controlFlowCaching.ts, 7, 47)) + + rotation += 360; +>rotation : Symbol(rotation, Decl(controlFlowCaching.ts, 7, 47)) + } + var cachedLabelW = this._getMaxLabelSize(); +>cachedLabelW : Symbol(cachedLabelW, Decl(controlFlowCaching.ts, 28, 7)) + + cachedLabelW = cachedLabelW && cachedLabelW.majLabelW; +>cachedLabelW : Symbol(cachedLabelW, Decl(controlFlowCaching.ts, 28, 7)) +>cachedLabelW : Symbol(cachedLabelW, Decl(controlFlowCaching.ts, 28, 7)) +>cachedLabelW : Symbol(cachedLabelW, Decl(controlFlowCaching.ts, 28, 7)) + + titleOffset = size * cosr + (cachedLabelW || 0) * sinr + labelGap + Math.max(taMajorTick.length > 0 ? taMajorTick.length : 0, +>titleOffset : Symbol(titleOffset, Decl(controlFlowCaching.ts, 8, 49)) +>size : Symbol(size, Decl(controlFlowCaching.ts, 20, 56)) +>cosr : Symbol(cosr, Decl(controlFlowCaching.ts, 21, 79)) +>cachedLabelW : Symbol(cachedLabelW, Decl(controlFlowCaching.ts, 28, 7)) +>sinr : Symbol(sinr, Decl(controlFlowCaching.ts, 22, 60)) +>labelGap : Symbol(labelGap, Decl(controlFlowCaching.ts, 8, 125)) +>Math.max : Symbol(Math.max, Decl(lib.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>max : Symbol(Math.max, Decl(lib.d.ts, --, --)) +>taMajorTick : Symbol(taMajorTick, Decl(controlFlowCaching.ts, 15, 96)) +>taMajorTick : Symbol(taMajorTick, Decl(controlFlowCaching.ts, 15, 96)) + + taMinorTick.length > 0 ? taMinorTick.length : 0) + +>taMinorTick : Symbol(taMinorTick, Decl(controlFlowCaching.ts, 16, 59)) +>taMinorTick : Symbol(taMinorTick, Decl(controlFlowCaching.ts, 16, 59)) + + tsize + taTitleGap; +>tsize : Symbol(tsize, Decl(controlFlowCaching.ts, 23, 60)) +>taTitleGap : Symbol(taTitleGap, Decl(controlFlowCaching.ts, 13, 91)) + + axisVector = { x: isRtl ? -1 : 1, y: 0 }; // chart mirroring +>axisVector : Symbol(axisVector, Decl(controlFlowCaching.ts, 8, 62)) +>x : Symbol(x, Decl(controlFlowCaching.ts, 33, 18)) +>isRtl : Symbol(isRtl, Decl(controlFlowCaching.ts, 4, 7)) +>y : Symbol(y, Decl(controlFlowCaching.ts, 33, 37)) + + switch (rotation) { +>rotation : Symbol(rotation, Decl(controlFlowCaching.ts, 7, 47)) + + default: + if (rotation < (90 - centerAnchorLimit)) { +>rotation : Symbol(rotation, Decl(controlFlowCaching.ts, 7, 47)) +>centerAnchorLimit : Symbol(centerAnchorLimit, Decl(controlFlowCaching.ts, 3, 38)) + + labelOffset.y = leftBottom ? size : 0; +>labelOffset : Symbol(labelOffset, Decl(controlFlowCaching.ts, 8, 100)) +>leftBottom : Symbol(leftBottom, Decl(controlFlowCaching.ts, 6, 72)) +>size : Symbol(size, Decl(controlFlowCaching.ts, 20, 56)) + + } else if (rotation < (90 + centerAnchorLimit)) { +>rotation : Symbol(rotation, Decl(controlFlowCaching.ts, 7, 47)) +>centerAnchorLimit : Symbol(centerAnchorLimit, Decl(controlFlowCaching.ts, 3, 38)) + + labelOffset.x = -size * 0.4; +>labelOffset : Symbol(labelOffset, Decl(controlFlowCaching.ts, 8, 100)) +>size : Symbol(size, Decl(controlFlowCaching.ts, 20, 56)) + + } else if (rotation < 180) { +>rotation : Symbol(rotation, Decl(controlFlowCaching.ts, 7, 47)) + + labelOffset.y = leftBottom ? 0 : -size; +>labelOffset : Symbol(labelOffset, Decl(controlFlowCaching.ts, 8, 100)) +>leftBottom : Symbol(leftBottom, Decl(controlFlowCaching.ts, 6, 72)) +>size : Symbol(size, Decl(controlFlowCaching.ts, 20, 56)) + + } else if (rotation < (270 - centerAnchorLimit)) { +>rotation : Symbol(rotation, Decl(controlFlowCaching.ts, 7, 47)) +>centerAnchorLimit : Symbol(centerAnchorLimit, Decl(controlFlowCaching.ts, 3, 38)) + + labelOffset.y = leftBottom ? 0 : -size; +>labelOffset : Symbol(labelOffset, Decl(controlFlowCaching.ts, 8, 100)) +>leftBottom : Symbol(leftBottom, Decl(controlFlowCaching.ts, 6, 72)) +>size : Symbol(size, Decl(controlFlowCaching.ts, 20, 56)) + + } else if (rotation < (270 + centerAnchorLimit)) { +>rotation : Symbol(rotation, Decl(controlFlowCaching.ts, 7, 47)) +>centerAnchorLimit : Symbol(centerAnchorLimit, Decl(controlFlowCaching.ts, 3, 38)) + + labelOffset.y = leftBottom ? size * 0.4 : 0; +>labelOffset : Symbol(labelOffset, Decl(controlFlowCaching.ts, 8, 100)) +>leftBottom : Symbol(leftBottom, Decl(controlFlowCaching.ts, 6, 72)) +>size : Symbol(size, Decl(controlFlowCaching.ts, 20, 56)) + + } else { + labelOffset.y = leftBottom ? size : 0; +>labelOffset : Symbol(labelOffset, Decl(controlFlowCaching.ts, 8, 100)) +>leftBottom : Symbol(leftBottom, Decl(controlFlowCaching.ts, 6, 72)) +>size : Symbol(size, Decl(controlFlowCaching.ts, 20, 56)) + } + } + + titleRotation = (taTitleOrientation && taTitleOrientation == "away") ? 180 : 0; +>titleRotation : Symbol(titleRotation, Decl(controlFlowCaching.ts, 8, 30)) +>taTitleOrientation : Symbol(taTitleOrientation, Decl(controlFlowCaching.ts, 14, 92)) +>taTitleOrientation : Symbol(taTitleOrientation, Decl(controlFlowCaching.ts, 14, 92)) + + titlePos.y = offsets.t - titleOffset + (titleRotation ? 0 : tsize); +>titlePos : Symbol(titlePos, Decl(controlFlowCaching.ts, 8, 20)) +>offsets : Symbol(offsets, Decl(controlFlowCaching.ts, 3, 15)) +>titleOffset : Symbol(titleOffset, Decl(controlFlowCaching.ts, 8, 49)) +>titleRotation : Symbol(titleRotation, Decl(controlFlowCaching.ts, 8, 30)) +>tsize : Symbol(tsize, Decl(controlFlowCaching.ts, 23, 60)) + + switch (labelAlign) { +>labelAlign : Symbol(labelAlign, Decl(controlFlowCaching.ts, 8, 113)) + + case "start": + labelAlign = "end"; +>labelAlign : Symbol(labelAlign, Decl(controlFlowCaching.ts, 8, 113)) + + break; + case "end": + labelAlign = "start"; +>labelAlign : Symbol(labelAlign, Decl(controlFlowCaching.ts, 8, 113)) + + break; + case "middle": + labelOffset.y -= size; +>labelOffset : Symbol(labelOffset, Decl(controlFlowCaching.ts, 8, 100)) +>size : Symbol(size, Decl(controlFlowCaching.ts, 20, 56)) + + break; + } + + let _ = rotation; +>_ : Symbol(_, Decl(controlFlowCaching.ts, 65, 7)) +>rotation : Symbol(rotation, Decl(controlFlowCaching.ts, 7, 47)) +} + diff --git a/tests/baselines/reference/controlFlowCaching.types b/tests/baselines/reference/controlFlowCaching.types new file mode 100644 index 00000000000..cd603b274d1 --- /dev/null +++ b/tests/baselines/reference/controlFlowCaching.types @@ -0,0 +1,607 @@ +=== tests/cases/compiler/controlFlowCaching.ts === + +// Repro for #8401 + +function f(dim, offsets, arr, acommon, centerAnchorLimit, g, has, lin) { +>f : (dim: any, offsets: any, arr: any, acommon: any, centerAnchorLimit: any, g: any, has: any, lin: any) => void +>dim : any +>offsets : any +>arr : any +>acommon : any +>centerAnchorLimit : any +>g : any +>has : any +>lin : any + + var isRtl = this._isRtl(); // chart mirroring +>isRtl : any +>this._isRtl() : any +>this._isRtl : any +>this : any +>_isRtl : any + + // prepare variable + var o = this.opt, ta = this.chart.theme.axis, position = o.position, +>o : any +>this.opt : any +>this : any +>opt : any +>ta : any +>this.chart.theme.axis : any +>this.chart.theme : any +>this.chart : any +>this : any +>chart : any +>theme : any +>axis : any +>position : any +>o.position : any +>o : any +>position : any + + leftBottom = position !== "rightOrTop", rotation = o.rotation % 360, +>leftBottom : boolean +>position !== "rightOrTop" : boolean +>position : any +>"rightOrTop" : string +>rotation : number +>o.rotation % 360 : number +>o.rotation : any +>o : any +>rotation : any +>360 : number + + start, stop, titlePos, titleRotation = 0, titleOffset, axisVector, tickVector, anchorOffset, labelOffset, labelAlign, +>start : any +>stop : any +>titlePos : any +>titleRotation : number +>0 : number +>titleOffset : any +>axisVector : any +>tickVector : any +>anchorOffset : any +>labelOffset : any +>labelAlign : any + + labelGap = this.chart.theme.axis.tick.labelGap, +>labelGap : any +>this.chart.theme.axis.tick.labelGap : any +>this.chart.theme.axis.tick : any +>this.chart.theme.axis : any +>this.chart.theme : any +>this.chart : any +>this : any +>chart : any +>theme : any +>axis : any +>tick : any +>labelGap : any + + taFont = o.font || (ta.majorTick && ta.majorTick.font) || (ta.tick && ta.tick.font), +>taFont : any +>o.font || (ta.majorTick && ta.majorTick.font) || (ta.tick && ta.tick.font) : any +>o.font || (ta.majorTick && ta.majorTick.font) : any +>o.font : any +>o : any +>font : any +>(ta.majorTick && ta.majorTick.font) : any +>ta.majorTick && ta.majorTick.font : any +>ta.majorTick : any +>ta : any +>majorTick : any +>ta.majorTick.font : any +>ta.majorTick : any +>ta : any +>majorTick : any +>font : any +>(ta.tick && ta.tick.font) : any +>ta.tick && ta.tick.font : any +>ta.tick : any +>ta : any +>tick : any +>ta.tick.font : any +>ta.tick : any +>ta : any +>tick : any +>font : any + + taTitleFont = o.titleFont || (ta.title && ta.title.font), +>taTitleFont : any +>o.titleFont || (ta.title && ta.title.font) : any +>o.titleFont : any +>o : any +>titleFont : any +>(ta.title && ta.title.font) : any +>ta.title && ta.title.font : any +>ta.title : any +>ta : any +>title : any +>ta.title.font : any +>ta.title : any +>ta : any +>title : any +>font : any + + taFontColor = o.fontColor || (ta.majorTick && ta.majorTick.fontColor) || (ta.tick && ta.tick.fontColor) || "black", +>taFontColor : any +>o.fontColor || (ta.majorTick && ta.majorTick.fontColor) || (ta.tick && ta.tick.fontColor) || "black" : any +>o.fontColor || (ta.majorTick && ta.majorTick.fontColor) || (ta.tick && ta.tick.fontColor) : any +>o.fontColor || (ta.majorTick && ta.majorTick.fontColor) : any +>o.fontColor : any +>o : any +>fontColor : any +>(ta.majorTick && ta.majorTick.fontColor) : any +>ta.majorTick && ta.majorTick.fontColor : any +>ta.majorTick : any +>ta : any +>majorTick : any +>ta.majorTick.fontColor : any +>ta.majorTick : any +>ta : any +>majorTick : any +>fontColor : any +>(ta.tick && ta.tick.fontColor) : any +>ta.tick && ta.tick.fontColor : any +>ta.tick : any +>ta : any +>tick : any +>ta.tick.fontColor : any +>ta.tick : any +>ta : any +>tick : any +>fontColor : any +>"black" : string + + taTitleFontColor = o.titleFontColor || (ta.title && ta.title.fontColor) || "black", +>taTitleFontColor : any +>o.titleFontColor || (ta.title && ta.title.fontColor) || "black" : any +>o.titleFontColor || (ta.title && ta.title.fontColor) : any +>o.titleFontColor : any +>o : any +>titleFontColor : any +>(ta.title && ta.title.fontColor) : any +>ta.title && ta.title.fontColor : any +>ta.title : any +>ta : any +>title : any +>ta.title.fontColor : any +>ta.title : any +>ta : any +>title : any +>fontColor : any +>"black" : string + + taTitleGap = (o.titleGap == 0) ? 0 : o.titleGap || (ta.title && ta.title.gap) || 15, +>taTitleGap : any +>(o.titleGap == 0) ? 0 : o.titleGap || (ta.title && ta.title.gap) || 15 : any +>(o.titleGap == 0) : boolean +>o.titleGap == 0 : boolean +>o.titleGap : any +>o : any +>titleGap : any +>0 : number +>0 : number +>o.titleGap || (ta.title && ta.title.gap) || 15 : any +>o.titleGap || (ta.title && ta.title.gap) : any +>o.titleGap : any +>o : any +>titleGap : any +>(ta.title && ta.title.gap) : any +>ta.title && ta.title.gap : any +>ta.title : any +>ta : any +>title : any +>ta.title.gap : any +>ta.title : any +>ta : any +>title : any +>gap : any +>15 : number + + taTitleOrientation = o.titleOrientation || (ta.title && ta.title.orientation) || "axis", +>taTitleOrientation : any +>o.titleOrientation || (ta.title && ta.title.orientation) || "axis" : any +>o.titleOrientation || (ta.title && ta.title.orientation) : any +>o.titleOrientation : any +>o : any +>titleOrientation : any +>(ta.title && ta.title.orientation) : any +>ta.title && ta.title.orientation : any +>ta.title : any +>ta : any +>title : any +>ta.title.orientation : any +>ta.title : any +>ta : any +>title : any +>orientation : any +>"axis" : string + + taMajorTick = this.chart.theme.getTick("major", o), +>taMajorTick : any +>this.chart.theme.getTick("major", o) : any +>this.chart.theme.getTick : any +>this.chart.theme : any +>this.chart : any +>this : any +>chart : any +>theme : any +>getTick : any +>"major" : string +>o : any + + taMinorTick = this.chart.theme.getTick("minor", o), +>taMinorTick : any +>this.chart.theme.getTick("minor", o) : any +>this.chart.theme.getTick : any +>this.chart.theme : any +>this.chart : any +>this : any +>chart : any +>theme : any +>getTick : any +>"minor" : string +>o : any + + taMicroTick = this.chart.theme.getTick("micro", o), +>taMicroTick : any +>this.chart.theme.getTick("micro", o) : any +>this.chart.theme.getTick : any +>this.chart.theme : any +>this.chart : any +>this : any +>chart : any +>theme : any +>getTick : any +>"micro" : string +>o : any + + taStroke = "stroke" in o ? o.stroke : ta.stroke, +>taStroke : any +>"stroke" in o ? o.stroke : ta.stroke : any +>"stroke" in o : boolean +>"stroke" : string +>o : any +>o.stroke : any +>o : any +>stroke : any +>ta.stroke : any +>ta : any +>stroke : any + + size = taFont ? g.normalizedLength(g.splitFontString(taFont).size) : 0, +>size : any +>taFont ? g.normalizedLength(g.splitFontString(taFont).size) : 0 : any +>taFont : any +>g.normalizedLength(g.splitFontString(taFont).size) : any +>g.normalizedLength : any +>g : any +>normalizedLength : any +>g.splitFontString(taFont).size : any +>g.splitFontString(taFont) : any +>g.splitFontString : any +>g : any +>splitFontString : any +>taFont : any +>size : any +>0 : number + + cosr = Math.abs(Math.cos(rotation * Math.PI / 180)), +>cosr : number +>Math.abs(Math.cos(rotation * Math.PI / 180)) : number +>Math.abs : (x: number) => number +>Math : Math +>abs : (x: number) => number +>Math.cos(rotation * Math.PI / 180) : number +>Math.cos : (x: number) => number +>Math : Math +>cos : (x: number) => number +>rotation * Math.PI / 180 : number +>rotation * Math.PI : number +>rotation : number +>Math.PI : number +>Math : Math +>PI : number +>180 : number + + sinr = Math.abs(Math.sin(rotation * Math.PI / 180)), +>sinr : number +>Math.abs(Math.sin(rotation * Math.PI / 180)) : number +>Math.abs : (x: number) => number +>Math : Math +>abs : (x: number) => number +>Math.sin(rotation * Math.PI / 180) : number +>Math.sin : (x: number) => number +>Math : Math +>sin : (x: number) => number +>rotation * Math.PI / 180 : number +>rotation * Math.PI : number +>rotation : number +>Math.PI : number +>Math : Math +>PI : number +>180 : number + + tsize = taTitleFont ? g.normalizedLength(g.splitFontString(taTitleFont).size) : 0; +>tsize : any +>taTitleFont ? g.normalizedLength(g.splitFontString(taTitleFont).size) : 0 : any +>taTitleFont : any +>g.normalizedLength(g.splitFontString(taTitleFont).size) : any +>g.normalizedLength : any +>g : any +>normalizedLength : any +>g.splitFontString(taTitleFont).size : any +>g.splitFontString(taTitleFont) : any +>g.splitFontString : any +>g : any +>splitFontString : any +>taTitleFont : any +>size : any +>0 : number + + if (rotation < 0) { +>rotation < 0 : boolean +>rotation : number +>0 : number + + rotation += 360; +>rotation += 360 : number +>rotation : number +>360 : number + } + var cachedLabelW = this._getMaxLabelSize(); +>cachedLabelW : any +>this._getMaxLabelSize() : any +>this._getMaxLabelSize : any +>this : any +>_getMaxLabelSize : any + + cachedLabelW = cachedLabelW && cachedLabelW.majLabelW; +>cachedLabelW = cachedLabelW && cachedLabelW.majLabelW : any +>cachedLabelW : any +>cachedLabelW && cachedLabelW.majLabelW : any +>cachedLabelW : any +>cachedLabelW.majLabelW : any +>cachedLabelW : any +>majLabelW : any + + titleOffset = size * cosr + (cachedLabelW || 0) * sinr + labelGap + Math.max(taMajorTick.length > 0 ? taMajorTick.length : 0, +>titleOffset = size * cosr + (cachedLabelW || 0) * sinr + labelGap + Math.max(taMajorTick.length > 0 ? taMajorTick.length : 0, taMinorTick.length > 0 ? taMinorTick.length : 0) + tsize + taTitleGap : any +>titleOffset : any +>size * cosr + (cachedLabelW || 0) * sinr + labelGap + Math.max(taMajorTick.length > 0 ? taMajorTick.length : 0, taMinorTick.length > 0 ? taMinorTick.length : 0) + tsize + taTitleGap : any +>size * cosr + (cachedLabelW || 0) * sinr + labelGap + Math.max(taMajorTick.length > 0 ? taMajorTick.length : 0, taMinorTick.length > 0 ? taMinorTick.length : 0) + tsize : any +>size * cosr + (cachedLabelW || 0) * sinr + labelGap + Math.max(taMajorTick.length > 0 ? taMajorTick.length : 0, taMinorTick.length > 0 ? taMinorTick.length : 0) : any +>size * cosr + (cachedLabelW || 0) * sinr + labelGap : any +>size * cosr + (cachedLabelW || 0) * sinr : number +>size * cosr : number +>size : any +>cosr : number +>(cachedLabelW || 0) * sinr : number +>(cachedLabelW || 0) : any +>cachedLabelW || 0 : any +>cachedLabelW : any +>0 : number +>sinr : number +>labelGap : any +>Math.max(taMajorTick.length > 0 ? taMajorTick.length : 0, taMinorTick.length > 0 ? taMinorTick.length : 0) : number +>Math.max : (...values: number[]) => number +>Math : Math +>max : (...values: number[]) => number +>taMajorTick.length > 0 ? taMajorTick.length : 0 : any +>taMajorTick.length > 0 : boolean +>taMajorTick.length : any +>taMajorTick : any +>length : any +>0 : number +>taMajorTick.length : any +>taMajorTick : any +>length : any +>0 : number + + taMinorTick.length > 0 ? taMinorTick.length : 0) + +>taMinorTick.length > 0 ? taMinorTick.length : 0 : any +>taMinorTick.length > 0 : boolean +>taMinorTick.length : any +>taMinorTick : any +>length : any +>0 : number +>taMinorTick.length : any +>taMinorTick : any +>length : any +>0 : number + + tsize + taTitleGap; +>tsize : any +>taTitleGap : any + + axisVector = { x: isRtl ? -1 : 1, y: 0 }; // chart mirroring +>axisVector = { x: isRtl ? -1 : 1, y: 0 } : { x: number; y: number; } +>axisVector : any +>{ x: isRtl ? -1 : 1, y: 0 } : { x: number; y: number; } +>x : number +>isRtl ? -1 : 1 : number +>isRtl : any +>-1 : number +>1 : number +>1 : number +>y : number +>0 : number + + switch (rotation) { +>rotation : number + + default: + if (rotation < (90 - centerAnchorLimit)) { +>rotation < (90 - centerAnchorLimit) : boolean +>rotation : number +>(90 - centerAnchorLimit) : number +>90 - centerAnchorLimit : number +>90 : number +>centerAnchorLimit : any + + labelOffset.y = leftBottom ? size : 0; +>labelOffset.y = leftBottom ? size : 0 : any +>labelOffset.y : any +>labelOffset : any +>y : any +>leftBottom ? size : 0 : any +>leftBottom : boolean +>size : any +>0 : number + + } else if (rotation < (90 + centerAnchorLimit)) { +>rotation < (90 + centerAnchorLimit) : boolean +>rotation : number +>(90 + centerAnchorLimit) : any +>90 + centerAnchorLimit : any +>90 : number +>centerAnchorLimit : any + + labelOffset.x = -size * 0.4; +>labelOffset.x = -size * 0.4 : number +>labelOffset.x : any +>labelOffset : any +>x : any +>-size * 0.4 : number +>-size : number +>size : any +>0.4 : number + + } else if (rotation < 180) { +>rotation < 180 : boolean +>rotation : number +>180 : number + + labelOffset.y = leftBottom ? 0 : -size; +>labelOffset.y = leftBottom ? 0 : -size : number +>labelOffset.y : any +>labelOffset : any +>y : any +>leftBottom ? 0 : -size : number +>leftBottom : boolean +>0 : number +>-size : number +>size : any + + } else if (rotation < (270 - centerAnchorLimit)) { +>rotation < (270 - centerAnchorLimit) : boolean +>rotation : number +>(270 - centerAnchorLimit) : number +>270 - centerAnchorLimit : number +>270 : number +>centerAnchorLimit : any + + labelOffset.y = leftBottom ? 0 : -size; +>labelOffset.y = leftBottom ? 0 : -size : number +>labelOffset.y : any +>labelOffset : any +>y : any +>leftBottom ? 0 : -size : number +>leftBottom : boolean +>0 : number +>-size : number +>size : any + + } else if (rotation < (270 + centerAnchorLimit)) { +>rotation < (270 + centerAnchorLimit) : boolean +>rotation : number +>(270 + centerAnchorLimit) : any +>270 + centerAnchorLimit : any +>270 : number +>centerAnchorLimit : any + + labelOffset.y = leftBottom ? size * 0.4 : 0; +>labelOffset.y = leftBottom ? size * 0.4 : 0 : number +>labelOffset.y : any +>labelOffset : any +>y : any +>leftBottom ? size * 0.4 : 0 : number +>leftBottom : boolean +>size * 0.4 : number +>size : any +>0.4 : number +>0 : number + + } else { + labelOffset.y = leftBottom ? size : 0; +>labelOffset.y = leftBottom ? size : 0 : any +>labelOffset.y : any +>labelOffset : any +>y : any +>leftBottom ? size : 0 : any +>leftBottom : boolean +>size : any +>0 : number + } + } + + titleRotation = (taTitleOrientation && taTitleOrientation == "away") ? 180 : 0; +>titleRotation = (taTitleOrientation && taTitleOrientation == "away") ? 180 : 0 : number +>titleRotation : number +>(taTitleOrientation && taTitleOrientation == "away") ? 180 : 0 : number +>(taTitleOrientation && taTitleOrientation == "away") : boolean +>taTitleOrientation && taTitleOrientation == "away" : boolean +>taTitleOrientation : any +>taTitleOrientation == "away" : boolean +>taTitleOrientation : any +>"away" : string +>180 : number +>0 : number + + titlePos.y = offsets.t - titleOffset + (titleRotation ? 0 : tsize); +>titlePos.y = offsets.t - titleOffset + (titleRotation ? 0 : tsize) : any +>titlePos.y : any +>titlePos : any +>y : any +>offsets.t - titleOffset + (titleRotation ? 0 : tsize) : any +>offsets.t - titleOffset : number +>offsets.t : any +>offsets : any +>t : any +>titleOffset : any +>(titleRotation ? 0 : tsize) : any +>titleRotation ? 0 : tsize : any +>titleRotation : number +>0 : number +>tsize : any + + switch (labelAlign) { +>labelAlign : any + + case "start": +>"start" : string + + labelAlign = "end"; +>labelAlign = "end" : string +>labelAlign : any +>"end" : string + + break; + case "end": +>"end" : string + + labelAlign = "start"; +>labelAlign = "start" : string +>labelAlign : any +>"start" : string + + break; + case "middle": +>"middle" : string + + labelOffset.y -= size; +>labelOffset.y -= size : number +>labelOffset.y : any +>labelOffset : any +>y : any +>size : any + + break; + } + + let _ = rotation; +>_ : number +>rotation : number +} + diff --git a/tests/cases/compiler/controlFlowCaching.ts b/tests/cases/compiler/controlFlowCaching.ts new file mode 100644 index 00000000000..6510a89b06e --- /dev/null +++ b/tests/cases/compiler/controlFlowCaching.ts @@ -0,0 +1,68 @@ +// @strictNullChecks: true + +// Repro for #8401 + +function f(dim, offsets, arr, acommon, centerAnchorLimit, g, has, lin) { + var isRtl = this._isRtl(); // chart mirroring + // prepare variable + var o = this.opt, ta = this.chart.theme.axis, position = o.position, + leftBottom = position !== "rightOrTop", rotation = o.rotation % 360, + start, stop, titlePos, titleRotation = 0, titleOffset, axisVector, tickVector, anchorOffset, labelOffset, labelAlign, + labelGap = this.chart.theme.axis.tick.labelGap, + taFont = o.font || (ta.majorTick && ta.majorTick.font) || (ta.tick && ta.tick.font), + taTitleFont = o.titleFont || (ta.title && ta.title.font), + taFontColor = o.fontColor || (ta.majorTick && ta.majorTick.fontColor) || (ta.tick && ta.tick.fontColor) || "black", + taTitleFontColor = o.titleFontColor || (ta.title && ta.title.fontColor) || "black", + taTitleGap = (o.titleGap == 0) ? 0 : o.titleGap || (ta.title && ta.title.gap) || 15, + taTitleOrientation = o.titleOrientation || (ta.title && ta.title.orientation) || "axis", + taMajorTick = this.chart.theme.getTick("major", o), + taMinorTick = this.chart.theme.getTick("minor", o), + taMicroTick = this.chart.theme.getTick("micro", o), + + taStroke = "stroke" in o ? o.stroke : ta.stroke, + size = taFont ? g.normalizedLength(g.splitFontString(taFont).size) : 0, + cosr = Math.abs(Math.cos(rotation * Math.PI / 180)), + sinr = Math.abs(Math.sin(rotation * Math.PI / 180)), + tsize = taTitleFont ? g.normalizedLength(g.splitFontString(taTitleFont).size) : 0; + if (rotation < 0) { + rotation += 360; + } + var cachedLabelW = this._getMaxLabelSize(); + cachedLabelW = cachedLabelW && cachedLabelW.majLabelW; + titleOffset = size * cosr + (cachedLabelW || 0) * sinr + labelGap + Math.max(taMajorTick.length > 0 ? taMajorTick.length : 0, + taMinorTick.length > 0 ? taMinorTick.length : 0) + + tsize + taTitleGap; + axisVector = { x: isRtl ? -1 : 1, y: 0 }; // chart mirroring + switch (rotation) { + default: + if (rotation < (90 - centerAnchorLimit)) { + labelOffset.y = leftBottom ? size : 0; + } else if (rotation < (90 + centerAnchorLimit)) { + labelOffset.x = -size * 0.4; + } else if (rotation < 180) { + labelOffset.y = leftBottom ? 0 : -size; + } else if (rotation < (270 - centerAnchorLimit)) { + labelOffset.y = leftBottom ? 0 : -size; + } else if (rotation < (270 + centerAnchorLimit)) { + labelOffset.y = leftBottom ? size * 0.4 : 0; + } else { + labelOffset.y = leftBottom ? size : 0; + } + } + + titleRotation = (taTitleOrientation && taTitleOrientation == "away") ? 180 : 0; + titlePos.y = offsets.t - titleOffset + (titleRotation ? 0 : tsize); + switch (labelAlign) { + case "start": + labelAlign = "end"; + break; + case "end": + labelAlign = "start"; + break; + case "middle": + labelOffset.y -= size; + break; + } + + let _ = rotation; +}