Skip to content

Commit 86091d7

Browse files
committed
Clean up and consolidate destructuring transform
1 parent 6453fde commit 86091d7

21 files changed

Lines changed: 572 additions & 426 deletions

src/compiler/binder.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,7 +1007,7 @@ namespace ts {
10071007
currentFlow = finishFlowLabel(preFinallyLabel);
10081008
bind(node.finallyBlock);
10091009
// if flow after finally is unreachable - keep it
1010-
// otherwise check if flows after try and after catch are unreachable
1010+
// otherwise check if flows after try and after catch are unreachable
10111011
// if yes - convert current flow to unreachable
10121012
// i.e.
10131013
// try { return "1" } finally { console.log(1); }
@@ -3077,7 +3077,7 @@ namespace ts {
30773077

30783078
case SyntaxKind.SpreadElementExpression:
30793079
// This node is ES6 syntax, but is handled by a containing node.
3080-
transformFlags |= TransformFlags.ContainsSpreadElementExpression;
3080+
transformFlags |= TransformFlags.AssertES2015 | TransformFlags.ContainsSpreadElementExpression;
30813081
break;
30823082

30833083
case SyntaxKind.SuperKeyword:

src/compiler/emitter.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2025,9 +2025,7 @@ namespace ts {
20252025
for (let i = 0; i < lines.length; i++) {
20262026
const line = indentation ? lines[i].slice(indentation) : lines[i];
20272027
if (line.length) {
2028-
if (i > 0) {
2029-
writeLine();
2030-
}
2028+
writeLine();
20312029
write(line);
20322030
}
20332031
}

src/compiler/factory.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1699,6 +1699,8 @@ namespace ts {
16991699
}
17001700
}
17011701

1702+
// Utilities
1703+
17021704
export interface CallBinding {
17031705
target: LeftHandSideExpression;
17041706
thisArg: Expression;
@@ -2733,7 +2735,9 @@ namespace ts {
27332735
*/
27342736
export function addEmitHelper<T extends Node>(node: T, helper: EmitHelper): T {
27352737
const emitNode = getOrCreateEmitNode(node);
2736-
emitNode.helpers = append(emitNode.helpers, helper);
2738+
if (!contains(emitNode.helpers, helper)) {
2739+
emitNode.helpers = append(emitNode.helpers, helper);
2740+
}
27372741
return node;
27382742
}
27392743

src/compiler/transformers/destructuring.ts

Lines changed: 428 additions & 313 deletions
Large diffs are not rendered by default.

src/compiler/transformers/es2015.ts

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/// <reference path="../factory.ts" />
22
/// <reference path="../visitor.ts" />
3+
/// <reference path="./destructuring.ts" />
34

45
/*@internal*/
56
namespace ts {
@@ -404,6 +405,9 @@ namespace ts {
404405
case SyntaxKind.YieldExpression:
405406
return visitYieldExpression(<YieldExpression>node);
406407

408+
case SyntaxKind.SpreadElementExpression:
409+
return visitSpreadElementExpression(<SpreadElementExpression>node);
410+
407411
case SyntaxKind.SuperKeyword:
408412
return visitSuperKeyword();
409413

@@ -1129,7 +1133,7 @@ namespace ts {
11291133
createVariableStatement(
11301134
/*modifiers*/ undefined,
11311135
createVariableDeclarationList(
1132-
flattenParameterDestructuring(parameter, temp, visitor)
1136+
flattenDestructuring(parameter, temp, /*recordTempVariable*/ undefined, visitor)
11331137
)
11341138
),
11351139
EmitFlags.CustomPrologue
@@ -1660,18 +1664,21 @@ namespace ts {
16601664
*/
16611665
function visitParenthesizedExpression(node: ParenthesizedExpression, needsDestructuringValue: boolean): ParenthesizedExpression {
16621666
// If we are here it is most likely because our expression is a destructuring assignment.
1663-
if (needsDestructuringValue) {
1667+
if (!needsDestructuringValue) {
1668+
// By default we always emit the RHS at the end of a flattened destructuring
1669+
// expression. If we are in a state where we do not need the destructuring value,
1670+
// we pass that information along to the children that care about it.
16641671
switch (node.expression.kind) {
16651672
case SyntaxKind.ParenthesizedExpression:
1666-
return createParen(
1667-
visitParenthesizedExpression(<ParenthesizedExpression>node.expression, /*needsDestructuringValue*/ true),
1668-
/*location*/ node
1673+
return updateParen(
1674+
node,
1675+
visitParenthesizedExpression(<ParenthesizedExpression>node.expression, /*needsDestructuringValue*/ false)
16691676
);
16701677

16711678
case SyntaxKind.BinaryExpression:
1672-
return createParen(
1673-
visitBinaryExpression(<BinaryExpression>node.expression, /*needsDestructuringValue*/ true),
1674-
/*location*/ node
1679+
return updateParen(
1680+
node,
1681+
visitBinaryExpression(<BinaryExpression>node.expression, /*needsDestructuringValue*/ false)
16751682
);
16761683
}
16771684
}
@@ -1689,7 +1696,13 @@ namespace ts {
16891696
function visitBinaryExpression(node: BinaryExpression, needsDestructuringValue: boolean): Expression {
16901697
// If we are here it is because this is a destructuring assignment.
16911698
Debug.assert(isDestructuringAssignment(node));
1692-
return flattenDestructuringAssignment(context, node, needsDestructuringValue, hoistVariableDeclaration, visitor);
1699+
return flattenDestructuringToExpression(
1700+
<DestructuringAssignment>node,
1701+
needsDestructuringValue,
1702+
createAssignment,
1703+
hoistVariableDeclaration,
1704+
visitor
1705+
);
16931706
}
16941707

16951708
function visitVariableStatement(node: VariableStatement): Statement {
@@ -1701,15 +1714,17 @@ namespace ts {
17011714
if (decl.initializer) {
17021715
let assignment: Expression;
17031716
if (isBindingPattern(decl.name)) {
1704-
assignment = flattenVariableDestructuringToExpression(decl, hoistVariableDeclaration, /*createAssignmentCallback*/ undefined, visitor);
1717+
assignment = flattenDestructuringToExpression(decl, /*needsValue*/ false, createAssignment, hoistVariableDeclaration, visitor);
17051718
}
17061719
else {
17071720
assignment = createBinary(<Identifier>decl.name, SyntaxKind.EqualsToken, visitNode(decl.initializer, visitor, isExpression));
17081721
}
1709-
(assignments || (assignments = [])).push(assignment);
1722+
1723+
assignments = append(assignments, assignment);
17101724
}
17111725
}
17121726
if (assignments) {
1727+
// TODO(rbuckton): use inlineExpressions.
17131728
return createStatement(reduceLeft(assignments, (acc, v) => createBinary(v, SyntaxKind.CommaToken, acc)), node);
17141729
}
17151730
else {
@@ -1854,8 +1869,11 @@ namespace ts {
18541869
if (isBindingPattern(node.name)) {
18551870
const recordTempVariablesInLine = !enclosingVariableStatement
18561871
|| !hasModifier(enclosingVariableStatement, ModifierFlags.Export);
1857-
return flattenVariableDestructuring(node, /*value*/ undefined, visitor,
1858-
recordTempVariablesInLine ? undefined : hoistVariableDeclaration);
1872+
return flattenDestructuring(
1873+
node,
1874+
/*value*/ undefined,
1875+
recordTempVariablesInLine ? undefined : hoistVariableDeclaration,
1876+
visitor);
18591877
}
18601878

18611879
return visitEachChild(node, visitor, context);
@@ -1956,9 +1974,10 @@ namespace ts {
19561974
if (firstOriginalDeclaration && isBindingPattern(firstOriginalDeclaration.name)) {
19571975
// This works whether the declaration is a var, let, or const.
19581976
// It will use rhsIterationValue _a[_i] as the initializer.
1959-
const declarations = flattenVariableDestructuring(
1977+
const declarations = flattenDestructuring(
19601978
firstOriginalDeclaration,
19611979
createElementAccess(rhsReference, counter),
1980+
/*recordTempVariable*/ undefined,
19621981
visitor
19631982
);
19641983

@@ -2007,10 +2026,10 @@ namespace ts {
20072026
// This is a destructuring pattern, so we flatten the destructuring instead.
20082027
statements.push(
20092028
createStatement(
2010-
flattenDestructuringAssignment(
2011-
context,
2029+
flattenDestructuringToExpression(
20122030
assignment,
20132031
/*needsValue*/ false,
2032+
createAssignment,
20142033
hoistVariableDeclaration,
20152034
visitor
20162035
)
@@ -2840,7 +2859,7 @@ namespace ts {
28402859
}
28412860

28422861
function visitSpanOfSpreadElements(chunk: Expression[]): VisitResult<Expression> {
2843-
return map(chunk, visitExpressionOfSpreadElement);
2862+
return map(chunk, visitSpreadElementExpression);
28442863
}
28452864

28462865
function visitSpanOfNonSpreadElements(chunk: Expression[], multiLine: boolean, hasTrailingComma: boolean): VisitResult<Expression> {
@@ -2856,7 +2875,7 @@ namespace ts {
28562875
*
28572876
* @param node A SpreadElementExpression node.
28582877
*/
2859-
function visitExpressionOfSpreadElement(node: SpreadElementExpression) {
2878+
function visitSpreadElementExpression(node: SpreadElementExpression) {
28602879
return visitNode(node.expression, visitor, isExpression);
28612880
}
28622881

src/compiler/transformers/module/module.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/// <reference path="../../factory.ts" />
22
/// <reference path="../../visitor.ts" />
3+
/// <reference path="../destructuring.ts" />
34

45
/*@internal*/
56
namespace ts {
@@ -45,6 +46,7 @@ namespace ts {
4546
let currentSourceFile: SourceFile; // The current file.
4647
let currentModuleInfo: ExternalModuleInfo; // The ExternalModuleInfo for the current file.
4748
let noSubstitution: Map<boolean>; // Set of nodes for which substitution rules should be ignored.
49+
let helperState: EmitHelperState;
4850

4951
return transformSourceFile;
5052

@@ -62,13 +64,15 @@ namespace ts {
6264

6365
currentSourceFile = node;
6466
currentModuleInfo = moduleInfoMap[getOriginalNodeId(node)] = collectExternalModuleInfo(node, resolver);
67+
helperState = { currentSourceFile, compilerOptions };
6568

6669
// Perform the transformation.
6770
const transformModule = transformModuleDelegates[moduleKind] || transformModuleDelegates[ModuleKind.None];
6871
const updated = transformModule(node);
6972

7073
currentSourceFile = undefined;
7174
currentModuleInfo = undefined;
75+
helperState = undefined;
7276
return aggregateTransformFlags(updated);
7377
}
7478

@@ -759,10 +763,12 @@ namespace ts {
759763
*/
760764
function transformInitializedVariable(node: VariableDeclaration): Expression {
761765
if (isBindingPattern(node.name)) {
762-
return flattenVariableDestructuringToExpression(
766+
return flattenDestructuringToExpression(
763767
node,
768+
/*needsValue*/ false,
769+
createExportExpression,
764770
hoistVariableDeclaration,
765-
createExportExpression
771+
/*visitor*/ undefined
766772
);
767773
}
768774
else {

src/compiler/transformers/module/system.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/// <reference path="../../factory.ts" />
22
/// <reference path="../../visitor.ts" />
3+
/// <reference path="../destructuring.ts" />
34

45
/*@internal*/
56
namespace ts {
@@ -40,6 +41,7 @@ namespace ts {
4041
let hoistedStatements: Statement[];
4142
let enclosingBlockScopedContainer: Node;
4243
let noSubstitution: Map<boolean>; // Set of nodes for which substitution rules should be ignored.
44+
let helperState: EmitHelperState;
4345

4446
return transformSourceFile;
4547

@@ -58,6 +60,7 @@ namespace ts {
5860
const id = getOriginalNodeId(node);
5961
currentSourceFile = node;
6062
enclosingBlockScopedContainer = node;
63+
helperState = { currentSourceFile, compilerOptions };
6164

6265
// System modules have the following shape:
6366
//
@@ -131,6 +134,7 @@ namespace ts {
131134
contextObject = undefined;
132135
hoistedStatements = undefined;
133136
enclosingBlockScopedContainer = undefined;
137+
helperState = undefined;
134138

135139
return aggregateTransformFlags(updated);
136140
}
@@ -818,7 +822,7 @@ namespace ts {
818822
function transformInitializedVariable(node: VariableDeclaration, isExportedDeclaration: boolean): Expression {
819823
const createAssignment = isExportedDeclaration ? createExportedVariableAssignment : createNonExportedVariableAssignment;
820824
return isBindingPattern(node.name)
821-
? flattenVariableDestructuringToExpression(node, hoistVariableDeclaration, createAssignment, destructuringVisitor)
825+
? flattenDestructuringToExpression(node, /*needsValue*/ false, createAssignment, hoistVariableDeclaration, destructuringVisitor)
822826
: createAssignment(node.name, visitNode(node.initializer, destructuringVisitor, isExpression));
823827
}
824828

@@ -1469,9 +1473,8 @@ namespace ts {
14691473
*/
14701474
function visitDestructuringAssignment(node: DestructuringAssignment): VisitResult<Expression> {
14711475
if (hasExportedReferenceInDestructuringTarget(node.left)) {
1472-
return flattenDestructuringAssignment(context, node, /*needsValue*/ true, hoistVariableDeclaration, destructuringVisitor);
1476+
return flattenDestructuringToExpression(node, /*needsValue*/ true, createAssignment, hoistVariableDeclaration, destructuringVisitor);
14731477
}
1474-
14751478
return visitEachChild(node, destructuringVisitor, context);
14761479
}
14771480

@@ -1481,7 +1484,7 @@ namespace ts {
14811484
* @param node The destructuring target.
14821485
*/
14831486
function hasExportedReferenceInDestructuringTarget(node: Expression | ObjectLiteralElementLike): boolean {
1484-
if (isAssignmentExpression(node)) {
1487+
if (isAssignmentExpression(node, /*excludeCompoundAssignment*/ true)) {
14851488
return hasExportedReferenceInDestructuringTarget(node.left);
14861489
}
14871490
else if (isSpreadElementExpression(node)) {

src/compiler/transformers/ts.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2312,12 +2312,7 @@ namespace ts {
23122312
function transformInitializedVariable(node: VariableDeclaration): Expression {
23132313
const name = node.name;
23142314
if (isBindingPattern(name)) {
2315-
return flattenVariableDestructuringToExpression(
2316-
node,
2317-
hoistVariableDeclaration,
2318-
createNamespaceExportExpression,
2319-
visitor
2320-
);
2315+
return flattenDestructuringToExpression(node, /*needsValue*/ false, createNamespaceExportExpression, hoistVariableDeclaration, visitor);
23212316
}
23222317
else {
23232318
return createAssignment(

src/compiler/types.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -635,17 +635,17 @@ namespace ts {
635635

636636
export interface ParameterDeclaration extends Declaration {
637637
kind: SyntaxKind.Parameter;
638-
dotDotDotToken?: DotDotDotToken; // Present on rest parameter
638+
dotDotDotToken?: DotDotDotToken; // Present on rest parameter
639639
name: BindingName; // Declared parameter name
640-
questionToken?: QuestionToken; // Present on optional parameter
640+
questionToken?: QuestionToken; // Present on optional parameter
641641
type?: TypeNode; // Optional type annotation
642642
initializer?: Expression; // Optional initializer
643643
}
644644

645645
export interface BindingElement extends Declaration {
646646
kind: SyntaxKind.BindingElement;
647647
propertyName?: PropertyName; // Binding property name (in object binding pattern)
648-
dotDotDotToken?: DotDotDotToken; // Present on rest binding element
648+
dotDotDotToken?: DotDotDotToken; // Present on rest binding element
649649
name: BindingName; // Declared binding element name
650650
initializer?: Expression; // Optional initializer
651651
}
@@ -671,7 +671,12 @@ namespace ts {
671671
name?: PropertyName;
672672
}
673673

674-
export type ObjectLiteralElementLike = PropertyAssignment | ShorthandPropertyAssignment | MethodDeclaration | AccessorDeclaration;
674+
export type ObjectLiteralElementLike
675+
= PropertyAssignment
676+
| ShorthandPropertyAssignment
677+
| MethodDeclaration
678+
| AccessorDeclaration
679+
;
675680

676681
export interface PropertyAssignment extends ObjectLiteralElement {
677682
kind: SyntaxKind.PropertyAssignment;
@@ -712,6 +717,7 @@ namespace ts {
712717
}
713718

714719
export interface BindingPattern extends Node {
720+
kind: SyntaxKind.ObjectBindingPattern | SyntaxKind.ArrayBindingPattern;
715721
elements: NodeArray<BindingElement | ArrayBindingElement>;
716722
}
717723

@@ -1157,16 +1163,16 @@ namespace ts {
11571163
right: Expression;
11581164
}
11591165

1160-
export interface AssignmentExpression extends BinaryExpression {
1166+
export interface AssignmentExpression<TKind extends AssignmentOperator> extends BinaryExpression {
11611167
left: LeftHandSideExpression;
1162-
operatorToken: Token<SyntaxKind.EqualsToken>;
1168+
operatorToken: Token<TKind>;
11631169
}
11641170

1165-
export interface ObjectDestructuringAssignment extends AssignmentExpression {
1171+
export interface ObjectDestructuringAssignment extends AssignmentExpression<SyntaxKind.EqualsToken> {
11661172
left: ObjectLiteralExpression;
11671173
}
11681174

1169-
export interface ArrayDestructuringAssignment extends AssignmentExpression {
1175+
export interface ArrayDestructuringAssignment extends AssignmentExpression<SyntaxKind.EqualsToken> {
11701176
left: ArrayLiteralExpression;
11711177
}
11721178

0 commit comments

Comments
 (0)