Skip to content

Commit 50e2912

Browse files
authored
Merge pull request #15791 from Microsoft/fix13935
Adds CommaList to avoid large deeply nested comma expressions
2 parents 3e13fad + 22cf036 commit 50e2912

File tree

7 files changed

+89
-1
lines changed

7 files changed

+89
-1
lines changed

src/compiler/core.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,35 @@ namespace ts {
490490
return result;
491491
}
492492

493+
/**
494+
* Maps an array. If the mapped value is an array, it is spread into the result.
495+
* Avoids allocation if all elements map to themselves.
496+
*
497+
* @param array The array to map.
498+
* @param mapfn The callback used to map the result into one or more values.
499+
*/
500+
export function sameFlatMap<T>(array: T[], mapfn: (x: T, i: number) => T | T[]): T[] {
501+
let result: T[];
502+
if (array) {
503+
for (let i = 0; i < array.length; i++) {
504+
const item = array[i];
505+
const mapped = mapfn(item, i);
506+
if (result || item !== mapped || isArray(mapped)) {
507+
if (!result) {
508+
result = array.slice(0, i);
509+
}
510+
if (isArray(mapped)) {
511+
addRange(result, mapped);
512+
}
513+
else {
514+
result.push(mapped);
515+
}
516+
}
517+
}
518+
}
519+
return result || array;
520+
}
521+
493522
/**
494523
* Computes the first matching span of elements and returns a tuple of the first span
495524
* and the remaining elements.

src/compiler/emitter.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -733,6 +733,9 @@ namespace ts {
733733
// Transformation nodes
734734
case SyntaxKind.PartiallyEmittedExpression:
735735
return emitPartiallyEmittedExpression(<PartiallyEmittedExpression>node);
736+
737+
case SyntaxKind.CommaListExpression:
738+
return emitCommaList(<CommaListExpression>node);
736739
}
737740
}
738741

@@ -2101,6 +2104,10 @@ namespace ts {
21012104
emitExpression(node.expression);
21022105
}
21032106

2107+
function emitCommaList(node: CommaListExpression) {
2108+
emitExpressionList(node, node.elements, ListFormat.CommaListElements);
2109+
}
2110+
21042111
/**
21052112
* Emits any prologue directives at the start of a Statement list, returning the
21062113
* number of prologue directives written to the output.
@@ -2951,6 +2958,7 @@ namespace ts {
29512958
ArrayBindingPatternElements = SingleLine | AllowTrailingComma | CommaDelimited | SpaceBetweenSiblings,
29522959
ObjectLiteralExpressionProperties = PreserveLines | CommaDelimited | SpaceBetweenSiblings | SpaceBetweenBraces | Indented | Braces,
29532960
ArrayLiteralExpressionElements = PreserveLines | CommaDelimited | SpaceBetweenSiblings | AllowTrailingComma | Indented | SquareBrackets,
2961+
CommaListElements = CommaDelimited | SpaceBetweenSiblings | SingleLine,
29542962
CallExpressionArguments = CommaDelimited | SpaceBetweenSiblings | SingleLine | Parenthesis,
29552963
NewExpressionArguments = CommaDelimited | SpaceBetweenSiblings | SingleLine | Parenthesis | OptionalIfUndefined,
29562964
TemplateExpressionSpans = SingleLine | NoInterveningComments,

src/compiler/factory.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2077,6 +2077,30 @@ namespace ts {
20772077
return node;
20782078
}
20792079

2080+
function flattenCommaElements(node: Expression): Expression | Expression[] {
2081+
if (nodeIsSynthesized(node) && !isParseTreeNode(node) && !node.original && !node.emitNode && !node.id) {
2082+
if (node.kind === SyntaxKind.CommaListExpression) {
2083+
return (<CommaListExpression>node).elements;
2084+
}
2085+
if (isBinaryExpression(node) && node.operatorToken.kind === SyntaxKind.CommaToken) {
2086+
return [node.left, node.right];
2087+
}
2088+
}
2089+
return node;
2090+
}
2091+
2092+
export function createCommaList(elements: Expression[]) {
2093+
const node = <CommaListExpression>createSynthesizedNode(SyntaxKind.CommaListExpression);
2094+
node.elements = createNodeArray(sameFlatMap(elements, flattenCommaElements));
2095+
return node;
2096+
}
2097+
2098+
export function updateCommaList(node: CommaListExpression, elements: Expression[]) {
2099+
return node.elements !== elements
2100+
? updateNode(createCommaList(elements), node)
2101+
: node;
2102+
}
2103+
20802104
export function createBundle(sourceFiles: SourceFile[]) {
20812105
const node = <Bundle>createNode(SyntaxKind.Bundle);
20822106
node.sourceFiles = sourceFiles;
@@ -2865,7 +2889,11 @@ namespace ts {
28652889
}
28662890

28672891
export function inlineExpressions(expressions: Expression[]) {
2868-
return reduceLeft(expressions, createComma);
2892+
// Avoid deeply nested comma expressions as traversing them during emit can result in "Maximum call
2893+
// stack size exceeded" errors.
2894+
return expressions.length > 10
2895+
? createCommaList(expressions)
2896+
: reduceLeft(expressions, createComma);
28692897
}
28702898

28712899
export function createExpressionFromEntityName(node: EntityName | Expression): Expression {

src/compiler/parser.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,8 @@ namespace ts {
362362
return visitNode(cbNode, (<ExternalModuleReference>node).expression);
363363
case SyntaxKind.MissingDeclaration:
364364
return visitNodes(cbNodes, node.decorators);
365+
case SyntaxKind.CommaListExpression:
366+
return visitNodes(cbNodes, (<CommaListExpression>node).elements);
365367

366368
case SyntaxKind.JsxElement:
367369
return visitNode(cbNode, (<JsxElement>node).openingElement) ||

src/compiler/types.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,7 @@ namespace ts {
389389
// Transformation nodes
390390
NotEmittedStatement,
391391
PartiallyEmittedExpression,
392+
CommaListExpression,
392393
MergeDeclarationMarker,
393394
EndOfDeclarationMarker,
394395

@@ -1603,6 +1604,14 @@ namespace ts {
16031604
kind: SyntaxKind.EndOfDeclarationMarker;
16041605
}
16051606

1607+
/**
1608+
* A list of comma-seperated expressions. This node is only created by transformations.
1609+
*/
1610+
export interface CommaListExpression extends Expression {
1611+
kind: SyntaxKind.CommaListExpression;
1612+
elements: NodeArray<Expression>;
1613+
}
1614+
16061615
/**
16071616
* Marks the beginning of a merged transformed declaration.
16081617
*/

