Merge pull request #8471 from Microsoft/controlFlowCaching

Improved control flow caching
This commit is contained in:
Anders Hejlsberg
2016-05-04 17:29:33 -07:00
11 changed files with 1218 additions and 89 deletions
+71 -46
View File
@@ -129,8 +129,8 @@ namespace ts {
let Symbol: { new (flags: SymbolFlags, name: string): Symbol };
let classifiableNames: Map<string>;
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((<FunctionLikeDeclaration>node).body)) {
if (!(currentFlow.flags & FlowFlags.Unreachable) && isFunctionLikeKind(kind) && nodeIsPresent((<FunctionLikeDeclaration>node).body)) {
flags |= NodeFlags.HasImplicitReturn;
if (hasExplicitReturn) {
flags |= NodeFlags.HasExplicitReturn;
@@ -639,55 +639,80 @@ 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 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;
}
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);
setFlowNodeReferenced(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;
}
setFlowNodeReferenced(antecedent);
return <FlowCondition>{
kind: FlowKind.Condition,
flags,
antecedent,
expression,
assumeTrue
};
}
function createFlowAssignment(antecedent: FlowNode, node: Expression | VariableDeclaration | BindingElement): FlowNode {
setFlowNodeReferenced(antecedent);
return <FlowAssignment>{
kind: FlowKind.Assignment,
flags: FlowFlags.Assignment,
antecedent,
node
};
}
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 && (<FlowLabel>flow).antecedents.length === 2) {
const a = (<FlowLabel>flow).antecedents[0];
const b = (<FlowLabel>flow).antecedents[1];
if ((a.flags & FlowFlags.TrueCondition && b.flags & FlowFlags.FalseCondition ||
a.flags & FlowFlags.FalseCondition && b.flags & FlowFlags.TrueCondition) &&
(<FlowCondition>a).antecedent === (<FlowCondition>b).antecedent &&
(<FlowCondition>a).expression === (<FlowCondition>b).expression) {
return (<FlowCondition>a).antecedent;
}
}
return flow;
}
function finishFlowLabel(flow: FlowLabel): FlowNode {
const antecedents = flow.antecedents;
if (!antecedents) {
@@ -696,7 +721,7 @@ namespace ts {
if (antecedents.length === 1) {
return antecedents[0];
}
return flow;
return skipSimpleConditionalFlow(flow);
}
function isStatementCondition(node: Node) {
@@ -747,8 +772,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 +788,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 +801,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 +814,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 +829,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 +845,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 +900,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 +918,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 +940,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 +976,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 +1026,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 +1056,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 +1073,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 +2090,7 @@ namespace ts {
}
function checkUnreachable(node: Node): boolean {
if (currentFlow.kind !== FlowKind.Unreachable) {
if (!(currentFlow.flags & FlowFlags.Unreachable)) {
return false;
}
if (currentFlow === unreachableFlow) {
+51 -25
View File
@@ -183,6 +183,7 @@ namespace ts {
let flowStackStart = 0;
let flowStackCount = 0;
let visitedFlowCount = 0;
const tupleTypes: Map<TupleType> = {};
const unionTypes: Map<UnionType> = {};
@@ -199,6 +200,8 @@ namespace ts {
const flowTypeCaches: Map<Type>[] = [];
const flowStackNodes: FlowNode[] = [];
const flowStackCacheKeys: string[] = [];
const visitedFlowNodes: FlowNode[] = [];
const visitedFlowTypes: Type[] = [];
const potentialThisCollisions: Node[] = [];
const awaitedTypeStack: number[] = [];
@@ -7581,34 +7584,57 @@ namespace ts {
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) {
switch (flow.kind) {
case FlowKind.Assignment:
const type = getTypeAtFlowAssignment(<FlowAssignment>flow);
if (!type) {
flow = (<FlowAssignment>flow).antecedent;
continue;
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];
}
return type;
case FlowKind.Condition:
return getTypeAtFlowCondition(<FlowCondition>flow);
case FlowKind.Label:
case FlowKind.LoopLabel:
if ((<FlowLabel>flow).antecedents.length === 1) {
flow = (<FlowLabel>flow).antecedents[0];
continue;
}
return getTypeAtFlowLabel(<FlowLabel>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;
}
}
// At the top of the flow we have the initial type
return initialType;
let type: Type;
if (flow.flags & FlowFlags.Assignment) {
type = getTypeAtFlowAssignment(<FlowAssignment>flow);
if (!type) {
flow = (<FlowAssignment>flow).antecedent;
continue;
}
}
else if (flow.flags & FlowFlags.Condition) {
type = getTypeAtFlowCondition(<FlowCondition>flow);
}
else if (flow.flags & FlowFlags.Label) {
if ((<FlowLabel>flow).antecedents.length === 1) {
flow = (<FlowLabel>flow).antecedents[0];
continue;
}
type = getTypeAtFlowLabel(<FlowLabel>flow);
}
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.
type = declaredType;
}
else {
// At the top of the flow we have the initial type.
type = initialType;
}
if (flow.flags & FlowFlags.Shared) {
// Record visited node and the associated type in the cache.
visitedFlowNodes[visitedFlowCount] = flow;
visitedFlowTypes[visitedFlowCount] = type;
visitedFlowCount++;
}
return type;
}
}
@@ -7644,7 +7670,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 +7702,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) {
+13 -9
View File
@@ -1515,17 +1515,22 @@ namespace ts {
isBracketed: boolean;
}
export const enum FlowKind {
Unreachable,
Start,
Label,
LoopLabel,
Assignment,
Condition
export const enum FlowFlags {
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
}
export interface FlowNode {
kind: FlowKind; // Node kind
flags: FlowFlags;
id?: number; // Node id used by flow type cache in checker
}
@@ -1545,7 +1550,6 @@ namespace ts {
// node's location in the control flow.
export interface FlowCondition extends FlowNode {
expression: Expression;
assumeTrue: boolean;
antecedent: FlowNode;
}
@@ -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; }
@@ -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;
}
@@ -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))
}
@@ -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
}
@@ -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
}
@@ -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
@@ -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
@@ -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;
}