src/compiler/utilities.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2327,6 +2327,9 @@ namespace ts {
23272327
case SyntaxKind.SpreadElement:
23282328
return 1;
23292329

2330+
case SyntaxKind.CommaListExpression:
2331+
return 0;
2332+
23302333
default:
23312334
return -1;
23322335
}
@@ -3915,6 +3918,7 @@ namespace ts {
39153918
|| kind === SyntaxKind.SpreadElement
39163919
|| kind === SyntaxKind.AsExpression
39173920
|| kind === SyntaxKind.OmittedExpression
3921+
|| kind === SyntaxKind.CommaListExpression
39183922
|| isUnaryExpressionKind(kind);
39193923
}
39203924

src/compiler/visitor.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,10 @@ namespace ts {
876876
return updatePartiallyEmittedExpression(<PartiallyEmittedExpression>node,
877877
visitNode((<PartiallyEmittedExpression>node).expression, visitor, isExpression));
878878

879+
case SyntaxKind.CommaListExpression:
880+
return updateCommaList(<CommaListExpression>node,
881+
nodesVisitor((<CommaListExpression>node).elements, visitor, isExpression));
882+
879883
default:
880884
// No need to visit nodes with no children.
881885
return node;
@@ -1389,6 +1393,10 @@ namespace ts {
13891393
result = reduceNode((<PartiallyEmittedExpression>node).expression, cbNode, result);
13901394
break;
13911395

1396+
case SyntaxKind.CommaListExpression:
1397+
result = reduceNodes((<CommaListExpression>node).elements, cbNodes, result);
1398+
break;
1399+
13921400
default:
13931401
break;
13941402
}

0 commit comments

Comments
 (0)