diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 3e65b547d5b..9a477f05211 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -570,6 +570,31 @@ namespace ts { } } + function bindEach(nodes: NodeArray) { + if (nodes === undefined) { + return; + } + + if (skipTransformFlagAggregation) { + forEach(nodes, bind); + } + else { + const savedSubtreeTransformFlags = subtreeTransformFlags; + subtreeTransformFlags = TransformFlags.None; + let nodeArrayFlags = TransformFlags.None; + for (const node of nodes) { + bind(node); + nodeArrayFlags |= node.transformFlags & ~TransformFlags.HasComputedFlags; + } + nodes.transformFlags = nodeArrayFlags | TransformFlags.HasComputedFlags; + subtreeTransformFlags |= savedSubtreeTransformFlags; + } + } + + function bindEachChild(node: Node) { + forEachChild(node, bind, bindEach); + } + function bindChildrenWorker(node: Node): void { // Binding of JsDocComment should be done before the current block scope container changes. // because the scope of JsDocComment should not be affected by whether the current node is a @@ -578,7 +603,7 @@ namespace ts { forEach(node.jsDocComments, bind); } if (checkUnreachable(node)) { - forEachChild(node, bind); + bindEachChild(node); return; } switch (node.kind) { @@ -643,7 +668,7 @@ namespace ts { bindCallExpressionFlow(node); break; default: - forEachChild(node, bind); + bindEachChild(node); break; } } @@ -976,7 +1001,7 @@ namespace ts { return undefined; } - function bindbreakOrContinueFlow(node: BreakOrContinueStatement, breakTarget: FlowLabel, continueTarget: FlowLabel) { + function bindBreakOrContinueFlow(node: BreakOrContinueStatement, breakTarget: FlowLabel, continueTarget: FlowLabel) { const flowLabel = node.kind === SyntaxKind.BreakStatement ? breakTarget : continueTarget; if (flowLabel) { addAntecedent(flowLabel, currentFlow); @@ -990,11 +1015,11 @@ namespace ts { const activeLabel = findActiveLabel(node.label.text); if (activeLabel) { activeLabel.referenced = true; - bindbreakOrContinueFlow(node, activeLabel.breakTarget, activeLabel.continueTarget); + bindBreakOrContinueFlow(node, activeLabel.breakTarget, activeLabel.continueTarget); } } else { - bindbreakOrContinueFlow(node, currentBreakTarget, currentContinueTarget); + bindBreakOrContinueFlow(node, currentBreakTarget, currentContinueTarget); } } @@ -1062,6 +1087,8 @@ namespace ts { } function bindCaseBlock(node: CaseBlock): void { + const savedSubtreeTransformFlags = subtreeTransformFlags; + subtreeTransformFlags = 0; const clauses = node.clauses; let fallthroughFlow = unreachableFlow; for (let i = 0; i < clauses.length; i++) { @@ -1081,6 +1108,8 @@ namespace ts { errorOnFirstToken(clause, Diagnostics.Fallthrough_case_in_switch); } } + clauses.transformFlags = subtreeTransformFlags | TransformFlags.HasComputedFlags; + subtreeTransformFlags |= savedSubtreeTransformFlags; } function bindCaseClause(node: CaseClause): void { @@ -1088,7 +1117,7 @@ namespace ts { currentFlow = preSwitchCaseFlow; bind(node.expression); currentFlow = saveCurrentFlow; - forEach(node.statements, bind); + bindEach(node.statements); } function pushActiveLabel(name: string, breakTarget: FlowLabel, continueTarget: FlowLabel): ActiveLabel { @@ -1180,12 +1209,12 @@ namespace ts { const saveTrueTarget = currentTrueTarget; currentTrueTarget = currentFalseTarget; currentFalseTarget = saveTrueTarget; - forEachChild(node, bind); + bindEachChild(node); currentFalseTarget = currentTrueTarget; currentTrueTarget = saveTrueTarget; } else { - forEachChild(node, bind); + bindEachChild(node); if (node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken) { bindAssignmentTargetFlow(node.operand); } @@ -1193,7 +1222,7 @@ namespace ts { } function bindPostfixUnaryExpressionFlow(node: PostfixUnaryExpression) { - forEachChild(node, bind); + bindEachChild(node); if (node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken) { bindAssignmentTargetFlow(node.operand); } @@ -1212,7 +1241,7 @@ namespace ts { } } else { - forEachChild(node, bind); + bindEachChild(node); if (isAssignmentOperator(operator) && !isAssignmentTarget(node)) { bindAssignmentTargetFlow(node.left); if (operator === SyntaxKind.EqualsToken && node.left.kind === SyntaxKind.ElementAccessExpression) { @@ -1226,7 +1255,7 @@ namespace ts { } function bindDeleteExpressionFlow(node: DeleteExpression) { - forEachChild(node, bind); + bindEachChild(node); if (node.expression.kind === SyntaxKind.PropertyAccessExpression) { bindAssignmentTargetFlow(node.expression); } @@ -1261,7 +1290,7 @@ namespace ts { } function bindVariableDeclarationFlow(node: VariableDeclaration) { - forEachChild(node, bind); + bindEachChild(node); if (node.initializer || node.parent.parent.kind === SyntaxKind.ForInStatement || node.parent.parent.kind === SyntaxKind.ForOfStatement) { bindInitializedVariableFlow(node); } @@ -1276,12 +1305,12 @@ namespace ts { expr = (expr).expression; } if (expr.kind === SyntaxKind.FunctionExpression || expr.kind === SyntaxKind.ArrowFunction) { - forEach(node.typeArguments, bind); - forEach(node.arguments, bind); + bindEach(node.typeArguments); + bindEach(node.arguments); bind(node.expression); } else { - forEachChild(node, bind); + bindEachChild(node); } if (node.expression.kind === SyntaxKind.PropertyAccessExpression) { const propertyAccess = node.expression; @@ -2514,7 +2543,7 @@ namespace ts { transformFlags |= TransformFlags.AssertTypeScript; } - if (subtreeFlags & TransformFlags.ContainsSpreadExpression + if (subtreeFlags & TransformFlags.ContainsSpread || isSuperOrSuperProperty(expression, expressionKind)) { // If the this node contains a SpreadExpression, or is a super call, then it is an ES6 // node. @@ -2545,7 +2574,7 @@ namespace ts { if (node.typeArguments) { transformFlags |= TransformFlags.AssertTypeScript; } - if (subtreeFlags & TransformFlags.ContainsSpreadExpression) { + if (subtreeFlags & TransformFlags.ContainsSpread) { // If the this node contains a SpreadElementExpression then it is an ES6 // node. transformFlags |= TransformFlags.AssertES2015; @@ -2554,7 +2583,6 @@ namespace ts { return transformFlags & ~TransformFlags.ArrayLiteralOrCallOrNewExcludes; } - function computeBinaryExpression(node: BinaryExpression, subtreeFlags: TransformFlags) { let transformFlags = subtreeFlags; const operatorTokenKind = node.operatorToken.kind; @@ -2601,7 +2629,7 @@ namespace ts { } // parameters with object rest destructuring are ES Next syntax - if (subtreeFlags & TransformFlags.ContainsSpreadExpression) { + if (subtreeFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest)) { transformFlags |= TransformFlags.AssertESNext; } @@ -2723,7 +2751,7 @@ namespace ts { } node.transformFlags = transformFlags | TransformFlags.HasComputedFlags; - return transformFlags & ~TransformFlags.NodeExcludes; + return transformFlags & ~TransformFlags.CatchClauseExcludes; } function computeExpressionWithTypeArguments(node: ExpressionWithTypeArguments, subtreeFlags: TransformFlags) { @@ -2839,7 +2867,7 @@ namespace ts { } // function declarations with object rest destructuring are ES Next syntax - if (subtreeFlags & TransformFlags.ContainsSpreadExpression) { + if (subtreeFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest)) { transformFlags |= TransformFlags.AssertESNext; } @@ -2881,7 +2909,7 @@ namespace ts { } // function expressions with object rest destructuring are ES Next syntax - if (subtreeFlags & TransformFlags.ContainsSpreadExpression) { + if (subtreeFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest)) { transformFlags |= TransformFlags.AssertESNext; } @@ -2924,7 +2952,7 @@ namespace ts { } // arrow functions with object rest destructuring are ES Next syntax - if (subtreeFlags & TransformFlags.ContainsSpreadExpression) { + if (subtreeFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest)) { transformFlags |= TransformFlags.AssertESNext; } @@ -3178,16 +3206,19 @@ namespace ts { break; case SyntaxKind.SpreadElement: + transformFlags |= TransformFlags.AssertES2015 | TransformFlags.ContainsSpread; + break; + case SyntaxKind.SpreadAssignment: - // This node is ES6 or ES next syntax, but is handled by a containing node. - transformFlags |= TransformFlags.ContainsSpreadExpression; + transformFlags |= TransformFlags.AssertESNext | TransformFlags.ContainsSpread | TransformFlags.ContainsObjectSpread; break; case SyntaxKind.BindingElement: - if ((node as BindingElement).dotDotDotToken) { - // this node is ES2015 or ES next syntax, but is handled by a containing node. - transformFlags |= TransformFlags.ContainsSpreadExpression; + transformFlags |= TransformFlags.AssertES2015; + if ((node).dotDotDotToken) { + transformFlags |= TransformFlags.ContainsRest; } + break; case SyntaxKind.SuperKeyword: // This node is ES6 syntax. @@ -3200,14 +3231,16 @@ namespace ts { break; case SyntaxKind.ObjectBindingPattern: + transformFlags |= TransformFlags.AssertES2015 | TransformFlags.ContainsBindingPattern; + if (subtreeFlags & TransformFlags.ContainsSpread) { + transformFlags |= TransformFlags.AssertESNext | TransformFlags.ContainsObjectRest; + } + excludeFlags = TransformFlags.BindingPatternExcludes; + break; + case SyntaxKind.ArrayBindingPattern: - // These nodes are ES2015 or ES Next syntax. - if (subtreeFlags & TransformFlags.ContainsSpreadExpression) { - transformFlags |= TransformFlags.AssertESNext | TransformFlags.ContainsBindingPattern; - } - else { - transformFlags |= TransformFlags.AssertES2015 | TransformFlags.ContainsBindingPattern; - } + transformFlags |= TransformFlags.AssertES2015 | TransformFlags.ContainsBindingPattern; + excludeFlags = TransformFlags.BindingPatternExcludes; break; case SyntaxKind.Decorator: @@ -3229,10 +3262,10 @@ namespace ts { transformFlags |= TransformFlags.ContainsLexicalThis; } - if (subtreeFlags & TransformFlags.ContainsSpreadExpression) { + if (subtreeFlags & TransformFlags.ContainsSpread) { // If an ObjectLiteralExpression contains a spread element, then it // is an ES next node. - transformFlags |= TransformFlags.AssertESNext; + transformFlags |= TransformFlags.AssertESNext | TransformFlags.ContainsObjectSpread; } break; @@ -3240,7 +3273,7 @@ namespace ts { case SyntaxKind.ArrayLiteralExpression: case SyntaxKind.NewExpression: excludeFlags = TransformFlags.ArrayLiteralOrCallOrNewExcludes; - if (subtreeFlags & TransformFlags.ContainsSpreadExpression) { + if (subtreeFlags & TransformFlags.ContainsSpread) { // If the this node contains a SpreadExpression, then it is an ES6 // node. transformFlags |= TransformFlags.AssertES2015; @@ -3333,6 +3366,11 @@ namespace ts { return TransformFlags.TypeExcludes; case SyntaxKind.ObjectLiteralExpression: return TransformFlags.ObjectLiteralExcludes; + case SyntaxKind.CatchClause: + return TransformFlags.CatchClauseExcludes; + case SyntaxKind.ObjectBindingPattern: + case SyntaxKind.ArrayBindingPattern: + return TransformFlags.BindingPatternExcludes; default: return TransformFlags.NodeExcludes; } diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 6674ffc7263..ac3d5ed6e29 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -2341,6 +2341,13 @@ namespace ts { // Utilities + export function convertToFunctionBody(node: ConciseBody) { + if (isBlock(node)) { + return node; + } + return createBlock([createReturn(node, node)], node); + } + function isUseStrictPrologue(node: ExpressionStatement): boolean { return (node.expression as StringLiteral).text === "use strict"; } @@ -3121,7 +3128,7 @@ namespace ts { } addCaptureThisForNodeIfNeeded(statements, node, enableSubstitutionsForCapturedThis); - addDefaultValueAssignmentsIfNeeded(statements, node, visitor, convertObjectRest); + addDefaultValueAssignmentsIfNeeded(context, statements, node, visitor, convertObjectRest); addRestParameterIfNeeded(statements, node, /*inConstructorWithSynthesizedSuper*/ false); // If we added any generated statements, this must be a multi-line block. @@ -3236,7 +3243,8 @@ namespace ts { * @param statements The statements for the new function body. * @param node A function-like node. */ - export function addDefaultValueAssignmentsIfNeeded(statements: Statement[], + export function addDefaultValueAssignmentsIfNeeded(context: TransformationContext, + statements: Statement[], node: FunctionLikeDeclaration, visitor: (node: Node) => VisitResult, convertObjectRest: boolean): void { @@ -3254,7 +3262,7 @@ namespace ts { } if (isBindingPattern(name)) { - addDefaultValueAssignmentForBindingPattern(statements, parameter, name, initializer, visitor, convertObjectRest); + addDefaultValueAssignmentForBindingPattern(context, statements, parameter, name, initializer, visitor, convertObjectRest); } else if (initializer) { addDefaultValueAssignmentForInitializer(statements, parameter, name, initializer, visitor); @@ -3270,7 +3278,8 @@ namespace ts { * @param name The name of the parameter. * @param initializer The initializer for the parameter. */ - function addDefaultValueAssignmentForBindingPattern(statements: Statement[], + function addDefaultValueAssignmentForBindingPattern(context: TransformationContext, + statements: Statement[], parameter: ParameterDeclaration, name: BindingPattern, initializer: Expression, visitor: (node: Node) => VisitResult, @@ -3286,7 +3295,17 @@ namespace ts { createVariableStatement( /*modifiers*/ undefined, createVariableDeclarationList( - flattenParameterDestructuring(parameter, temp, visitor, convertObjectRest) + flattenDestructuringToDeclarations( + context, + parameter, + temp, + /*skipInitializer*/ convertObjectRest, + /*recordTempVariablesInLine*/ true, + convertObjectRest + ? FlattenLevel.ObjectRest + : FlattenLevel.All, + visitor + ) ) ), EmitFlags.CustomPrologue @@ -3490,12 +3509,16 @@ namespace ts { if (firstOriginalDeclaration && isBindingPattern(firstOriginalDeclaration.name)) { // This works whether the declaration is a var, let, or const. // It will use rhsIterationValue _a[_i] as the initializer. - const declarations = flattenVariableDestructuring( + const declarations = flattenDestructuringToDeclarations( + context, firstOriginalDeclaration, elementAccess, - visitor, - /*recordTempVariable*/ undefined, + /*skipInitializer*/ false, + /*recordTempVariablesInLine*/ true, convertObjectRest + ? FlattenLevel.ObjectRest + : FlattenLevel.All, + visitor ); const declarationList = createVariableDeclarationList(declarations, /*location*/ initializer); @@ -3543,13 +3566,15 @@ namespace ts { // This is a destructuring pattern, so we flatten the destructuring instead. statements.push( createStatement( - flattenDestructuringAssignment( + flattenDestructuringToExpression( context, assignment, /*needsValue*/ false, - context.hoistVariableDeclaration, - visitor, convertObjectRest + ? FlattenLevel.ObjectRest + : FlattenLevel.All, + /*createAssignmentCallback*/ undefined, + visitor ) ) ); diff --git a/src/compiler/transformers/destructuring.ts b/src/compiler/transformers/destructuring.ts index b0cb3f9d239..d4886457062 100644 --- a/src/compiler/transformers/destructuring.ts +++ b/src/compiler/transformers/destructuring.ts @@ -3,685 +3,218 @@ /*@internal*/ namespace ts { - type EffectiveBindingElement = VariableDeclaration | ParameterDeclaration | BindingElement | ObjectLiteralElementLike | Expression; - type EffectiveObjectBindingPattern = ObjectBindingPattern | ObjectLiteralExpression; - type EffectiveArrayBindingPattern = ArrayBindingPattern | ArrayLiteralExpression; - type EffectiveBindingPattern = EffectiveObjectBindingPattern | EffectiveArrayBindingPattern; - type EffectiveBindingTarget = EffectiveBindingPattern | Expression; - type EffectiveRestIndicator = DotDotDotToken | SpreadElement | SpreadAssignment; - - export const enum FlattenLevel { - All, - ObjectRestDestructuringOnly, - } - - /** - * Flattens a destructuring assignment expression. - * - * @param root The destructuring assignment expression. - * @param needsValue Indicates whether the value from the right-hand-side of the - * destructuring assignment is needed as part of a larger expression. - * @param recordTempVariable A callback used to record new temporary variables. - * @param visitor An optional visitor to use to visit expressions. - */ - export function flattenDestructuringAssignment( - context: TransformationContext, - node: BinaryExpression, - needsValue: boolean, - recordTempVariable: (node: Identifier) => void, - visitor?: (node: Node) => VisitResult, - transformRest?: boolean): Expression { - - if (isEmptyObjectLiteralOrArrayLiteral(node.left)) { - const right = node.right; - if (isDestructuringAssignment(right)) { - return flattenDestructuringAssignment(context, right, needsValue, recordTempVariable, visitor); - } - else { - return node.right; - } - } - - let location: TextRange = node; - let value = node.right; - const expressions: Expression[] = []; - if (needsValue) { - // If the right-hand value of the destructuring assignment needs to be preserved (as - // is the case when the destructuring assignmen) is part of a larger expression), - // then we need to cache the right-hand value. - // - // The source map location for the assignment should point to the entire binary - // expression. - value = ensureIdentifierOld(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment, visitor); - } - else if (nodeIsSynthesized(node)) { - // Generally, the source map location for a destructuring assignment is the root - // expression. - // - // However, if the root expression is synthesized (as in the case - // of the initializer when transforming a ForOfStatement), then the source map - // location should point to the right-hand value of the expression. - location = value; - } - - flattenDestructuringOld(node, value, location, emitAssignment, emitTempVariableAssignment, recordTempVariable, emitRestAssignment, transformRest, visitor); - - if (needsValue) { - expressions.push(value); - } - - const expression = inlineExpressions(expressions); - aggregateTransformFlags(expression); - return expression; - - function emitAssignment(name: Identifier | ObjectLiteralExpression, value: Expression, location: TextRange) { - const expression = createAssignment(name, value, location); - - // NOTE: this completely disables source maps, but aligns with the behavior of - // `emitAssignment` in the old emitter. - setEmitFlags(expression, EmitFlags.NoNestedSourceMaps); - - aggregateTransformFlags(expression); - expressions.push(expression); - } - - function emitTempVariableAssignment(value: Expression, location: TextRange) { - const name = createTempVariable(recordTempVariable); - emitAssignment(name, value, location); - return name; - } - - function emitRestAssignment(elements: ObjectLiteralElementLike[], value: Expression, location: TextRange) { - emitAssignment(createObjectLiteral(elements), value, location); - } - } - - /** - * Flattens binding patterns in a parameter declaration. - * - * @param node The ParameterDeclaration to flatten. - * @param value The rhs value for the binding pattern. - * @param visitor An optional visitor to use to visit expressions. - */ - export function flattenParameterDestructuring( - node: ParameterDeclaration, - value: Expression, - visitor?: (node: Node) => VisitResult, - transformRest?: boolean) { - const declarations: VariableDeclaration[] = []; - - flattenDestructuringOld(node, value, node, emitAssignment, emitTempVariableAssignment, noop, emitRestAssignment, transformRest, visitor); - - return declarations; - - function emitAssignment(name: Identifier | BindingPattern, value: Expression, location: TextRange) { - const declaration = createVariableDeclaration(name, /*type*/ undefined, value, location); - - // NOTE: this completely disables source maps, but aligns with the behavior of - // `emitAssignment` in the old emitter. - setEmitFlags(declaration, EmitFlags.NoNestedSourceMaps); - - aggregateTransformFlags(declaration); - declarations.push(declaration); - } - - function emitTempVariableAssignment(value: Expression, location: TextRange) { - const name = createTempVariable(/*recordTempVariable*/ undefined); - emitAssignment(name, value, location); - return name; - } - - function emitRestAssignment(elements: BindingElement[], value: Expression, location: TextRange) { - emitAssignment(createObjectBindingPattern(elements), value, location); - } - } - - /** - * Flattens binding patterns in a variable declaration. - * - * @param node The VariableDeclaration to flatten. - * @param value An optional rhs value for the binding pattern. - * @param visitor An optional visitor to use to visit expressions. - */ - export function flattenVariableDestructuring( - node: VariableDeclaration, - value?: Expression, - visitor?: (node: Node) => VisitResult, - recordTempVariable?: (node: Identifier) => void, - transformRest?: boolean) { - const declarations: VariableDeclaration[] = []; - - let pendingAssignments: Expression[]; - flattenDestructuringOld(node, value, node, emitAssignment, emitTempVariableAssignment, recordTempVariable, emitRestAssignment, transformRest, visitor); - - return declarations; - - function emitAssignment(name: Identifier | BindingPattern, value: Expression, location: TextRange, original: Node) { - if (pendingAssignments) { - pendingAssignments.push(value); - value = inlineExpressions(pendingAssignments); - pendingAssignments = undefined; - } - - const declaration = createVariableDeclaration(name, /*type*/ undefined, value, location); - declaration.original = original; - - // NOTE: this completely disables source maps, but aligns with the behavior of - // `emitAssignment` in the old emitter. - setEmitFlags(declaration, EmitFlags.NoNestedSourceMaps); - - declarations.push(declaration); - aggregateTransformFlags(declaration); - } - - function emitTempVariableAssignment(value: Expression, location: TextRange) { - const name = createTempVariable(recordTempVariable); - if (recordTempVariable) { - const assignment = createAssignment(name, value, location); - if (pendingAssignments) { - pendingAssignments.push(assignment); - } - else { - pendingAssignments = [assignment]; - } - } - else { - emitAssignment(name, value, location, /*original*/ undefined); - } - return name; - } - - function emitRestAssignment(elements: BindingElement[], value: Expression, location: TextRange, original: Node) { - emitAssignment(createObjectBindingPattern(elements), value, location, original); - } - } - - /** - * Flattens binding patterns in a variable declaration and transforms them into an expression. - * - * @param node The VariableDeclaration to flatten. - * @param recordTempVariable A callback used to record new temporary variables. - * @param createAssignmentCallback An optional callback used to create assignment expressions - * for non-temporary variables. - * @param visitor An optional visitor to use to visit expressions. - */ - export function flattenVariableDestructuringToExpression( - node: VariableDeclaration, - recordTempVariable: (name: Identifier) => void, - createAssignmentCallback?: (name: Identifier, value: Expression, location?: TextRange) => Expression, - visitor?: (node: Node) => VisitResult) { - - const pendingAssignments: Expression[] = []; - - flattenDestructuringOld(node, /*value*/ undefined, node, emitAssignment, emitTempVariableAssignment, noop, emitRestAssignment, /*transformRest*/ false, visitor); - - const expression = inlineExpressions(pendingAssignments); - aggregateTransformFlags(expression); - return expression; - - function emitAssignment(name: Identifier | ObjectLiteralExpression, value: Expression, location: TextRange, original: Node) { - const expression = createAssignmentCallback - ? createAssignmentCallback(name.kind === SyntaxKind.Identifier ? name : emitTempVariableAssignment(name, location), - value, - location) - : createAssignment(name, value, location); - - emitPendingAssignment(expression, original); - } - - function emitTempVariableAssignment(value: Expression, location: TextRange) { - const name = createTempVariable(recordTempVariable); - emitPendingAssignment(createAssignment(name, value, location), /*original*/ undefined); - return name; - } - - function emitRestAssignment(elements: ObjectLiteralElementLike[], value: Expression, location: TextRange, original: Node) { - emitAssignment(createObjectLiteral(elements), value, location, original); - } - - function emitPendingAssignment(expression: Expression, original: Node) { - expression.original = original; - - // NOTE: this completely disables source maps, but aligns with the behavior of - // `emitAssignment` in the old emitter. - setEmitFlags(expression, EmitFlags.NoNestedSourceMaps); - - pendingAssignments.push(expression); - } - } - - function flattenDestructuringOld( - root: VariableDeclaration | ParameterDeclaration | BindingElement | BinaryExpression, - value: Expression, - location: TextRange, - emitAssignment: (name: Identifier, value: Expression, location: TextRange, original: Node) => void, - emitTempVariableAssignment: (value: Expression, location: TextRange) => Identifier, - recordTempVariable: (node: Identifier) => void, - emitRestAssignment: (elements: (ObjectLiteralElementLike[] | BindingElement[]), value: Expression, location: TextRange, original: Node) => void, - transformRest: boolean, - visitor?: (node: Node) => VisitResult) { - if (value && visitor) { - value = visitNode(value, visitor, isExpression); - } - - if (isBinaryExpression(root)) { - emitDestructuringAssignment(root.left, value, location); - } - else { - emitBindingElement(root, value); - } - - function emitDestructuringAssignment(bindingTarget: Expression | ShorthandPropertyAssignment, value: Expression, location: TextRange) { - // When emitting target = value use source map node to highlight, including any temporary assignments needed for this - let target: Expression; - if (isShorthandPropertyAssignment(bindingTarget)) { - const initializer = visitor - ? visitNode(bindingTarget.objectAssignmentInitializer, visitor, isExpression) - : bindingTarget.objectAssignmentInitializer; - - if (initializer) { - value = createDefaultValueCheck(value, initializer, location); - } - - target = bindingTarget.name; - } - else if (isBinaryExpression(bindingTarget) && bindingTarget.operatorToken.kind === SyntaxKind.EqualsToken) { - const initializer = visitor - ? visitNode(bindingTarget.right, visitor, isExpression) - : bindingTarget.right; - - value = createDefaultValueCheck(value, initializer, location); - target = bindingTarget.left; - } - else { - target = bindingTarget; - } - - if (target.kind === SyntaxKind.ObjectLiteralExpression) { - emitObjectLiteralAssignment(target, value, location); - } - else if (target.kind === SyntaxKind.ArrayLiteralExpression) { - emitArrayLiteralAssignment(target, value, location); - } - else { - const name = getMutableClone(target); - setSourceMapRange(name, target); - setCommentRange(name, target); - emitAssignment(name, value, location, /*original*/ undefined); - } - } - - function emitObjectLiteralAssignment(target: ObjectLiteralExpression, value: Expression, location: TextRange) { - const properties = target.properties; - if (properties.length !== 1) { - // For anything but a single element destructuring we need to generate a temporary - // to ensure value is evaluated exactly once. - // When doing so we want to hightlight the passed in source map node since thats the one needing this temp assignment - value = ensureIdentifierOld(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment); - } - - let bindingElements: ObjectLiteralElementLike[] = []; - for (let i = 0; i < properties.length; i++) { - const p = properties[i]; - if (p.kind === SyntaxKind.PropertyAssignment || p.kind === SyntaxKind.ShorthandPropertyAssignment) { - if (!transformRest || - p.transformFlags & TransformFlags.ContainsSpreadExpression || - (p.kind === SyntaxKind.PropertyAssignment && p.initializer.transformFlags & TransformFlags.ContainsSpreadExpression)) { - if (bindingElements.length) { - emitRestAssignment(bindingElements, value, location, target); - bindingElements = []; - } - const propName = (p).name; - const bindingTarget = p.kind === SyntaxKind.ShorthandPropertyAssignment ? p : (p).initializer || propName; - // Assignment for bindingTarget = value.propName should highlight whole property, hence use p as source map node - emitDestructuringAssignment(bindingTarget, createDestructuringPropertyAccess(value, propName), p); - } - else { - bindingElements.push(p); - } - } - else if (i === properties.length - 1 && p.kind === SyntaxKind.SpreadAssignment) { - Debug.assert((p as SpreadAssignment).expression.kind === SyntaxKind.Identifier); - if (bindingElements.length) { - emitRestAssignment(bindingElements, value, location, target); - bindingElements = []; - } - const propName = (p as SpreadAssignment).expression as Identifier; - const restCall = createRestCall(value, target.properties, p => p.name, target); - emitDestructuringAssignment(propName, restCall, p); - } - } - if (bindingElements.length) { - emitRestAssignment(bindingElements, value, location, target); - bindingElements = []; - } - } - - function emitArrayLiteralAssignment(target: ArrayLiteralExpression, value: Expression, location: TextRange) { - if (transformRest) { - emitESNextArrayLiteralAssignment(target, value, location); - } - else { - emitES2015ArrayLiteralAssignment(target, value, location); - } - } - - function emitESNextArrayLiteralAssignment(target: ArrayLiteralExpression, value: Expression, location: TextRange) { - const elements = target.elements; - const numElements = elements.length; - if (numElements !== 1) { - // For anything but a single element destructuring we need to generate a temporary - // to ensure value is evaluated exactly once. - // When doing so we want to highlight the passed-in source map node since thats the one needing this temp assignment - value = ensureIdentifierOld(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment); - } - - const expressions: Expression[] = []; - const spreadContainingExpressions: [Expression, Identifier][] = []; - for (let i = 0; i < numElements; i++) { - const e = elements[i]; - if (e.kind === SyntaxKind.OmittedExpression) { - continue; - } - if (e.transformFlags & TransformFlags.ContainsSpreadExpression && i < numElements - 1) { - const tmp = createTempVariable(recordTempVariable); - spreadContainingExpressions.push([e, tmp]); - expressions.push(tmp); - } - else { - expressions.push(e); - } - } - emitAssignment(updateArrayLiteral(target, expressions) as any as Identifier, value, undefined, undefined); - for (const [e, tmp] of spreadContainingExpressions) { - emitDestructuringAssignment(e, tmp, e); - } - } - - function emitES2015ArrayLiteralAssignment(target: ArrayLiteralExpression, value: Expression, location: TextRange) { - const elements = target.elements; - const numElements = elements.length; - if (numElements !== 1) { - // For anything but a single element destructuring we need to generate a temporary - // to ensure value is evaluated exactly once. - // When doing so we want to highlight the passed-in source map node since thats the one needing this temp assignment - value = ensureIdentifierOld(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment); - } - - for (let i = 0; i < numElements; i++) { - const e = elements[i]; - if (e.kind !== SyntaxKind.OmittedExpression) { - // Assignment for target = value.propName should highligh whole property, hence use e as source map node - if (e.kind !== SyntaxKind.SpreadElement) { - emitDestructuringAssignment(e, createElementAccess(value, createLiteral(i)), e); - } - else if (i === numElements - 1) { - emitDestructuringAssignment((e).expression, createArraySlice(value, i), e); - } - } - } - } - - /** Given value: o, propName: p, pattern: { a, b, ...p } from the original statement - * `{ a, b, ...p } = o`, create `p = __rest(o, ["a", "b"]);`*/ - function createRestCall(value: Expression, elements: T[], getPropertyName: (element: T) => PropertyName, location: TextRange): Expression { - const propertyNames: LiteralExpression[] = []; - for (let i = 0; i < elements.length - 1; i++) { - if (isOmittedExpression(elements[i])) { - continue; - } - const str = createSynthesizedNode(SyntaxKind.StringLiteral); - str.pos = location.pos; - str.end = location.end; - str.text = getTextOfPropertyName(getPropertyName(elements[i])); - propertyNames.push(str); - } - const args = createSynthesizedNodeArray([value, createArrayLiteral(propertyNames, location)]); - return createCall(createIdentifier("__rest"), undefined, args); - } - - function emitBindingElement(target: VariableDeclaration | ParameterDeclaration | BindingElement, value: Expression) { - // Any temporary assignments needed to emit target = value should point to target - const initializer = visitor ? visitNode(target.initializer, visitor, isExpression) : target.initializer; - if (transformRest) { - value = value || initializer; - } - else if (initializer) { - // Combine value and initializer - value = value ? createDefaultValueCheck(value, initializer, target) : initializer; - } - else if (!value) { - // Use 'void 0' in absence of value and initializer - value = createVoidZero(); - } - - const name = target.name; - if (!isBindingPattern(name)) { - emitAssignment(name, value, target, target); - } - else { - const numElements = name.elements.length; - if (numElements !== 1) { - // For anything other than a single-element destructuring we need to generate a temporary - // to ensure value is evaluated exactly once. Additionally, if we have zero elements - // we need to emit *something* to ensure that in case a 'var' keyword was already emitted, - // so in that case, we'll intentionally create that temporary. - value = ensureIdentifierOld(value, /*reuseIdentifierExpressions*/ numElements !== 0, target, emitTempVariableAssignment); - } - if (name.kind === SyntaxKind.ArrayBindingPattern) { - emitArrayBindingElement(name as ArrayBindingPattern, value); - } - else { - emitObjectBindingElement(target, value); - } - } - } - - function emitArrayBindingElement(name: ArrayBindingPattern, value: Expression) { - if (transformRest) { - emitESNextArrayBindingElement(name, value); - } - else { - emitES2015ArrayBindingElement(name, value); - } - } - - function emitES2015ArrayBindingElement(name: ArrayBindingPattern, value: Expression) { - const elements = name.elements; - const numElements = elements.length; - for (let i = 0; i < numElements; i++) { - const element = elements[i]; - if (isOmittedExpression(element)) { - continue; - } - if (!element.dotDotDotToken) { - // Rewrite element to a declaration that accesses array element at index i - emitBindingElement(element, createElementAccess(value, i)); - } - else if (i === numElements - 1) { - emitBindingElement(element, createArraySlice(value, i)); - } - } - } - - function emitESNextArrayBindingElement(name: ArrayBindingPattern, value: Expression) { - const elements = name.elements; - const numElements = elements.length; - const bindingElements: BindingElement[] = []; - const spreadContainingElements: BindingElement[] = []; - for (let i = 0; i < numElements; i++) { - const element = elements[i]; - if (isOmittedExpression(element)) { - continue; - } - if (element.transformFlags & TransformFlags.ContainsSpreadExpression && i < numElements - 1) { - spreadContainingElements.push(element); - bindingElements.push(createBindingElement(undefined, undefined, getGeneratedNameForNode(element), undefined, value)); - } - else { - bindingElements.push(element); - } - } - emitAssignment(updateArrayBindingPattern(name, bindingElements) as any as Identifier, value, undefined, undefined); - for (const element of spreadContainingElements) { - emitBindingElement(element, getGeneratedNameForNode(element)); - } - } - - function emitObjectBindingElement(target: VariableDeclaration | ParameterDeclaration | BindingElement, value: Expression) { - const name = target.name as BindingPattern; - const elements = name.elements; - const numElements = elements.length; - let bindingElements: BindingElement[] = []; - for (let i = 0; i < numElements; i++) { - const element = elements[i]; - if (isOmittedExpression(element)) { - continue; - } - if (i === numElements - 1 && element.dotDotDotToken) { - if (bindingElements.length) { - emitRestAssignment(bindingElements, value, target, target); - bindingElements = []; - } - const restCall = createRestCall(value, - name.elements, - element => (element as BindingElement).propertyName || (element as BindingElement).name, - name); - emitBindingElement(element, restCall); - } - else if (transformRest && !(element.transformFlags & TransformFlags.ContainsSpreadExpression)) { - // do not emit until we have a complete bundle of ES2015 syntax - bindingElements.push(element); - } - else { - if (bindingElements.length) { - emitRestAssignment(bindingElements, value, target, target); - bindingElements = []; - } - // Rewrite element to a declaration with an initializer that fetches property - const propName = element.propertyName || element.name; - emitBindingElement(element, createDestructuringPropertyAccess(value, propName)); - } - } - if (bindingElements.length) { - emitRestAssignment(bindingElements, value, target, target); - bindingElements = []; - } - } - - function createDefaultValueCheck(value: Expression, defaultValue: Expression, location: TextRange): Expression { - value = ensureIdentifierOld(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment); - return createConditional( - createStrictEquality(value, createVoidZero()), - createToken(SyntaxKind.QuestionToken), - defaultValue, - createToken(SyntaxKind.ColonToken), - value - ); - } - - /** - * Creates either a PropertyAccessExpression or an ElementAccessExpression for the - * right-hand side of a transformed destructuring assignment. - * - * @param expression The right-hand expression that is the source of the property. - * @param propertyName The destructuring property name. - */ - function createDestructuringPropertyAccess(expression: Expression, propertyName: PropertyName): LeftHandSideExpression { - if (isComputedPropertyName(propertyName)) { - return createElementAccess( - expression, - ensureIdentifierOld(propertyName.expression, /*reuseIdentifierExpressions*/ false, /*location*/ propertyName, emitTempVariableAssignment) - ); - } - else if (isLiteralExpression(propertyName)) { - const clone = getSynthesizedClone(propertyName); - clone.text = unescapeIdentifier(clone.text); - return createElementAccess(expression, clone); - } - else { - if (isGeneratedIdentifier(propertyName)) { - const clone = getSynthesizedClone(propertyName); - clone.text = unescapeIdentifier(clone.text); - return createPropertyAccess(expression, clone); - } - else { - return createPropertyAccess(expression, createIdentifier(unescapeIdentifier(propertyName.text))); - } - } - } - } - - /** - * Ensures that there exists a declared identifier whose value holds the given expression. - * This function is useful to ensure that the expression's value can be read from in subsequent expressions. - * Unless 'reuseIdentifierExpressions' is false, 'value' will be returned if it is just an identifier. - * - * @param value the expression whose value needs to be bound. - * @param reuseIdentifierExpressions true if identifier expressions can simply be returned; - * false if it is necessary to always emit an identifier. - * @param location The location to use for source maps and comments. - * @param emitTempVariableAssignment A callback used to emit a temporary variable. - * @param visitor An optional callback used to visit the value. - */ - function ensureIdentifierOld( - value: Expression, - reuseIdentifierExpressions: boolean, - location: TextRange, - emitTempVariableAssignment: (value: Expression, location: TextRange) => Identifier, - visitor?: (node: Node) => VisitResult) { - - if (isIdentifier(value) && reuseIdentifierExpressions) { - return value; - } - else { - if (visitor) { - value = visitNode(value, visitor, isExpression); - } - - return emitTempVariableAssignment(value, location); - } - } + type EffectiveBindingOrAssignmentElement = VariableDeclaration | ParameterDeclaration | BindingElement | ObjectLiteralElementLike | Expression; + type EffectiveObjectBindingOrAssignmentPattern = ObjectBindingPattern | ObjectLiteralExpression; + type EffectiveArrayBindingOrAssignmentPattern = ArrayBindingPattern | ArrayLiteralExpression; + type EffectiveBindingOrAssignmentPattern = EffectiveObjectBindingOrAssignmentPattern | EffectiveArrayBindingOrAssignmentPattern; + type EffectiveBindingOrAssignmentTarget = EffectiveBindingOrAssignmentPattern | Expression; + type EffectiveBindingOrAssignmentRestIndicator = DotDotDotToken | SpreadElement | SpreadAssignment; interface FlattenHost { context: TransformationContext; level: FlattenLevel; recordTempVariablesInLine: boolean; - emitAssignment: (target: EffectiveBindingTarget, value: Expression, location: TextRange, original: Node) => void; - emitArrayAssignment: (elements: EffectiveBindingElement[], value: Expression, location: TextRange, original: Node) => void; - emitObjectAssignment: (elements: EffectiveBindingElement[], value: Expression, location: TextRange, original: Node) => void; emitExpression: (value: Expression) => void; + emitBindingOrAssignment: (target: EffectiveBindingOrAssignmentTarget, value: Expression, location: TextRange, original: Node) => void; + createArrayBindingOrAssignmentPattern: (elements: EffectiveBindingOrAssignmentElement[]) => EffectiveArrayBindingOrAssignmentPattern; + createObjectBindingOrAssignmentPattern: (elements: EffectiveBindingOrAssignmentElement[]) => EffectiveObjectBindingOrAssignmentPattern; + createArrayBindingOrAssignmentElement: (node: Identifier) => EffectiveBindingOrAssignmentElement; + visitor?: (node: Node) => VisitResult; } + export const enum FlattenLevel { + ObjectRest, + All, + } + + export const enum FlattenOutput { + Expression, + Declarations + } + + export const enum FlattenOptions { + NeedsValue = 1 << 0, + SkipInitializer = 1 << 1, + RecordTempVariablesInLine = 1 << 2, + } + + export function flattenDestructuring( + output: FlattenOutput.Expression, + level: FlattenLevel, + node: VariableDeclaration | DestructuringAssignment, + visitor: (node: Node) => VisitResult, + context: TransformationContext, + options: FlattenOptions, + createAssignment?: (name: Identifier, value: Expression, location?: TextRange) => Expression): Expression; + export function flattenDestructuring( + output: FlattenOutput.Declarations, + level: FlattenLevel, + node: VariableDeclaration | ParameterDeclaration, + visitor: (node: Node) => VisitResult, + context: TransformationContext, + options: FlattenOptions, + boundValue?: Expression): VariableDeclaration[]; + export function flattenDestructuring( + _output: FlattenOutput, + _level: FlattenLevel, + _node: VariableDeclaration | ParameterDeclaration | DestructuringAssignment, + _visitor: (node: Node) => VisitResult, + _context: TransformationContext, + _options: FlattenOptions, + _valueOrCallback?: Expression | ((name: Identifier, value: Expression, location?: TextRange) => Expression)): Expression | VariableDeclaration[] { + // const flattenMode = flags & FlattenFlags.FlattenMask; + // const outputMode = flags & FlattenFlags.OutputMask; + // const options = flags & FlattenFlags.OptionsMask; + + + // if (outputMode === FlattenFlags.OutputExpressions) { + // return flattenDestructuringToExpression(context, node, !(options & FlattenFlags.NeedsValue), flattenMode, createAssignmentCallback, visitor); + // } + // else { + // return flattenDestructuringToDeclarations( + // context, + // node, + // boundValue) + // } + return; + } + + /** + * Flattens a DestructuringAssignment or a VariableDeclaration to an expression. + * + * @param context The transformation context + * @param node The node to flatten. + * @param needsValue Indicates whether the value from the right-hand-side of the + * destructuring assignment is needed as part of a larger expression. + * @param level Indicates the extent to which flattening should occur. + * @param createAssignmentCallback A callback used to create the assignment expression. + * @param visitor A visitor used to visit initializers. + */ + export function flattenDestructuringToExpression( + context: TransformationContext, + node: VariableDeclaration | DestructuringAssignment, + needsValue: boolean, + level: FlattenLevel, + createAssignmentCallback: (name: Identifier, value: Expression, location?: TextRange) => Expression, + visitor: (node: Node) => VisitResult): Expression { + + let location: TextRange = node; + let value: Expression; + if (isDestructuringAssignment(node)) { + value = node.right; + while (isEmptyObjectLiteralOrArrayLiteral(node.left)) { + if (isDestructuringAssignment(value)) { + location = node = value; + value = node.right; + } + else { + return value; + } + } + } + + const expressions: Expression[] = []; + const host: FlattenHost = { + context, + level, + recordTempVariablesInLine: false, + emitExpression, + emitBindingOrAssignment, + createArrayBindingOrAssignmentPattern: createEffectiveArrayAssignmentPattern, + createObjectBindingOrAssignmentPattern: createEffectiveObjectAssignmentPattern, + createArrayBindingOrAssignmentElement: createEffectiveAssignmentElement, + visitor + }; + + if (value) { + value = visitNode(value, visitor, isExpression); + if (needsValue) { + // If the right-hand value of the destructuring assignment needs to be preserved (as + // is the case when the destructuring assignment is part of a larger expression), + // then we need to cache the right-hand value. + // + // The source map location for the assignment should point to the entire binary + // expression. + value = ensureIdentifier(host, value, /*reuseIdentifierExpressions*/ true, location); + } + else if (nodeIsSynthesized(node)) { + // Generally, the source map location for a destructuring assignment is the root + // expression. + // + // However, if the root expression is synthesized (as in the case + // of the initializer when transforming a ForOfStatement), then the source map + // location should point to the right-hand value of the expression. + location = value; + } + } + + flattenEffectiveBindingElement(host, node, value, location, /*skipInitializer*/ isDestructuringAssignment(node)); + + if (value && needsValue) { + expressions.push(value); + } + + return aggregateTransformFlags(inlineExpressions(expressions)); + + function emitExpression(expression: Expression) { + // NOTE: this completely disables source maps, but aligns with the behavior of + // `emitAssignment` in the old emitter. + setEmitFlags(expression, EmitFlags.NoNestedSourceMaps); + aggregateTransformFlags(expression); + expressions.push(expression); + } + + function emitBindingOrAssignment(target: EffectiveBindingOrAssignmentTarget, value: Expression, location: TextRange, original: Node) { + Debug.assertNode(target, createAssignmentCallback ? isIdentifier : isExpression); + const expression = createAssignmentCallback + ? createAssignmentCallback(target, value, location) + : createAssignment(visitNode(target, visitor, isExpression), value, location); + expression.original = original; + emitExpression(expression); + } + } + + /** + * Flattens a VariableDeclaration or ParameterDeclaration to one or more variable declarations. + * + * @param context The transformation context + * @param node The node to flatten. + * @param boundValue The value bound to the declaration. + * @param recordTempVariablesInLine Indicates whether temporary variables should be recored in-line. + * @param level Indicates the extent to which flattening should occur. + */ export function flattenDestructuringToDeclarations( context: TransformationContext, node: VariableDeclaration | ParameterDeclaration, boundValue: Expression | undefined, + skipInitializer: boolean, recordTempVariablesInLine: boolean, - level: FlattenLevel): VisitResult { + level: FlattenLevel, + visitor: (node: Node) => VisitResult): VariableDeclaration[] { - const pendingDeclarations: { pendingExpressions?: Expression[], name: BindingName, value: Expression, location?: TextRange, original?: Node; }[] = []; let pendingExpressions: Expression[]; - let declaration: VariableDeclaration; - let declarations: VariableDeclaration[]; + const pendingDeclarations: { pendingExpressions?: Expression[], name: BindingName, value: Expression, location?: TextRange, original?: Node; }[] = []; + const declarations: VariableDeclaration[] = []; const host: FlattenHost = { context, level, recordTempVariablesInLine, emitExpression, - emitAssignment, - emitArrayAssignment, - emitObjectAssignment + emitBindingOrAssignment, + createArrayBindingOrAssignmentPattern: createEffectiveArrayBindingPattern, + createObjectBindingOrAssignmentPattern: createEffectiveObjectBindingPattern, + createArrayBindingOrAssignmentElement: createEffectiveBindingElement, + visitor }; - flattenEffectiveBindingElement(host, node, boundValue, node); + flattenEffectiveBindingElement(host, node, boundValue, node, skipInitializer); if (pendingExpressions) { const temp = createTempVariable(/*recordTempVariable*/ undefined); if (recordTempVariablesInLine) { const value = inlineExpressions(pendingExpressions); pendingExpressions = undefined; - emitAssignment(temp, value, /*location*/ undefined, /*original*/ undefined); + emitBindingOrAssignment(temp, value, /*location*/ undefined, /*original*/ undefined); } else { context.hoistVariableDeclaration(temp); @@ -706,51 +239,33 @@ namespace ts { setEmitFlags(variable, EmitFlags.NoNestedSourceMaps); } aggregateTransformFlags(variable); - if (!declaration) { - declaration = variable; - } - else if (!declarations) { - declarations = [declaration, variable]; - } - else { - declarations.push(variable); - } + declarations.push(variable); } - return declarations || declaration; + return declarations; function emitExpression(value: Expression) { pendingExpressions = append(pendingExpressions, value); } - function emitAssignment(target: EffectiveBindingTarget, value: Expression, location: TextRange, original: Node) { + function emitBindingOrAssignment(target: EffectiveBindingOrAssignmentTarget, value: Expression, location: TextRange, original: Node) { Debug.assertNode(target, isBindingName); - pendingDeclarations.push({ pendingExpressions, name: target, value, location, original }); if (pendingExpressions) { value = inlineExpressions(append(pendingExpressions, value)); pendingExpressions = undefined; } - } - - function emitArrayAssignment(elements: EffectiveBindingElement[], value: Expression, location: TextRange, original: Node) { - Debug.assertEachNode(elements, isArrayBindingElement); - emitAssignment(createArrayBindingPattern(elements), value, location, original); - } - - function emitObjectAssignment(elements: EffectiveBindingElement[], value: Expression, location: TextRange, original: Node) { - Debug.assertEachNode(elements, isBindingElement); - emitAssignment(createObjectBindingPattern(elements), value, location, original); + pendingDeclarations.push({ pendingExpressions, name: target, value, location, original }); } } function flattenEffectiveBindingElement( host: FlattenHost, - bindingElement: EffectiveBindingElement, + bindingElement: EffectiveBindingOrAssignmentElement, boundValue: Expression | undefined, location: TextRange, skipInitializer?: boolean) { if (!skipInitializer) { - const initializer = getInitializerOfEffectiveBindingElement(bindingElement); + const initializer = visitNode(getInitializerOfEffectiveBindingElement(bindingElement), host.visitor, isExpression); if (initializer) { // Combine value and initializer boundValue = boundValue ? createDefaultValueCheck(host, boundValue, initializer, location) : initializer; @@ -762,7 +277,7 @@ namespace ts { } const bindingTarget = getTargetOfEffectiveBindingElement(bindingElement); if (!isEffectiveBindingPattern(bindingTarget)) { - host.emitAssignment(bindingTarget, boundValue, location, /*original*/ bindingElement); + host.emitBindingOrAssignment(bindingTarget, boundValue, location, /*original*/ bindingElement); } else { const elements = getElementsOfEffectiveBindingPattern(bindingTarget); @@ -784,18 +299,20 @@ namespace ts { } } - function flattenEffectiveObjectBindingElements(host: FlattenHost, bindingTarget: EffectiveObjectBindingPattern, elements: EffectiveBindingElement[], boundValue: Expression, location: TextRange) { - let bindingElements: EffectiveBindingElement[]; + function flattenEffectiveObjectBindingElements(host: FlattenHost, bindingTarget: EffectiveObjectBindingOrAssignmentPattern, elements: EffectiveBindingOrAssignmentElement[], boundValue: Expression, location: TextRange) { + let bindingElements: EffectiveBindingOrAssignmentElement[]; const numElements = elements.length; for (let i = 0; i < numElements; i++) { const element = elements[i]; if (!getEffectiveRestIndicator(element)) { - if (host.level >= FlattenLevel.ObjectRestDestructuringOnly && !(element.transformFlags & TransformFlags.ContainsSpreadExpression)) { + if (host.level <= FlattenLevel.ObjectRest + && !(element.transformFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest)) + && !(getTargetOfEffectiveBindingElement(element).transformFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest))) { bindingElements = append(bindingElements, element); } else { if (bindingElements) { - host.emitObjectAssignment(bindingElements, boundValue, location, bindingTarget); + host.emitBindingOrAssignment(host.createObjectBindingOrAssignmentPattern(bindingElements), boundValue, location, bindingTarget); bindingElements = undefined; } const propertyName = getEffectivePropertyNameOfEffectiveBindingElement(element); @@ -805,7 +322,7 @@ namespace ts { } else if (i === numElements - 1) { if (bindingElements) { - host.emitObjectAssignment(bindingElements, boundValue, location, bindingTarget); + host.emitBindingOrAssignment(host.createObjectBindingOrAssignmentPattern(bindingElements), boundValue, location, bindingTarget); bindingElements = undefined; } const value = createRestCall(boundValue, elements, bindingTarget); @@ -813,31 +330,29 @@ namespace ts { } } if (bindingElements) { - host.emitObjectAssignment(bindingElements, boundValue, location, bindingTarget); + host.emitBindingOrAssignment(host.createObjectBindingOrAssignmentPattern(bindingElements), boundValue, location, bindingTarget); } } - function flattenEffectiveArrayBindingElements(host: FlattenHost, bindingTarget: EffectiveArrayBindingPattern, elements: EffectiveBindingElement[], boundValue: Expression, location: TextRange) { - let bindingElements: EffectiveBindingElement[]; - let spreadContainingElements: EffectiveBindingElement[]; - let tempNames: Map; + function flattenEffectiveArrayBindingElements(host: FlattenHost, bindingTarget: EffectiveArrayBindingOrAssignmentPattern, elements: EffectiveBindingOrAssignmentElement[], boundValue: Expression, location: TextRange) { + let bindingElements: EffectiveBindingOrAssignmentElement[]; + let spreadContainingElements: [Identifier, EffectiveBindingOrAssignmentElement][]; const numElements = elements.length; for (let i = 0; i < numElements; i++) { const element = elements[i]; - if (isOmittedExpression(element)) { - continue; - } - else if (host.level >= FlattenLevel.ObjectRestDestructuringOnly) { - if (element.transformFlags & TransformFlags.ContainsSpreadExpression) { + if (host.level <= FlattenLevel.ObjectRest) { + if (element.transformFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest) && i < numElements - 1) { const temp = createTempVariable(/*recordTempVariable*/ undefined); - tempNames = appendProperty(tempNames, getNodeId(element), temp); - spreadContainingElements = append(spreadContainingElements, element); - bindingElements = append(bindingElements, temp); + spreadContainingElements = append(spreadContainingElements, <[Identifier, EffectiveBindingOrAssignmentElement]>[temp, element]); + bindingElements = append(bindingElements, host.createArrayBindingOrAssignmentElement(temp)); } else { bindingElements = append(bindingElements, element); } } + else if (isOmittedExpression(element)) { + continue; + } else if (!getEffectiveRestIndicator(element)) { const value = createElementAccess(boundValue, i); flattenEffectiveBindingElement(host, element, value, /*location*/ element); @@ -848,11 +363,11 @@ namespace ts { } } if (bindingElements) { - host.emitArrayAssignment(bindingElements, boundValue, location, bindingTarget); + host.emitBindingOrAssignment(host.createArrayBindingOrAssignmentPattern(bindingElements), boundValue, location, bindingTarget); } if (spreadContainingElements) { - for (const element of spreadContainingElements) { - flattenEffectiveBindingElement(host, element, tempNames[getNodeId(element)], element); + for (const [id, element] of spreadContainingElements) { + flattenEffectiveBindingElement(host, element, id, element); } } } @@ -923,7 +438,7 @@ namespace ts { else { const temp = createTempVariable(/*recordTempVariable*/ undefined); if (host.recordTempVariablesInLine) { - host.emitAssignment(temp, value, location, /*original*/ undefined); + host.emitBindingOrAssignment(temp, value, location, /*original*/ undefined); } else { host.context.hoistVariableDeclaration(temp); @@ -936,7 +451,7 @@ namespace ts { /** * Determines whether the EffectiveBindingElement is a declaration */ - function isDeclarationBindingElement(bindingElement: EffectiveBindingElement): bindingElement is VariableDeclaration | ParameterDeclaration | BindingElement { + function isDeclarationBindingElement(bindingElement: EffectiveBindingOrAssignmentElement): bindingElement is VariableDeclaration | ParameterDeclaration | BindingElement { switch (bindingElement.kind) { case SyntaxKind.VariableDeclaration: case SyntaxKind.Parameter: @@ -950,7 +465,7 @@ namespace ts { /** * Gets the initializer of an EffectiveBindingElement. */ - function getInitializerOfEffectiveBindingElement(bindingElement: EffectiveBindingElement): Expression | undefined { + function getInitializerOfEffectiveBindingElement(bindingElement: EffectiveBindingOrAssignmentElement): Expression | undefined { if (isDeclarationBindingElement(bindingElement)) { // `1` in `let { a = 1 } = ...` // `1` in `let { a: b = 1 } = ...` @@ -992,7 +507,7 @@ namespace ts { /** * Gets the name of an EffectiveBindingElement. */ - function getTargetOfEffectiveBindingElement(bindingElement: EffectiveBindingElement): EffectiveBindingTarget { + function getTargetOfEffectiveBindingElement(bindingElement: EffectiveBindingOrAssignmentElement): EffectiveBindingOrAssignmentTarget { if (isDeclarationBindingElement(bindingElement)) { // `a` in `let { a } = ...` // `a` in `let { a = 1 } = ...` @@ -1067,7 +582,7 @@ namespace ts { /** * Determines whether an EffectiveBindingElement is a rest element. */ - function getEffectiveRestIndicator(bindingElement: EffectiveBindingElement): EffectiveRestIndicator { + function getEffectiveRestIndicator(bindingElement: EffectiveBindingOrAssignmentElement): EffectiveBindingOrAssignmentRestIndicator { switch (bindingElement.kind) { case SyntaxKind.Parameter: case SyntaxKind.BindingElement: @@ -1086,7 +601,7 @@ namespace ts { /** * Gets the property name of a BindingElement-like element */ - function getEffectivePropertyNameOfEffectiveBindingElement(bindingElement: EffectiveBindingElement) { + function getEffectivePropertyNameOfEffectiveBindingElement(bindingElement: EffectiveBindingOrAssignmentElement) { switch (bindingElement.kind) { case SyntaxKind.BindingElement: // `a` in `let { a: b } = ...` @@ -1126,7 +641,7 @@ namespace ts { /** * Determines whether a node is BindingPattern-like */ - function isEffectiveBindingPattern(node: EffectiveBindingTarget): node is EffectiveBindingPattern { + function isEffectiveBindingPattern(node: EffectiveBindingOrAssignmentTarget): node is EffectiveBindingOrAssignmentPattern { return isEffectiveObjectBindingPattern(node) || isEffectiveArrayBindingPattern(node); } @@ -1134,7 +649,7 @@ namespace ts { /** * Determines whether a node is ObjectBindingPattern-like */ - function isEffectiveObjectBindingPattern(node: EffectiveBindingTarget): node is EffectiveObjectBindingPattern { + function isEffectiveObjectBindingPattern(node: EffectiveBindingOrAssignmentTarget): node is EffectiveObjectBindingOrAssignmentPattern { switch (node.kind) { case SyntaxKind.ObjectBindingPattern: case SyntaxKind.ObjectLiteralExpression: @@ -1147,7 +662,7 @@ namespace ts { /** * Determines whether a node is ArrayBindingPattern-like */ - function isEffectiveArrayBindingPattern(node: EffectiveBindingTarget): node is EffectiveArrayBindingPattern { + function isEffectiveArrayBindingPattern(node: EffectiveBindingOrAssignmentTarget): node is EffectiveArrayBindingOrAssignmentPattern { switch (node.kind) { case SyntaxKind.ArrayBindingPattern: case SyntaxKind.ArrayLiteralExpression: @@ -1160,7 +675,7 @@ namespace ts { /** * Gets the elements of a BindingPattern-like name */ - function getElementsOfEffectiveBindingPattern(name: EffectiveBindingPattern): EffectiveBindingElement[] { + function getElementsOfEffectiveBindingPattern(name: EffectiveBindingOrAssignmentPattern): EffectiveBindingOrAssignmentElement[] { switch (name.kind) { case SyntaxKind.ObjectBindingPattern: case SyntaxKind.ArrayBindingPattern: @@ -1175,179 +690,79 @@ namespace ts { } } - // function updateEffectiveBindingElement(bindingElement: EffectiveBindingElement, restIndicator: EffectiveRestIndicator, propertyName: PropertyName, target: EffectiveBindingTarget, initializer: Expression) { - // switch (bindingElement.kind) { - // case SyntaxKind.VariableDeclaration: - // Debug.assertNode(target, isBindingName); - // Debug.assertMissingNode(propertyName); - // return updateVariableDeclaration( - // bindingElement, - // target, - // /*type*/ undefined, - // initializer - // ); - - // case SyntaxKind.Parameter: - // Debug.assertOptionalToken(restIndicator, SyntaxKind.DotDotDotToken); - // Debug.assertMissingNode(propertyName); - // Debug.assertNode(target, isBindingName); - // return updateParameter( - // bindingElement, - // /*decorators*/ undefined, - // /*modifiers*/ undefined, - // restIndicator, - // target, - // /*type*/ undefined, - // initializer - // ); - - // case SyntaxKind.BindingElement: - // Debug.assertOptionalToken(restIndicator, SyntaxKind.DotDotDotToken); - // Debug.assertNode(target, isBindingName); - // return updateBindingElement( - // bindingElement, - // restIndicator, - // propertyName, - // target, - // initializer - // ); - - // case SyntaxKind.PropertyAssignment: - // case SyntaxKind.ShorthandPropertyAssignment: - // case SyntaxKind.SpreadAssignment: - // if (restIndicator) { - // return convertToSpreadAssignment(bindingElement, propertyName, target, initializer); - // } - // else if (propertyName) { - // return convertToPropertyAssignment(bindingElement, propertyName, target, initializer); - // } - // else { - // return convertToShorthandPropertyAssignment(bindingElement, target, initializer); - // } - - // case SyntaxKind.ArrayLiteralExpression: - // case SyntaxKind.BinaryExpression: - // case SyntaxKind.SpreadElement: - // case SyntaxKind.PropertyAccessExpression: - // case SyntaxKind.ElementAccessExpression: - // Debug.assertMissingNode(propertyName); - // if (restIndicator) { - // return convertToSpreadElement(bindingElement, target, initializer); - // } - // else { - - // } - // } - // } - - // function convertToSpreadAssignment(node: PropertyAssignment | ShorthandPropertyAssignment | SpreadAssignment, propertyName: PropertyName, target: EffectiveBindingTarget, initializer: Expression) { - // Debug.assertMissingNode(propertyName); - // Debug.assertNode(target, isIdentifier); - // Debug.assertMissingNode(initializer); - // if (node.kind === SyntaxKind.SpreadAssignment) { - // return updateSpreadAssignment(node, target); - // } - // return setOriginalNode( - // createSpreadAssignment( - // target, - // node - // ), - // node - // ); - // } - - // function convertToPropertyAssignment(node: PropertyAssignment | ShorthandPropertyAssignment | SpreadAssignment, propertyName: PropertyName, target: EffectiveBindingTarget, initializer: Expression) { - // Debug.assertNode(target, isExpression); - // if (node.kind === SyntaxKind.PropertyAssignment) { - // return updatePropertyAssignment( - // node, - // propertyName, - // initializer ? - // isAssignmentExpression(node.initializer, /*excludeCompoundAssignment*/ true) - // ? updateBinary(node.initializer, target, initializer) - // : createAssignment(target, initializer) - // : target - // ); - // } - // return setOriginalNode( - // createPropertyAssignment( - // propertyName, - // initializer ? - // createAssignment(target, initializer) - // : target, - // node - // ), - // node - // ); - // } - - // function convertToShorthandPropertyAssignment(node: PropertyAssignment | ShorthandPropertyAssignment | SpreadAssignment, target: EffectiveBindingTarget, initializer: Expression) { - // Debug.assertNode(target, isIdentifier); - // if (node.kind === SyntaxKind.ShorthandPropertyAssignment) { - // return updateShorthandPropertyAssignment( - // node, - // target, - // initializer - // ); - // } - // return setOriginalNode( - // createShorthandPropertyAssignment( - // target, - // initializer, - // node - // ), - // node - // ); - // } - - // function convertToSpreadElement(node: Expression, target: EffectiveBindingTarget, initializer: Expression) { - // Debug.assertNode(target, isExpression); - // Debug.assertMissingNode(initializer); - // if (node.kind === SyntaxKind.SpreadElement) { - // return updateSpread( - // node, - // target); - // } - // return setOriginalNode( - // createSpread( - // target, - // node - // ), - // node - // ); - // } - - // function convertToElement(node: Expression, target: EffectiveBindingTarget, initializer: Expression) { - - // } - - function createOrUpdateVariableDeclaration(node: Node, name: BindingName, initializer: Expression, location: TextRange) { - if (node && node.kind === SyntaxKind.VariableDeclaration) { - return updateVariableDeclaration(node, name, /*type*/ undefined, initializer); - } - const variable = createVariableDeclaration(name, /*type*/ undefined, initializer, location); - return node ? setOriginalNode(variable, node) : variable; + function createEffectiveArrayBindingPattern(elements: EffectiveBindingOrAssignmentElement[]) { + Debug.assertEachNode(elements, isArrayBindingElement); + return createArrayBindingPattern(elements); } - function createOrUpdateArrayBindingPattern(node: Node, elements: ArrayBindingElement[], location: TextRange) { - if (node && node.kind === SyntaxKind.ArrayBindingPattern) { - return updateArrayBindingPattern(node, elements); - } - const pattern = createArrayBindingPattern(elements, location); - return node ? setOriginalNode(pattern, node) : pattern; + function createEffectiveArrayAssignmentPattern(elements: EffectiveBindingOrAssignmentElement[]) { + return createArrayLiteral(map(elements, convertToArrayLiteralElement)); } - function createOrUpdateObjectBindingPattern(node: Node, elements: BindingElement[], location: TextRange) { - if (node && node.kind === SyntaxKind.ArrayBindingPattern) { - return updateObjectBindingPattern(node, elements); + function createEffectiveObjectBindingPattern(elements: EffectiveBindingOrAssignmentElement[]) { + Debug.assertEachNode(elements, isBindingElement); + return createObjectBindingPattern(elements); + } + + function createEffectiveObjectAssignmentPattern(elements: EffectiveBindingOrAssignmentElement[]) { + return createObjectLiteral(map(elements, convertToObjectLiteralElement)); + } + + function createEffectiveBindingElement(name: Identifier) { + return createBindingElement(/*propertyName*/ undefined, /*dotDotDotToken*/ undefined, name); + } + + function createEffectiveAssignmentElement(name: Identifier) { + return name; + } + + function convertToArrayLiteralElement(element: EffectiveBindingOrAssignmentElement) { + if (isBindingElement(element)) { + if (element.dotDotDotToken) { + Debug.assertNode(element.name, isIdentifier); + return setOriginalNode(createSpread(element.name, element), element); + } + const expression = convertToExpressionTarget(element.name); + return element.initializer ? setOriginalNode(createAssignment(expression, element.initializer, element), element) : expression; } - const pattern = createObjectBindingPattern(elements, location); - return node ? setOriginalNode(pattern, node) : pattern; + Debug.assertNode(element, isExpression); + return element; + } + + function convertToObjectLiteralElement(element: EffectiveBindingOrAssignmentElement) { + if (isBindingElement(element)) { + if (element.dotDotDotToken) { + Debug.assertNode(element.name, isIdentifier); + return setOriginalNode(createSpreadAssignment(element.name, element), element); + } + if (element.propertyName) { + const expression = convertToExpressionTarget(element.name); + return setOriginalNode(createPropertyAssignment(element.propertyName, element.initializer ? createAssignment(expression, element.initializer) : expression, element), element); + } + Debug.assertNode(element.name, isIdentifier); + return setOriginalNode(createShorthandPropertyAssignment(element.name, element.initializer, element), element); + } + Debug.assertNode(element, isObjectLiteralElementLike); + return element; + } + + function convertToExpressionTarget(target: EffectiveBindingOrAssignmentTarget): Expression { + if (isBindingPattern(target)) { + switch (target.kind) { + case SyntaxKind.ArrayBindingPattern: + return setOriginalNode(createArrayLiteral(map(target.elements, convertToArrayLiteralElement), target), target); + case SyntaxKind.ObjectBindingPattern: + return setOriginalNode(createObjectLiteral(map(target.elements, convertToObjectLiteralElement), target), target); + } + return; + } + Debug.assertNode(target, isExpression); + return target; } /** Given value: o, propName: p, pattern: { a, b, ...p } from the original statement * `{ a, b, ...p } = o`, create `p = __rest(o, ["a", "b"]);`*/ - function createRestCall(value: Expression, elements: EffectiveBindingElement[], location: TextRange): Expression { + function createRestCall(value: Expression, elements: EffectiveBindingOrAssignmentElement[], location: TextRange): Expression { const propertyNames: LiteralExpression[] = []; for (let i = 0; i < elements.length - 1; i++) { if (isOmittedExpression(elements[i])) { diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index e4fb8a0aaf0..dd7498c4149 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -279,7 +279,7 @@ namespace ts { else if (node.transformFlags & TransformFlags.ContainsES2015 || (isInConstructorWithCapturedSuper && !isExpression(node))) { // we want to dive in this branch either if node has children with ES2015 specific syntax // or we are inside constructor that captures result of the super call so all returns without expression should be - // rewritten. Note: we skip expressions since returns should never appear there + // rewritten. Note: we skip expressions since returns should never appear there return visitEachChild(node, visitor, context); } else { @@ -861,7 +861,7 @@ namespace ts { } if (constructor) { - addDefaultValueAssignmentsIfNeeded(statements, constructor, visitor, /*convertObjectRest*/ false); + addDefaultValueAssignmentsIfNeeded(context, statements, constructor, visitor, /*convertObjectRest*/ false); addRestParameterIfNeeded(statements, constructor, hasSynthesizedSuper); Debug.assert(statementOffset >= 0, "statementOffset not initialized correctly!"); @@ -1328,16 +1328,10 @@ namespace ts { // If we are here it is most likely because our expression is a destructuring assignment. switch (node.expression.kind) { case SyntaxKind.ParenthesizedExpression: - return updateStatement(node, - visitParenthesizedExpression(node.expression, /*needsDestructuringValue*/ false) - ); - + return updateStatement(node, visitParenthesizedExpression(node.expression, /*needsDestructuringValue*/ false)); case SyntaxKind.BinaryExpression: - return updateStatement(node, - visitBinaryExpression(node.expression, /*needsDestructuringValue*/ false) - ); + return updateStatement(node, visitBinaryExpression(node.expression, /*needsDestructuringValue*/ false)); } - return visitEachChild(node, visitor, context); } @@ -1350,22 +1344,14 @@ namespace ts { */ function visitParenthesizedExpression(node: ParenthesizedExpression, needsDestructuringValue: boolean): ParenthesizedExpression { // If we are here it is most likely because our expression is a destructuring assignment. - if (needsDestructuringValue) { + if (!needsDestructuringValue) { switch (node.expression.kind) { case SyntaxKind.ParenthesizedExpression: - return createParen( - visitParenthesizedExpression(node.expression, /*needsDestructuringValue*/ true), - /*location*/ node - ); - + return updateParen(node, visitParenthesizedExpression(node.expression, /*needsDestructuringValue*/ false)); case SyntaxKind.BinaryExpression: - return createParen( - visitBinaryExpression(node.expression, /*needsDestructuringValue*/ true), - /*location*/ node - ); + return updateParen(node, visitBinaryExpression(node.expression, /*needsDestructuringValue*/ false)); } } - return visitEachChild(node, visitor, context); } @@ -1379,7 +1365,13 @@ namespace ts { function visitBinaryExpression(node: BinaryExpression, needsDestructuringValue: boolean): Expression { // If we are here it is because this is a destructuring assignment. Debug.assert(isDestructuringAssignment(node)); - return flattenDestructuringAssignment(context, node, needsDestructuringValue, hoistVariableDeclaration, visitor); + return flattenDestructuringToExpression( + context, + node, + needsDestructuringValue, + FlattenLevel.All, + /*createAssignmentCallback*/ undefined, + visitor); } function visitVariableStatement(node: VariableStatement): Statement { @@ -1391,7 +1383,14 @@ namespace ts { if (decl.initializer) { let assignment: Expression; if (isBindingPattern(decl.name)) { - assignment = flattenVariableDestructuringToExpression(decl, hoistVariableDeclaration, /*createAssignmentCallback*/ undefined, visitor); + assignment = flattenDestructuringToExpression( + context, + decl, + /*needsValue*/ false, + FlattenLevel.All, + /*createAssignmentCallback*/ undefined, + visitor + ); } else { assignment = createBinary(decl.name, SyntaxKind.EqualsToken, visitNode(decl.initializer, visitor, isExpression)); @@ -1544,8 +1543,16 @@ namespace ts { if (isBindingPattern(node.name)) { const recordTempVariablesInLine = !enclosingVariableStatement || !hasModifier(enclosingVariableStatement, ModifierFlags.Export); - return flattenVariableDestructuring(node, /*value*/ undefined, visitor, - recordTempVariablesInLine ? undefined : hoistVariableDeclaration); + debugger; + return flattenDestructuringToDeclarations( + context, + node, + /*value*/ undefined, + /*skipInitializer*/ false, + recordTempVariablesInLine, + FlattenLevel.All, + visitor + ); } return visitEachChild(node, visitor, context); @@ -2174,7 +2181,7 @@ namespace ts { const temp = createTempVariable(undefined); const newVariableDeclaration = createVariableDeclaration(temp, undefined, undefined, node.variableDeclaration); - const vars = flattenVariableDestructuring(node.variableDeclaration, temp, visitor); + const vars = flattenDestructuringToDeclarations(context, node.variableDeclaration, temp, /*skipInitializer*/ false, /*recordTempVariablesInLine*/ true, FlattenLevel.All, visitor); const list = createVariableDeclarationList(vars, /*location*/node.variableDeclaration, /*flags*/node.variableDeclaration.flags); const destructure = createVariableStatement(undefined, list); @@ -2261,7 +2268,7 @@ namespace ts { setEmitFlags(thisArg, EmitFlags.NoSubstitution); } let resultingCall: CallExpression | BinaryExpression; - if (node.transformFlags & TransformFlags.ContainsSpreadExpression) { + if (node.transformFlags & TransformFlags.ContainsSpread) { // [source] // f(...a, b) // x.m(...a, b) @@ -2323,7 +2330,7 @@ namespace ts { */ function visitNewExpression(node: NewExpression): LeftHandSideExpression { // We are here because we contain a SpreadElementExpression. - Debug.assert((node.transformFlags & TransformFlags.ContainsSpreadExpression) !== 0); + Debug.assert((node.transformFlags & TransformFlags.ContainsSpread) !== 0); // [source] // new C(...a) diff --git a/src/compiler/transformers/es2017.ts b/src/compiler/transformers/es2017.ts index 548609d676c..0990e9c454f 100644 --- a/src/compiler/transformers/es2017.ts +++ b/src/compiler/transformers/es2017.ts @@ -29,12 +29,6 @@ namespace ts { */ let enabledSubstitutions: ES2017SubstitutionFlags; - /** - * Keeps track of whether we are within any containing namespaces when performing - * just-in-time substitution while printing an expression identifier. - */ - let applicableSubstitutions: ES2017SubstitutionFlags; - /** * This keeps track of containers where `super` is valid, for use with * just-in-time substitution for `super` expressions inside of async methods. @@ -49,8 +43,6 @@ namespace ts { context.onEmitNode = onEmitNode; context.onSubstituteNode = onSubstituteNode; - let currentScope: SourceFile | Block | ModuleBlock | CaseBlock; - return transformSourceFile; function transformSourceFile(node: SourceFile) { @@ -64,13 +56,9 @@ namespace ts { } function visitor(node: Node): VisitResult { - if (node.transformFlags & TransformFlags.ES2017) { + if (node.transformFlags & TransformFlags.ContainsES2017) { return visitorWorker(node); } - else if (node.transformFlags & TransformFlags.ContainsES2017) { - return visitEachChild(node, visitor, context); - } - return node; } @@ -101,8 +89,7 @@ namespace ts { return visitArrowFunction(node); default: - Debug.failBadSyntaxKind(node); - return node; + return visitEachChild(node, visitor, context); } } @@ -133,28 +120,18 @@ namespace ts { * @param node The method node. */ function visitMethodDeclaration(node: MethodDeclaration) { - if (!isAsyncFunctionLike(node)) { - return node; - } - const method = createMethod( + return updateMethod( + node, /*decorators*/ undefined, visitNodes(node.modifiers, visitor, isModifier), - node.asteriskToken, node.name, /*typeParameters*/ undefined, - visitNodes(node.parameters, visitor, isParameter), + visitParameterList(node.parameters, visitor, context), /*type*/ undefined, - transformFunctionBody(node), - /*location*/ node + isAsyncFunctionLike(node) + ? transformAsyncFunctionBody(node) + : visitFunctionBody(node.body, visitor, context) ); - - // While we emit the source map for the node after skipping decorators and modifiers, - // we need to emit the comments for the original range. - setCommentRange(method, node); - setSourceMapRange(method, moveRangePastDecorators(node)); - setOriginalNode(method, node); - - return method; } /** @@ -166,23 +143,18 @@ namespace ts { * @param node The function node. */ function visitFunctionDeclaration(node: FunctionDeclaration): VisitResult { - if (!isAsyncFunctionLike(node)) { - return node; - } - const func = createFunctionDeclaration( + return updateFunctionDeclaration( + node, /*decorators*/ undefined, visitNodes(node.modifiers, visitor, isModifier), - node.asteriskToken, node.name, /*typeParameters*/ undefined, visitNodes(node.parameters, visitor, isParameter), /*type*/ undefined, - transformFunctionBody(node), - /*location*/ node + isAsyncFunctionLike(node) + ? transformAsyncFunctionBody(node) + : visitFunctionBody(node.body, visitor, context) ); - setOriginalNode(func, node); - - return func; } /** @@ -194,27 +166,20 @@ namespace ts { * @param node The function expression node. */ function visitFunctionExpression(node: FunctionExpression): Expression { - if (!isAsyncFunctionLike(node)) { - return node; - } if (nodeIsMissing(node.body)) { return createOmittedExpression(); } - - const func = createFunctionExpression( + return updateFunctionExpression( + node, /*modifiers*/ undefined, - node.asteriskToken, node.name, /*typeParameters*/ undefined, visitNodes(node.parameters, visitor, isParameter), /*type*/ undefined, - transformFunctionBody(node), - /*location*/ node + isAsyncFunctionLike(node) + ? transformAsyncFunctionBody(node) + : visitFunctionBody(node.body, visitor, context) ); - - setOriginalNode(func, node); - - return func; } /** @@ -223,45 +188,21 @@ namespace ts { * - The node is marked async */ function visitArrowFunction(node: ArrowFunction) { - if (!isAsyncFunctionLike(node)) { - return node; - } - const func = createArrowFunction( + return updateArrowFunction( + node, visitNodes(node.modifiers, visitor, isModifier), /*typeParameters*/ undefined, visitNodes(node.parameters, visitor, isParameter), /*type*/ undefined, - node.equalsGreaterThanToken, - transformConciseBody(node), - /*location*/ node + isAsyncFunctionLike(node) + ? transformAsyncFunctionBody(node) + : visitFunctionBody(node.body, visitor, context) ); - - setOriginalNode(func, node); - - return func; } - function transformFunctionBody(node: MethodDeclaration | AccessorDeclaration | FunctionDeclaration | FunctionExpression): FunctionBody { - return transformAsyncFunctionBody(node); - } - - function transformConciseBody(node: ArrowFunction): ConciseBody { - return transformAsyncFunctionBody(node); - } - - function transformFunctionBodyWorker(body: Block, start = 0) { - const savedCurrentScope = currentScope; - currentScope = body; - startLexicalEnvironment(); - - const statements = visitNodes(body.statements, visitor, isStatement, start); - const visited = updateBlock(body, statements); - const declarations = endLexicalEnvironment(); - currentScope = savedCurrentScope; - return mergeFunctionBodyLexicalEnvironment(visited, declarations); - } - - function transformAsyncFunctionBody(node: FunctionLikeDeclaration): ConciseBody | FunctionBody { + function transformAsyncFunctionBody(node: MethodDeclaration | AccessorDeclaration | FunctionDeclaration | FunctionExpression): FunctionBody; + function transformAsyncFunctionBody(node: ArrowFunction): ConciseBody; + function transformAsyncFunctionBody(node: FunctionLikeDeclaration): ConciseBody { const original = getOriginalNode(node, isFunctionLike); const nodeType = original.type; const promiseConstructor = languageVersion < ScriptTarget.ES2015 ? getPromiseConstructor(nodeType) : undefined; @@ -274,7 +215,6 @@ namespace ts { // passed to `__awaiter` is executed inside of the callback to the // promise constructor. - if (!isArrowFunction) { const statements: Statement[] = []; const statementOffset = addPrologueDirectives(statements, (node.body).statements, /*ensureUseStrict*/ false, visitor); @@ -289,6 +229,7 @@ namespace ts { ) ); + addRange(statements, endLexicalEnvironment()); const block = createBlock(statements, /*location*/ node.body, /*multiLine*/ true); // Minor optimization, emit `_super` helper to capture `super` access in an arrow. @@ -307,32 +248,32 @@ namespace ts { return block; } else { - return createAwaiterHelper( + const expression = createAwaiterHelper( currentSourceFileExternalHelpersModuleName, hasLexicalArguments, promiseConstructor, - transformConciseBodyWorker(node.body, /*forceBlockFunctionBody*/ true) + transformFunctionBodyWorker(node.body) ); + + const declarations = endLexicalEnvironment(); + if (some(declarations)) { + const block = convertToFunctionBody(expression); + return updateBlock(block, createNodeArray(concatenate(block.statements, declarations), block.statements)); + } + + return expression; } } - function transformConciseBodyWorker(body: Block | Expression, forceBlockFunctionBody: boolean) { + function transformFunctionBodyWorker(body: ConciseBody, start?: number) { if (isBlock(body)) { - return transformFunctionBodyWorker(body); + return updateBlock(body, visitLexicalEnvironment(body.statements, visitor, context, start)); } else { startLexicalEnvironment(); - const visited: Expression | Block = visitNode(body, visitor, isConciseBody); + const visited = convertToFunctionBody(visitNode(body, visitor, isConciseBody)); const declarations = endLexicalEnvironment(); - const merged = mergeFunctionBodyLexicalEnvironment(visited, declarations); - if (forceBlockFunctionBody && !isBlock(merged)) { - return createBlock([ - createReturn(merged) - ]); - } - else { - return merged; - } + return updateBlock(visited, createNodeArray(concatenate(visited.statements, declarations), visited.statements)); } } @@ -452,18 +393,17 @@ namespace ts { * @param emit A callback used to emit the node in the printer. */ function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void { - const savedApplicableSubstitutions = applicableSubstitutions; - const savedCurrentSuperContainer = currentSuperContainer; // If we need to support substitutions for `super` in an async method, // we should track it here. if (enabledSubstitutions & ES2017SubstitutionFlags.AsyncMethodsWithSuper && isSuperContainer(node)) { + const savedCurrentSuperContainer = currentSuperContainer; currentSuperContainer = node; + previousOnEmitNode(emitContext, node, emitCallback); + currentSuperContainer = savedCurrentSuperContainer; + } + else { + previousOnEmitNode(emitContext, node, emitCallback); } - - previousOnEmitNode(emitContext, node, emitCallback); - - applicableSubstitutions = savedApplicableSubstitutions; - currentSuperContainer = savedCurrentSuperContainer; } /** diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index 9013fabf584..a52ee9b1a24 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -4,9 +4,6 @@ /*@internal*/ namespace ts { export function transformESNext(context: TransformationContext) { - const { - hoistVariableDeclaration, - } = context; let currentSourceFile: SourceFile; return transformSourceFile; @@ -20,7 +17,7 @@ namespace ts { return visitorWorker(node); } else if (node.transformFlags & TransformFlags.ContainsESNext) { - return visitEachChild(node, visitor, context); + return visitNodeContainingESNext(node); } else { return node; @@ -32,7 +29,7 @@ namespace ts { case SyntaxKind.ObjectLiteralExpression: return visitObjectLiteralExpression(node as ObjectLiteralExpression); case SyntaxKind.BinaryExpression: - return visitBinaryExpression(node as BinaryExpression); + return visitBinaryExpression(node as BinaryExpression, /*needsDestructuringValue*/ true); case SyntaxKind.VariableDeclaration: return visitVariableDeclaration(node as VariableDeclaration); case SyntaxKind.ForOfStatement: @@ -54,6 +51,16 @@ namespace ts { } } + function visitNodeContainingESNext(node: Node) { + switch (node.kind) { + case SyntaxKind.ExpressionStatement: + return visitExpressionStatement(node as ExpressionStatement); + case SyntaxKind.ParenthesizedExpression: + return visitParenthesizedExpression(node as ParenthesizedExpression, /*needsDestructuringValue*/ true); + } + return visitEachChild(node, visitor, context); + } + function chunkObjectLiteralElements(elements: ObjectLiteralElement[]): Expression[] { let chunkObject: (ShorthandPropertyAssignment | PropertyAssignment)[]; const objects: Expression[] = []; @@ -99,14 +106,43 @@ namespace ts { return createCall(createIdentifier("__assign"), undefined, objects); } + function visitExpressionStatement(node: ExpressionStatement): ExpressionStatement { + switch (node.expression.kind) { + case SyntaxKind.ParenthesizedExpression: + return updateStatement(node, visitParenthesizedExpression(node.expression, /*needsDestructuringValue*/ false)); + case SyntaxKind.BinaryExpression: + return updateStatement(node, visitBinaryExpression(node.expression, /*needsDestructuringValue*/ false)); + } + return visitEachChild(node, visitor, context); + } + + function visitParenthesizedExpression(node: ParenthesizedExpression, needsDestructuringValue: boolean): ParenthesizedExpression { + if (!needsDestructuringValue) { + switch (node.expression.kind) { + case SyntaxKind.ParenthesizedExpression: + return updateParen(node, visitParenthesizedExpression(node.expression, /*needsDestructuringValue*/ false)); + case SyntaxKind.BinaryExpression: + return updateParen(node, visitBinaryExpression(node.expression, /*needsDestructuringValue*/ false)); + } + } + return visitEachChild(node, visitor, context); + } + /** * Visits a BinaryExpression that contains a destructuring assignment. * * @param node A BinaryExpression node. */ - function visitBinaryExpression(node: BinaryExpression): Expression { + function visitBinaryExpression(node: BinaryExpression, needsDestructuringValue: boolean): Expression { if (isDestructuringAssignment(node) && node.left.transformFlags & TransformFlags.ContainsESNext) { - return flattenDestructuringAssignment(context, node, /*needsDestructuringValue*/ true, hoistVariableDeclaration, visitor, /*transformRest*/ true); + return flattenDestructuringToExpression( + context, + node, + needsDestructuringValue, + FlattenLevel.ObjectRest, + /*createAssignmentCallback*/ undefined, + visitor + ); } return visitEachChild(node, visitor, context); @@ -120,10 +156,8 @@ namespace ts { function visitVariableDeclaration(node: VariableDeclaration): VisitResult { // If we are here it is because the name contains a binding pattern with a rest somewhere in it. if (isBindingPattern(node.name) && node.name.transformFlags & TransformFlags.AssertESNext) { - const result = flattenVariableDestructuring(node, /*value*/ undefined, visitor, /*recordTempVariable*/ undefined, /*transformRest*/ true); - return result; + return flattenDestructuringToDeclarations(context, node, /*boundValue*/ undefined, /*skipInitializer*/ false, /*recordTempVariablesInLine*/ true, FlattenLevel.ObjectRest, visitor); } - return visitEachChild(node, visitor, context); } @@ -168,14 +202,14 @@ namespace ts { const declaration = firstOrUndefined(initializer.declarations); return declaration && declaration.name && declaration.name.kind === SyntaxKind.ObjectBindingPattern && - !!(declaration.name.transformFlags & TransformFlags.ContainsSpreadExpression); + !!(declaration.name.transformFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest)); } return false; } function isRestAssignment(initializer: ForInitializer) { return initializer.kind === SyntaxKind.ObjectLiteralExpression && - initializer.transformFlags & TransformFlags.ContainsSpreadExpression; + initializer.transformFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest); } function visitParameter(node: ParameterDeclaration): ParameterDeclaration { @@ -204,7 +238,7 @@ namespace ts { function isObjectRestParameter(node: ParameterDeclaration) { return node.name && node.name.kind === SyntaxKind.ObjectBindingPattern && - !!(node.name.transformFlags & TransformFlags.ContainsSpreadExpression); + !!(node.name.transformFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest)); } function visitFunctionDeclaration(node: FunctionDeclaration): FunctionDeclaration { diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index ba762cc5f12..8bc09ea45b2 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -27,12 +27,9 @@ namespace ts { } function visitor(node: Node): VisitResult { - if (node.transformFlags & TransformFlags.Jsx) { + if (node.transformFlags & TransformFlags.ContainsJsx) { return visitorWorker(node); } - else if (node.transformFlags & TransformFlags.ContainsJsx) { - return visitEachChild(node, visitor, context); - } else { return node; } @@ -50,8 +47,7 @@ namespace ts { return visitJsxExpression(node); default: - Debug.failBadSyntaxKind(node); - return undefined; + return visitEachChild(node, visitor, context); } } diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index 87d02b44025..bb17b99e5c6 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -19,8 +19,7 @@ namespace ts { const { startLexicalEnvironment, - endLexicalEnvironment, - hoistVariableDeclaration, + endLexicalEnvironment } = context; const compilerOptions = context.getCompilerOptions(); @@ -757,10 +756,13 @@ namespace ts { */ function transformInitializedVariable(node: VariableDeclaration): Expression { if (isBindingPattern(node.name)) { - return flattenVariableDestructuringToExpression( + return flattenDestructuringToExpression( + context, node, - hoistVariableDeclaration, - createExportExpression + /*needsValue*/ false, + FlattenLevel.All, + createExportExpression, + /*visitor*/ undefined ); } else { diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index 0524b9354d2..1ed1f82d506 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -808,7 +808,7 @@ namespace ts { function transformInitializedVariable(node: VariableDeclaration, isExportedDeclaration: boolean): Expression { const createAssignment = isExportedDeclaration ? createExportedVariableAssignment : createNonExportedVariableAssignment; return isBindingPattern(node.name) - ? flattenVariableDestructuringToExpression(node, hoistVariableDeclaration, createAssignment, destructuringVisitor) + ? flattenDestructuringToExpression(context, node, /*needsValue*/ false, FlattenLevel.All, createAssignment, destructuringVisitor) : createAssignment(node.name, visitNode(node.initializer, destructuringVisitor, isExpression)); } @@ -1459,7 +1459,13 @@ namespace ts { */ function visitDestructuringAssignment(node: DestructuringAssignment): VisitResult { if (hasExportedReferenceInDestructuringTarget(node.left)) { - return flattenDestructuringAssignment(context, node, /*needsValue*/ true, hoistVariableDeclaration, destructuringVisitor); + return flattenDestructuringToExpression( + context, + node, + /*needsValue*/ true, + FlattenLevel.All, + /*createAssignmentCallback*/ undefined, + destructuringVisitor); } return visitEachChild(node, destructuringVisitor, context); diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 89fa039100a..118b170b6a4 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -2353,9 +2353,11 @@ namespace ts { function transformInitializedVariable(node: VariableDeclaration): Expression { const name = node.name; if (isBindingPattern(name)) { - return flattenVariableDestructuringToExpression( + return flattenDestructuringToExpression( + context, node, - hoistVariableDeclaration, + /*needsValue*/ false, + FlattenLevel.All, createNamespaceExportExpression, visitor ); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index e8018083783..e0ba546fb96 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -506,6 +506,7 @@ namespace ts { export interface NodeArray extends Array, TextRange { hasTrailingComma?: boolean; + /* @internal */ transformFlags?: TransformFlags; } export interface Token extends Node { @@ -3495,45 +3496,46 @@ namespace ts { // - Flags used to indicate that a node or subtree contains syntax that requires transformation. TypeScript = 1 << 0, ContainsTypeScript = 1 << 1, - Jsx = 1 << 2, - ContainsJsx = 1 << 3, - ESNext = 1 << 4, - ContainsESNext = 1 << 5, - ES2017 = 1 << 6, - ContainsES2017 = 1 << 7, - ES2016 = 1 << 8, - ContainsES2016 = 1 << 9, - ES2015 = 1 << 10, - ContainsES2015 = 1 << 11, - Generator = 1 << 12, - ContainsGenerator = 1 << 13, - DestructuringAssignment = 1 << 14, - ContainsDestructuringAssignment = 1 << 15, + ContainsJsx = 1 << 2, + ESNext = 1 << 3, + ContainsESNext = 1 << 4, + ContainsES2017 = 1 << 5, + ES2016 = 1 << 6, + ContainsES2016 = 1 << 7, + ES2015 = 1 << 8, + ContainsES2015 = 1 << 9, + Generator = 1 << 10, + ContainsGenerator = 1 << 11, + DestructuringAssignment = 1 << 12, + ContainsDestructuringAssignment = 1 << 13, // Markers // - Flags used to indicate that a subtree contains a specific transformation. - ContainsDecorators = 1 << 16, - ContainsPropertyInitializer = 1 << 17, - ContainsLexicalThis = 1 << 18, - ContainsCapturedLexicalThis = 1 << 19, - ContainsLexicalThisInComputedPropertyName = 1 << 20, - ContainsDefaultValueAssignments = 1 << 21, - ContainsParameterPropertyAssignments = 1 << 22, - ContainsSpreadExpression = 1 << 23, - ContainsComputedPropertyName = 1 << 24, - ContainsBlockScopedBinding = 1 << 25, - ContainsBindingPattern = 1 << 26, - ContainsYield = 1 << 27, - ContainsHoistedDeclarationOrCompletion = 1 << 28, + ContainsDecorators = 1 << 14, + ContainsPropertyInitializer = 1 << 15, + ContainsLexicalThis = 1 << 16, + ContainsCapturedLexicalThis = 1 << 17, + ContainsLexicalThisInComputedPropertyName = 1 << 18, + ContainsDefaultValueAssignments = 1 << 19, + ContainsParameterPropertyAssignments = 1 << 20, + ContainsSpread = 1 << 21, + ContainsRest = ContainsSpread, + ContainsObjectSpread = 1 << 22, + ContainsObjectRest = ContainsObjectSpread, + ContainsComputedPropertyName = 1 << 23, + ContainsBlockScopedBinding = 1 << 24, + ContainsBindingPattern = 1 << 25, + ContainsYield = 1 << 26, + ContainsHoistedDeclarationOrCompletion = 1 << 27, HasComputedFlags = 1 << 29, // Transform flags have been computed. // Assertions // - Bitmasks that are used to assert facts about the syntax of a node and its subtree. AssertTypeScript = TypeScript | ContainsTypeScript, - AssertJsx = Jsx | ContainsJsx, + AssertJsx = ContainsJsx, AssertESNext = ESNext | ContainsESNext, - AssertES2017 = ES2017 | ContainsES2017, + AssertES2017 = ContainsES2017, AssertES2016 = ES2016 | ContainsES2016, AssertES2015 = ES2015 | ContainsES2015, AssertGenerator = Generator | ContainsGenerator, @@ -3542,18 +3544,20 @@ namespace ts { // Scope Exclusions // - Bitmasks that exclude flags from propagating out of a specific context // into the subtree flags of their container. - NodeExcludes = TypeScript | Jsx | ESNext | ES2017 | ES2016 | ES2015 | DestructuringAssignment | Generator | HasComputedFlags, - ArrowFunctionExcludes = NodeExcludes | ContainsDecorators | ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsParameterPropertyAssignments | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion, - FunctionExcludes = NodeExcludes | ContainsDecorators | ContainsDefaultValueAssignments | ContainsCapturedLexicalThis | ContainsLexicalThis | ContainsParameterPropertyAssignments | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion, - ConstructorExcludes = NodeExcludes | ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion, - MethodOrAccessorExcludes = NodeExcludes | ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion, + NodeExcludes = TypeScript | ESNext | ES2016 | ES2015 | DestructuringAssignment | Generator | HasComputedFlags, + ArrowFunctionExcludes = NodeExcludes | ContainsDecorators | ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsParameterPropertyAssignments | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRest, + FunctionExcludes = NodeExcludes | ContainsDecorators | ContainsDefaultValueAssignments | ContainsCapturedLexicalThis | ContainsLexicalThis | ContainsParameterPropertyAssignments | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRest, + ConstructorExcludes = NodeExcludes | ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRest, + MethodOrAccessorExcludes = NodeExcludes | ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRest, ClassExcludes = NodeExcludes | ContainsDecorators | ContainsPropertyInitializer | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsComputedPropertyName | ContainsParameterPropertyAssignments | ContainsLexicalThisInComputedPropertyName, ModuleExcludes = NodeExcludes | ContainsDecorators | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding | ContainsHoistedDeclarationOrCompletion, TypeExcludes = ~ContainsTypeScript, - ObjectLiteralExcludes = NodeExcludes | ContainsDecorators | ContainsComputedPropertyName | ContainsLexicalThisInComputedPropertyName, - ArrayLiteralOrCallOrNewExcludes = NodeExcludes | ContainsSpreadExpression, - VariableDeclarationListExcludes = NodeExcludes | ContainsBindingPattern, - ParameterExcludes = NodeExcludes | ContainsBindingPattern, + ObjectLiteralExcludes = NodeExcludes | ContainsDecorators | ContainsComputedPropertyName | ContainsLexicalThisInComputedPropertyName | ContainsObjectSpread, + ArrayLiteralOrCallOrNewExcludes = NodeExcludes | ContainsSpread, + VariableDeclarationListExcludes = NodeExcludes | ContainsBindingPattern | ContainsObjectSpread, + ParameterExcludes = NodeExcludes, + CatchClauseExcludes = NodeExcludes | ContainsObjectSpread, + BindingPatternExcludes = NodeExcludes | ContainsSpread, // Masks // - Additional bitmasks @@ -3622,7 +3626,6 @@ namespace ts { endLexicalEnvironment(): Statement[]; } - export interface TextSpan { start: number; length: number; diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index ebb3ec01ed3..825e0b45e45 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -99,20 +99,26 @@ namespace ts { return node ? f(initial, node) : initial; } + function reduceNodeArray(nodes: Node[], f: (memo: T, nodes: Node[]) => T, initial: T) { + return nodes ? f(initial, nodes) : initial; + } + /** * Similar to `reduceLeft`, performs a reduction against each child of a node. * NOTE: Unlike `forEachChild`, this does *not* visit every node. Only nodes added to the * `nodeEdgeTraversalMap` above will be visited. * * @param node The node containing the children to reduce. - * @param f The callback function * @param initial The initial value to supply to the reduction. + * @param f The callback function */ - export function reduceEachChild(node: Node, f: (memo: T, node: Node) => T, initial: T): T { + export function reduceEachChild(node: Node, initial: T, cbNode: (memo: T, node: Node) => T, cbNodeArray?: (memo: T, nodes: Node[]) => T): T { if (node === undefined) { return initial; } + const reduceNodes: (nodes: Node[], f: (memo: T, node: Node | Node[]) => T, initial: T) => T = cbNodeArray ? reduceNodeArray : reduceLeft; + const cbNodes = cbNodeArray || cbNode; const kind = node.kind; // No need to visit nodes with no children. @@ -138,127 +144,127 @@ namespace ts { // Names case SyntaxKind.ComputedPropertyName: - result = reduceNode((node).expression, f, result); + result = reduceNode((node).expression, cbNode, result); break; // Signature elements case SyntaxKind.Parameter: - result = reduceLeft((node).decorators, f, result); - result = reduceLeft((node).modifiers, f, result); - result = reduceNode((node).name, f, result); - result = reduceNode((node).type, f, result); - result = reduceNode((node).initializer, f, result); + result = reduceNodes((node).decorators, cbNodes, result); + result = reduceNodes((node).modifiers, cbNodes, result); + result = reduceNode((node).name, cbNode, result); + result = reduceNode((node).type, cbNode, result); + result = reduceNode((node).initializer, cbNode, result); break; case SyntaxKind.Decorator: - result = reduceNode((node).expression, f, result); + result = reduceNode((node).expression, cbNode, result); break; // Type member case SyntaxKind.PropertyDeclaration: - result = reduceLeft((node).decorators, f, result); - result = reduceLeft((node).modifiers, f, result); - result = reduceNode((node).name, f, result); - result = reduceNode((node).type, f, result); - result = reduceNode((node).initializer, f, result); + result = reduceNodes((node).decorators, cbNodes, result); + result = reduceNodes((node).modifiers, cbNodes, result); + result = reduceNode((node).name, cbNode, result); + result = reduceNode((node).type, cbNode, result); + result = reduceNode((node).initializer, cbNode, result); break; case SyntaxKind.MethodDeclaration: - result = reduceLeft((node).decorators, f, result); - result = reduceLeft((node).modifiers, f, result); - result = reduceNode((node).name, f, result); - result = reduceLeft((node).typeParameters, f, result); - result = reduceLeft((node).parameters, f, result); - result = reduceNode((node).type, f, result); - result = reduceNode((node).body, f, result); + result = reduceNodes((node).decorators, cbNodes, result); + result = reduceNodes((node).modifiers, cbNodes, result); + result = reduceNode((node).name, cbNode, result); + result = reduceNodes((node).typeParameters, cbNodes, result); + result = reduceNodes((node).parameters, cbNodes, result); + result = reduceNode((node).type, cbNode, result); + result = reduceNode((node).body, cbNode, result); break; case SyntaxKind.Constructor: - result = reduceLeft((node).modifiers, f, result); - result = reduceLeft((node).parameters, f, result); - result = reduceNode((node).body, f, result); + result = reduceNodes((node).modifiers, cbNodes, result); + result = reduceNodes((node).parameters, cbNodes, result); + result = reduceNode((node).body, cbNode, result); break; case SyntaxKind.GetAccessor: - result = reduceLeft((node).decorators, f, result); - result = reduceLeft((node).modifiers, f, result); - result = reduceNode((node).name, f, result); - result = reduceLeft((node).parameters, f, result); - result = reduceNode((node).type, f, result); - result = reduceNode((node).body, f, result); + result = reduceNodes((node).decorators, cbNodes, result); + result = reduceNodes((node).modifiers, cbNodes, result); + result = reduceNode((node).name, cbNode, result); + result = reduceNodes((node).parameters, cbNodes, result); + result = reduceNode((node).type, cbNode, result); + result = reduceNode((node).body, cbNode, result); break; case SyntaxKind.SetAccessor: - result = reduceLeft((node).decorators, f, result); - result = reduceLeft((node).modifiers, f, result); - result = reduceNode((node).name, f, result); - result = reduceLeft((node).parameters, f, result); - result = reduceNode((node).body, f, result); + result = reduceNodes((node).decorators, cbNodes, result); + result = reduceNodes((node).modifiers, cbNodes, result); + result = reduceNode((node).name, cbNode, result); + result = reduceNodes((node).parameters, cbNodes, result); + result = reduceNode((node).body, cbNode, result); break; // Binding patterns case SyntaxKind.ObjectBindingPattern: case SyntaxKind.ArrayBindingPattern: - result = reduceLeft((node).elements, f, result); + result = reduceNodes((node).elements, cbNodes, result); break; case SyntaxKind.BindingElement: - result = reduceNode((node).propertyName, f, result); - result = reduceNode((node).name, f, result); - result = reduceNode((node).initializer, f, result); + result = reduceNode((node).propertyName, cbNode, result); + result = reduceNode((node).name, cbNode, result); + result = reduceNode((node).initializer, cbNode, result); break; // Expression case SyntaxKind.ArrayLiteralExpression: - result = reduceLeft((node).elements, f, result); + result = reduceNodes((node).elements, cbNodes, result); break; case SyntaxKind.ObjectLiteralExpression: - result = reduceLeft((node).properties, f, result); + result = reduceNodes((node).properties, cbNodes, result); break; case SyntaxKind.PropertyAccessExpression: - result = reduceNode((node).expression, f, result); - result = reduceNode((node).name, f, result); + result = reduceNode((node).expression, cbNode, result); + result = reduceNode((node).name, cbNode, result); break; case SyntaxKind.ElementAccessExpression: - result = reduceNode((node).expression, f, result); - result = reduceNode((node).argumentExpression, f, result); + result = reduceNode((node).expression, cbNode, result); + result = reduceNode((node).argumentExpression, cbNode, result); break; case SyntaxKind.CallExpression: - result = reduceNode((node).expression, f, result); - result = reduceLeft((node).typeArguments, f, result); - result = reduceLeft((node).arguments, f, result); + result = reduceNode((node).expression, cbNode, result); + result = reduceNodes((node).typeArguments, cbNodes, result); + result = reduceNodes((node).arguments, cbNodes, result); break; case SyntaxKind.NewExpression: - result = reduceNode((node).expression, f, result); - result = reduceLeft((node).typeArguments, f, result); - result = reduceLeft((node).arguments, f, result); + result = reduceNode((node).expression, cbNode, result); + result = reduceNodes((node).typeArguments, cbNodes, result); + result = reduceNodes((node).arguments, cbNodes, result); break; case SyntaxKind.TaggedTemplateExpression: - result = reduceNode((node).tag, f, result); - result = reduceNode((node).template, f, result); + result = reduceNode((node).tag, cbNode, result); + result = reduceNode((node).template, cbNode, result); break; case SyntaxKind.FunctionExpression: - result = reduceLeft((node).modifiers, f, result); - result = reduceNode((node).name, f, result); - result = reduceLeft((node).typeParameters, f, result); - result = reduceLeft((node).parameters, f, result); - result = reduceNode((node).type, f, result); - result = reduceNode((node).body, f, result); + result = reduceNodes((node).modifiers, cbNodes, result); + result = reduceNode((node).name, cbNode, result); + result = reduceNodes((node).typeParameters, cbNodes, result); + result = reduceNodes((node).parameters, cbNodes, result); + result = reduceNode((node).type, cbNode, result); + result = reduceNode((node).body, cbNode, result); break; case SyntaxKind.ArrowFunction: - result = reduceLeft((node).modifiers, f, result); - result = reduceLeft((node).typeParameters, f, result); - result = reduceLeft((node).parameters, f, result); - result = reduceNode((node).type, f, result); - result = reduceNode((node).body, f, result); + result = reduceNodes((node).modifiers, cbNodes, result); + result = reduceNodes((node).typeParameters, cbNodes, result); + result = reduceNodes((node).parameters, cbNodes, result); + result = reduceNode((node).type, cbNode, result); + result = reduceNode((node).body, cbNode, result); break; case SyntaxKind.ParenthesizedExpression: @@ -269,258 +275,258 @@ namespace ts { case SyntaxKind.YieldExpression: case SyntaxKind.SpreadElement: case SyntaxKind.NonNullExpression: - result = reduceNode((node).expression, f, result); + result = reduceNode((node).expression, cbNode, result); break; case SyntaxKind.PrefixUnaryExpression: case SyntaxKind.PostfixUnaryExpression: - result = reduceNode((node).operand, f, result); + result = reduceNode((node).operand, cbNode, result); break; case SyntaxKind.BinaryExpression: - result = reduceNode((node).left, f, result); - result = reduceNode((node).right, f, result); + result = reduceNode((node).left, cbNode, result); + result = reduceNode((node).right, cbNode, result); break; case SyntaxKind.ConditionalExpression: - result = reduceNode((node).condition, f, result); - result = reduceNode((node).whenTrue, f, result); - result = reduceNode((node).whenFalse, f, result); + result = reduceNode((node).condition, cbNode, result); + result = reduceNode((node).whenTrue, cbNode, result); + result = reduceNode((node).whenFalse, cbNode, result); break; case SyntaxKind.TemplateExpression: - result = reduceNode((node).head, f, result); - result = reduceLeft((node).templateSpans, f, result); + result = reduceNode((node).head, cbNode, result); + result = reduceNodes((node).templateSpans, cbNodes, result); break; case SyntaxKind.ClassExpression: - result = reduceLeft((node).modifiers, f, result); - result = reduceNode((node).name, f, result); - result = reduceLeft((node).typeParameters, f, result); - result = reduceLeft((node).heritageClauses, f, result); - result = reduceLeft((node).members, f, result); + result = reduceNodes((node).modifiers, cbNodes, result); + result = reduceNode((node).name, cbNode, result); + result = reduceNodes((node).typeParameters, cbNodes, result); + result = reduceNodes((node).heritageClauses, cbNodes, result); + result = reduceNodes((node).members, cbNodes, result); break; case SyntaxKind.ExpressionWithTypeArguments: - result = reduceNode((node).expression, f, result); - result = reduceLeft((node).typeArguments, f, result); + result = reduceNode((node).expression, cbNode, result); + result = reduceNodes((node).typeArguments, cbNodes, result); break; // Misc case SyntaxKind.TemplateSpan: - result = reduceNode((node).expression, f, result); - result = reduceNode((node).literal, f, result); + result = reduceNode((node).expression, cbNode, result); + result = reduceNode((node).literal, cbNode, result); break; // Element case SyntaxKind.Block: - result = reduceLeft((node).statements, f, result); + result = reduceNodes((node).statements, cbNodes, result); break; case SyntaxKind.VariableStatement: - result = reduceLeft((node).modifiers, f, result); - result = reduceNode((node).declarationList, f, result); + result = reduceNodes((node).modifiers, cbNodes, result); + result = reduceNode((node).declarationList, cbNode, result); break; case SyntaxKind.ExpressionStatement: - result = reduceNode((node).expression, f, result); + result = reduceNode((node).expression, cbNode, result); break; case SyntaxKind.IfStatement: - result = reduceNode((node).expression, f, result); - result = reduceNode((node).thenStatement, f, result); - result = reduceNode((node).elseStatement, f, result); + result = reduceNode((node).expression, cbNode, result); + result = reduceNode((node).thenStatement, cbNode, result); + result = reduceNode((node).elseStatement, cbNode, result); break; case SyntaxKind.DoStatement: - result = reduceNode((node).statement, f, result); - result = reduceNode((node).expression, f, result); + result = reduceNode((node).statement, cbNode, result); + result = reduceNode((node).expression, cbNode, result); break; case SyntaxKind.WhileStatement: case SyntaxKind.WithStatement: - result = reduceNode((node).expression, f, result); - result = reduceNode((node).statement, f, result); + result = reduceNode((node).expression, cbNode, result); + result = reduceNode((node).statement, cbNode, result); break; case SyntaxKind.ForStatement: - result = reduceNode((node).initializer, f, result); - result = reduceNode((node).condition, f, result); - result = reduceNode((node).incrementor, f, result); - result = reduceNode((node).statement, f, result); + result = reduceNode((node).initializer, cbNode, result); + result = reduceNode((node).condition, cbNode, result); + result = reduceNode((node).incrementor, cbNode, result); + result = reduceNode((node).statement, cbNode, result); break; case SyntaxKind.ForInStatement: case SyntaxKind.ForOfStatement: - result = reduceNode((node).initializer, f, result); - result = reduceNode((node).expression, f, result); - result = reduceNode((node).statement, f, result); + result = reduceNode((node).initializer, cbNode, result); + result = reduceNode((node).expression, cbNode, result); + result = reduceNode((node).statement, cbNode, result); break; case SyntaxKind.ReturnStatement: case SyntaxKind.ThrowStatement: - result = reduceNode((node).expression, f, result); + result = reduceNode((node).expression, cbNode, result); break; case SyntaxKind.SwitchStatement: - result = reduceNode((node).expression, f, result); - result = reduceNode((node).caseBlock, f, result); + result = reduceNode((node).expression, cbNode, result); + result = reduceNode((node).caseBlock, cbNode, result); break; case SyntaxKind.LabeledStatement: - result = reduceNode((node).label, f, result); - result = reduceNode((node).statement, f, result); + result = reduceNode((node).label, cbNode, result); + result = reduceNode((node).statement, cbNode, result); break; case SyntaxKind.TryStatement: - result = reduceNode((node).tryBlock, f, result); - result = reduceNode((node).catchClause, f, result); - result = reduceNode((node).finallyBlock, f, result); + result = reduceNode((node).tryBlock, cbNode, result); + result = reduceNode((node).catchClause, cbNode, result); + result = reduceNode((node).finallyBlock, cbNode, result); break; case SyntaxKind.VariableDeclaration: - result = reduceNode((node).name, f, result); - result = reduceNode((node).type, f, result); - result = reduceNode((node).initializer, f, result); + result = reduceNode((node).name, cbNode, result); + result = reduceNode((node).type, cbNode, result); + result = reduceNode((node).initializer, cbNode, result); break; case SyntaxKind.VariableDeclarationList: - result = reduceLeft((node).declarations, f, result); + result = reduceNodes((node).declarations, cbNodes, result); break; case SyntaxKind.FunctionDeclaration: - result = reduceLeft((node).decorators, f, result); - result = reduceLeft((node).modifiers, f, result); - result = reduceNode((node).name, f, result); - result = reduceLeft((node).typeParameters, f, result); - result = reduceLeft((node).parameters, f, result); - result = reduceNode((node).type, f, result); - result = reduceNode((node).body, f, result); + result = reduceNodes((node).decorators, cbNodes, result); + result = reduceNodes((node).modifiers, cbNodes, result); + result = reduceNode((node).name, cbNode, result); + result = reduceNodes((node).typeParameters, cbNodes, result); + result = reduceNodes((node).parameters, cbNodes, result); + result = reduceNode((node).type, cbNode, result); + result = reduceNode((node).body, cbNode, result); break; case SyntaxKind.ClassDeclaration: - result = reduceLeft((node).decorators, f, result); - result = reduceLeft((node).modifiers, f, result); - result = reduceNode((node).name, f, result); - result = reduceLeft((node).typeParameters, f, result); - result = reduceLeft((node).heritageClauses, f, result); - result = reduceLeft((node).members, f, result); + result = reduceNodes((node).decorators, cbNodes, result); + result = reduceNodes((node).modifiers, cbNodes, result); + result = reduceNode((node).name, cbNode, result); + result = reduceNodes((node).typeParameters, cbNodes, result); + result = reduceNodes((node).heritageClauses, cbNodes, result); + result = reduceNodes((node).members, cbNodes, result); break; case SyntaxKind.CaseBlock: - result = reduceLeft((node).clauses, f, result); + result = reduceNodes((node).clauses, cbNodes, result); break; case SyntaxKind.ImportDeclaration: - result = reduceLeft((node).decorators, f, result); - result = reduceLeft((node).modifiers, f, result); - result = reduceNode((node).importClause, f, result); - result = reduceNode((node).moduleSpecifier, f, result); + result = reduceNodes((node).decorators, cbNodes, result); + result = reduceNodes((node).modifiers, cbNodes, result); + result = reduceNode((node).importClause, cbNode, result); + result = reduceNode((node).moduleSpecifier, cbNode, result); break; case SyntaxKind.ImportClause: - result = reduceNode((node).name, f, result); - result = reduceNode((node).namedBindings, f, result); + result = reduceNode((node).name, cbNode, result); + result = reduceNode((node).namedBindings, cbNode, result); break; case SyntaxKind.NamespaceImport: - result = reduceNode((node).name, f, result); + result = reduceNode((node).name, cbNode, result); break; case SyntaxKind.NamedImports: case SyntaxKind.NamedExports: - result = reduceLeft((node).elements, f, result); + result = reduceNodes((node).elements, cbNodes, result); break; case SyntaxKind.ImportSpecifier: case SyntaxKind.ExportSpecifier: - result = reduceNode((node).propertyName, f, result); - result = reduceNode((node).name, f, result); + result = reduceNode((node).propertyName, cbNode, result); + result = reduceNode((node).name, cbNode, result); break; case SyntaxKind.ExportAssignment: - result = reduceLeft((node).decorators, f, result); - result = reduceLeft((node).modifiers, f, result); - result = reduceNode((node).expression, f, result); + result = reduceLeft((node).decorators, cbNode, result); + result = reduceLeft((node).modifiers, cbNode, result); + result = reduceNode((node).expression, cbNode, result); break; case SyntaxKind.ExportDeclaration: - result = reduceLeft((node).decorators, f, result); - result = reduceLeft((node).modifiers, f, result); - result = reduceNode((node).exportClause, f, result); - result = reduceNode((node).moduleSpecifier, f, result); + result = reduceLeft((node).decorators, cbNode, result); + result = reduceLeft((node).modifiers, cbNode, result); + result = reduceNode((node).exportClause, cbNode, result); + result = reduceNode((node).moduleSpecifier, cbNode, result); break; // JSX case SyntaxKind.JsxElement: - result = reduceNode((node).openingElement, f, result); - result = reduceLeft((node).children, f, result); - result = reduceNode((node).closingElement, f, result); + result = reduceNode((node).openingElement, cbNode, result); + result = reduceLeft((node).children, cbNode, result); + result = reduceNode((node).closingElement, cbNode, result); break; case SyntaxKind.JsxSelfClosingElement: case SyntaxKind.JsxOpeningElement: - result = reduceNode((node).tagName, f, result); - result = reduceLeft((node).attributes, f, result); + result = reduceNode((node).tagName, cbNode, result); + result = reduceNodes((node).attributes, cbNodes, result); break; case SyntaxKind.JsxClosingElement: - result = reduceNode((node).tagName, f, result); + result = reduceNode((node).tagName, cbNode, result); break; case SyntaxKind.JsxAttribute: - result = reduceNode((node).name, f, result); - result = reduceNode((node).initializer, f, result); + result = reduceNode((node).name, cbNode, result); + result = reduceNode((node).initializer, cbNode, result); break; case SyntaxKind.JsxSpreadAttribute: - result = reduceNode((node).expression, f, result); + result = reduceNode((node).expression, cbNode, result); break; case SyntaxKind.JsxExpression: - result = reduceNode((node).expression, f, result); + result = reduceNode((node).expression, cbNode, result); break; // Clauses case SyntaxKind.CaseClause: - result = reduceNode((node).expression, f, result); + result = reduceNode((node).expression, cbNode, result); // fall-through case SyntaxKind.DefaultClause: - result = reduceLeft((node).statements, f, result); + result = reduceNodes((node).statements, cbNodes, result); break; case SyntaxKind.HeritageClause: - result = reduceLeft((node).types, f, result); + result = reduceNodes((node).types, cbNodes, result); break; case SyntaxKind.CatchClause: - result = reduceNode((node).variableDeclaration, f, result); - result = reduceNode((node).block, f, result); + result = reduceNode((node).variableDeclaration, cbNode, result); + result = reduceNode((node).block, cbNode, result); break; // Property assignments case SyntaxKind.PropertyAssignment: - result = reduceNode((node).name, f, result); - result = reduceNode((node).initializer, f, result); + result = reduceNode((node).name, cbNode, result); + result = reduceNode((node).initializer, cbNode, result); break; case SyntaxKind.ShorthandPropertyAssignment: - result = reduceNode((node).name, f, result); - result = reduceNode((node).objectAssignmentInitializer, f, result); + result = reduceNode((node).name, cbNode, result); + result = reduceNode((node).objectAssignmentInitializer, cbNode, result); break; case SyntaxKind.SpreadAssignment: - result = reduceNode((node as SpreadAssignment).expression, f, result); + result = reduceNode((node as SpreadAssignment).expression, cbNode, result); break; // Top-level nodes case SyntaxKind.SourceFile: - result = reduceLeft((node).statements, f, result); + result = reduceNodes((node).statements, cbNodes, result); break; case SyntaxKind.PartiallyEmittedExpression: - result = reduceNode((node).expression, f, result); + result = reduceNode((node).expression, cbNode, result); break; default: @@ -530,8 +536,8 @@ namespace ts { const value = (>node)[edge.name]; if (value !== undefined) { result = isArray(value) - ? reduceLeft(>value, f, result) - : f(result, value); + ? reduceNodes(>value, cbNodes, result) + : cbNode(result, value); } } } @@ -553,8 +559,8 @@ namespace ts { export function visitNode(node: T, visitor: (node: Node) => VisitResult, test: (node: Node) => boolean, optional?: boolean, lift?: (node: NodeArray) => T): T; export function visitNode(node: T, visitor: (node: Node) => VisitResult, test: (node: Node) => boolean, optional: boolean, lift: (node: NodeArray) => T, parenthesize: (node: Node, parentNode: Node) => Node, parentNode: Node): T; export function visitNode(node: Node, visitor: (node: Node) => VisitResult, test: (node: Node) => boolean, optional?: boolean, lift?: (node: Node[]) => Node, parenthesize?: (node: Node, parentNode: Node) => Node, parentNode?: Node): Node { - if (node === undefined) { - return undefined; + if (node === undefined || visitor === undefined) { + return node; } aggregateTransformFlags(node); @@ -659,6 +665,30 @@ namespace ts { return updated || nodes; } + export function visitLexicalEnvironment(nodes: NodeArray, visitor: (node: Node) => VisitResult, context: LexicalEnvironment, start?: number) { + context.startLexicalEnvironment(); + const updated = visitNodes(nodes, visitor, isStatement, start); + const declarations = context.endLexicalEnvironment(); + return createNodeArray(concatenate(updated, declarations), updated); + } + + export function visitParameterList(nodes: NodeArray, visitor: (node: Node) => VisitResult, context: LexicalEnvironment) { + context.startLexicalEnvironment(); + return visitNodes(nodes, visitor, isParameter); + } + + export function visitFunctionBody(node: FunctionBody, visitor: (node: Node) => VisitResult, context: LexicalEnvironment): FunctionBody; + export function visitFunctionBody(node: ConciseBody, visitor: (node: Node) => VisitResult, context: LexicalEnvironment): ConciseBody; + export function visitFunctionBody(node: ConciseBody, visitor: (node: Node) => VisitResult, context: LexicalEnvironment) { + const visited = visitNode(node, visitor, isConciseBody); + const declarations = context.endLexicalEnvironment(); + if (some(declarations)) { + const block = convertToFunctionBody(visited); + return updateBlock(block, createNodeArray(concatenate(block.statements, declarations), block.statements)); + } + return visited; + } + /** * Visits each child of a Node using the supplied visitor, possibly returning a new Node of the same kind in its place. * @@ -721,41 +751,33 @@ namespace ts { visitNodes((node).modifiers, visitor, isModifier), visitNode((node).name, visitor, isPropertyName), visitNodes((node).typeParameters, visitor, isTypeParameter), - (context.startLexicalEnvironment(), visitNodes((node).parameters, visitor, isParameter)), + visitParameterList((node).parameters, visitor, context), visitNode((node).type, visitor, isTypeNode, /*optional*/ true), - mergeFunctionBodyLexicalEnvironment( - visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), - context.endLexicalEnvironment())); + visitFunctionBody((node).body, visitor, context)); case SyntaxKind.Constructor: return updateConstructor(node, visitNodes((node).decorators, visitor, isDecorator), visitNodes((node).modifiers, visitor, isModifier), - (context.startLexicalEnvironment(), visitNodes((node).parameters, visitor, isParameter)), - mergeFunctionBodyLexicalEnvironment( - visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), - context.endLexicalEnvironment())); + visitParameterList((node).parameters, visitor, context), + visitFunctionBody((node).body, visitor, context)); case SyntaxKind.GetAccessor: return updateGetAccessor(node, visitNodes((node).decorators, visitor, isDecorator), visitNodes((node).modifiers, visitor, isModifier), visitNode((node).name, visitor, isPropertyName), - (context.startLexicalEnvironment(), visitNodes((node).parameters, visitor, isParameter)), + visitParameterList((node).parameters, visitor, context), visitNode((node).type, visitor, isTypeNode, /*optional*/ true), - mergeFunctionBodyLexicalEnvironment( - visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), - context.endLexicalEnvironment())); + visitFunctionBody((node).body, visitor, context)); case SyntaxKind.SetAccessor: return updateSetAccessor(node, visitNodes((node).decorators, visitor, isDecorator), visitNodes((node).modifiers, visitor, isModifier), visitNode((node).name, visitor, isPropertyName), - (context.startLexicalEnvironment(), visitNodes((node).parameters, visitor, isParameter)), - mergeFunctionBodyLexicalEnvironment( - visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), - context.endLexicalEnvironment())); + visitParameterList((node).parameters, visitor, context), + visitFunctionBody((node).body, visitor, context)); // Binding patterns case SyntaxKind.ObjectBindingPattern: @@ -818,21 +840,17 @@ namespace ts { visitNodes((node).modifiers, visitor, isModifier), visitNode((node).name, visitor, isPropertyName), visitNodes((node).typeParameters, visitor, isTypeParameter), - (context.startLexicalEnvironment(), visitNodes((node).parameters, visitor, isParameter)), + visitParameterList((node).parameters, visitor, context), visitNode((node).type, visitor, isTypeNode, /*optional*/ true), - mergeFunctionBodyLexicalEnvironment( - visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), - context.endLexicalEnvironment())); + visitFunctionBody((node).body, visitor, context)); case SyntaxKind.ArrowFunction: return updateArrowFunction(node, visitNodes((node).modifiers, visitor, isModifier), visitNodes((node).typeParameters, visitor, isTypeParameter), - (context.startLexicalEnvironment(), visitNodes((node).parameters, visitor, isParameter)), + visitParameterList((node).parameters, visitor, context), visitNode((node).type, visitor, isTypeNode, /*optional*/ true), - mergeFunctionBodyLexicalEnvironment( - visitNode((node).body, visitor, isConciseBody, /*optional*/ true), - context.endLexicalEnvironment())); + visitFunctionBody((node).body, visitor, context)); case SyntaxKind.DeleteExpression: return updateDelete(node, @@ -1003,11 +1021,9 @@ namespace ts { visitNodes((node).modifiers, visitor, isModifier), visitNode((node).name, visitor, isPropertyName), visitNodes((node).typeParameters, visitor, isTypeParameter), - (context.startLexicalEnvironment(), visitNodes((node).parameters, visitor, isParameter)), + visitParameterList((node).parameters, visitor, context), visitNode((node).type, visitor, isTypeNode, /*optional*/ true), - mergeFunctionBodyLexicalEnvironment( - visitNode((node).body, visitor, isFunctionBody, /*optional*/ true), - context.endLexicalEnvironment())); + visitFunctionBody((node).body, visitor, context)); case SyntaxKind.ClassDeclaration: return updateClassDeclaration(node, @@ -1139,13 +1155,8 @@ namespace ts { // Top-level nodes case SyntaxKind.SourceFile: - context.startLexicalEnvironment(); return updateSourceFileNode(node, - createNodeArray( - concatenate( - visitNodes((node).statements, visitor, isStatement), - context.endLexicalEnvironment()), - (node).statements)); + visitLexicalEnvironment((node).statements, visitor, context)); // Transformation nodes case SyntaxKind.PartiallyEmittedExpression: @@ -1249,13 +1260,25 @@ namespace ts { if (node === undefined) { return TransformFlags.None; } - else if (node.transformFlags & TransformFlags.HasComputedFlags) { + if (node.transformFlags & TransformFlags.HasComputedFlags) { return node.transformFlags & ~getTransformFlagsSubtreeExclusions(node.kind); } - else { - const subtreeFlags = aggregateTransformFlagsForSubtree(node); - return computeTransformFlagsForNode(node, subtreeFlags); + const subtreeFlags = aggregateTransformFlagsForSubtree(node); + return computeTransformFlagsForNode(node, subtreeFlags); + } + + function aggregateTransformFlagsForNodeArray(nodes: NodeArray): TransformFlags { + if (nodes === undefined) { + return TransformFlags.None; } + let subtreeFlags = TransformFlags.None; + let nodeArrayFlags = TransformFlags.None; + for (const node of nodes) { + subtreeFlags |= aggregateTransformFlagsForNode(node); + nodeArrayFlags |= node.transformFlags & ~TransformFlags.HasComputedFlags; + } + nodes.transformFlags = nodeArrayFlags | TransformFlags.HasComputedFlags; + return subtreeFlags; } /** @@ -1269,15 +1292,19 @@ namespace ts { } // Aggregate the transform flags of each child. - return reduceEachChild(node, aggregateTransformFlagsForChildNode, TransformFlags.None); + return reduceEachChild(node, TransformFlags.None, aggregateTransformFlagsForChildNode, aggregateTransformFlagsForChildNodes); } /** * Aggregates the TransformFlags of a child node with the TransformFlags of its * siblings. */ - function aggregateTransformFlagsForChildNode(transformFlags: TransformFlags, child: Node): TransformFlags { - return transformFlags | aggregateTransformFlagsForNode(child); + function aggregateTransformFlagsForChildNode(transformFlags: TransformFlags, node: Node): TransformFlags { + return transformFlags | aggregateTransformFlagsForNode(node); + } + + function aggregateTransformFlagsForChildNodes(transformFlags: TransformFlags, nodes: NodeArray): TransformFlags { + return transformFlags | aggregateTransformFlagsForNodeArray(nodes); } export namespace Debug { diff --git a/tests/baselines/reference/assignmentTypeNarrowing.js b/tests/baselines/reference/assignmentTypeNarrowing.js index 7c10dde65cc..92fd49d9941 100644 --- a/tests/baselines/reference/assignmentTypeNarrowing.js +++ b/tests/baselines/reference/assignmentTypeNarrowing.js @@ -37,17 +37,17 @@ x = [true][0]; x; // boolean _a = [1][0], x = _a === void 0 ? "" : _a; x; // string | number -(_b = { x: true }, x = _b.x, _b); +(x = { x: true }.x); x; // boolean -(_c = { y: 1 }, x = _c.y, _c); +(x = { y: 1 }.y); x; // number -(_d = { x: true }, _e = _d.x, x = _e === void 0 ? "" : _e, _d); +(_b = { x: true }.x, x = _b === void 0 ? "" : _b); x; // string | boolean -(_f = { y: 1 }, _g = _f.y, x = _g === void 0 ? /a/ : _g, _f); +(_c = { y: 1 }.y, x = _c === void 0 ? /a/ : _c); x; // number | RegExp var a; for (var _i = 0, a_1 = a; _i < a_1.length; _i++) { x = a_1[_i]; x; // string } -var _a, _b, _c, _d, _e, _f, _g; +var _a, _b, _c; diff --git a/tests/baselines/reference/asyncMethodWithSuper_es5.js b/tests/baselines/reference/asyncMethodWithSuper_es5.js index 9ceff6bab4e..235f2763f9f 100644 --- a/tests/baselines/reference/asyncMethodWithSuper_es5.js +++ b/tests/baselines/reference/asyncMethodWithSuper_es5.js @@ -81,8 +81,8 @@ var B = (function (_super) { // async method with assignment/destructuring on 'super' requires a binding B.prototype.advanced = function () { return __awaiter(this, void 0, void 0, function () { - var f, a, b, _a, _b; - return __generator(this, function (_c) { + var f, a, b; + return __generator(this, function (_a) { f = function () { }; // call with property access _super.prototype.x.call(this); @@ -95,9 +95,9 @@ var B = (function (_super) { // element access (assign) _super.prototype["x"] = f; // destructuring assign with property access - (_a = { f: f }, super.x = _a.f, _a); + (_super.prototype.x = { f: f }.f); // destructuring assign with element access - (_b = { f: f }, super["x"] = _b.f, _b); + (_super.prototype["x"] = { f: f }.f); return [2 /*return*/]; }); }); diff --git a/tests/baselines/reference/computedPropertiesInDestructuring1.js b/tests/baselines/reference/computedPropertiesInDestructuring1.js index 0bc4286ed7b..b0dc33a4b4e 100644 --- a/tests/baselines/reference/computedPropertiesInDestructuring1.js +++ b/tests/baselines/reference/computedPropertiesInDestructuring1.js @@ -65,11 +65,11 @@ function f5(_a) { var _f = foo(), bar6 = [{ bar: "bar" }][0][_f]; var _g = foo.toExponential(), bar7 = [{ bar: "bar" }][0][_g]; // destructuring assignment -(_h = { bar: "bar" }, _j = foo, bar = _h[_j], _h); -(_k = { bar: "bar" }, _l = "bar", bar2 = _k[_l], _k); -(_m = { bar: "bar" }, _o = foo2(), bar3 = _m[_o], _m); -_p = foo, bar4 = [{ bar: "bar" }][0][_p]; -_q = foo2(), bar5 = [{ bar: "bar" }][0][_q]; -_r = foo(), bar4 = [{ bar: "bar" }][0][_r]; -_s = (1 + {}), bar4 = [{ bar: "bar" }][0][_s]; -var _h, _j, _k, _l, _m, _o, _p, _q, _r, _s; +(_h = foo, bar = { bar: "bar" }[_h]); +(_j = "bar", bar2 = { bar: "bar" }[_j]); +(_k = foo2(), bar3 = { bar: "bar" }[_k]); +_l = foo, bar4 = [{ bar: "bar" }][0][_l]; +_m = foo2(), bar5 = [{ bar: "bar" }][0][_m]; +_o = foo(), bar4 = [{ bar: "bar" }][0][_o]; +_p = (1 + {}), bar4 = [{ bar: "bar" }][0][_p]; +var _h, _j, _k, _l, _m, _o, _p; diff --git a/tests/baselines/reference/declarationsAndAssignments.js b/tests/baselines/reference/declarationsAndAssignments.js index 018aa5522ca..3a2f9ef3b4e 100644 --- a/tests/baselines/reference/declarationsAndAssignments.js +++ b/tests/baselines/reference/declarationsAndAssignments.js @@ -300,8 +300,8 @@ function f18() { var a; var b; var aa; - (_a = { a: a, b: b }, a = _a.a, b = _a.b, _a); - (_b = { b: b, a: a }, a = _b.a, b = _b.b, _b); + (_a = { a: a, b: b }, a = _a.a, b = _a.b); + (_b = { b: b, a: a }, a = _b.a, b = _b.b); _c = [a, b], aa[0] = _c[0], b = _c[1]; _d = [b, a], a = _d[0], b = _d[1]; // Error _e = [2, "def"], _f = _e[0], a = _f === void 0 ? 1 : _f, _g = _e[1], b = _g === void 0 ? "abc" : _g; @@ -311,7 +311,7 @@ function f19() { var a, b; _a = [1, 2], a = _a[0], b = _a[1]; _b = [b, a], a = _b[0], b = _b[1]; - (_c = { b: b, a: a }, a = _c.a, b = _c.b, _c); + (_c = { b: b, a: a }, a = _c.a, b = _c.b); _d = [[2, 3]][0], _e = _d === void 0 ? [1, 2] : _d, a = _e[0], b = _e[1]; var x = (_f = [1, 2], a = _f[0], b = _f[1], _f); var _a, _b, _c, _d, _e, _f; diff --git a/tests/baselines/reference/destructuringAssignmentWithDefault.js b/tests/baselines/reference/destructuringAssignmentWithDefault.js index ce3837e1162..fe3ccdcee53 100644 --- a/tests/baselines/reference/destructuringAssignmentWithDefault.js +++ b/tests/baselines/reference/destructuringAssignmentWithDefault.js @@ -7,5 +7,5 @@ let x = 0; //// [destructuringAssignmentWithDefault.js] var a = {}; var x = 0; -(_a = a.x, x = _a === void 0 ? 1 : _a, a); +(_a = a.x, x = _a === void 0 ? 1 : _a); var _a; diff --git a/tests/baselines/reference/emptyAssignmentPatterns02_ES5.js b/tests/baselines/reference/emptyAssignmentPatterns02_ES5.js index 370e019104f..f5434d4a1c3 100644 --- a/tests/baselines/reference/emptyAssignmentPatterns02_ES5.js +++ b/tests/baselines/reference/emptyAssignmentPatterns02_ES5.js @@ -9,8 +9,8 @@ let x, y, z, a1, a2, a3; //// [emptyAssignmentPatterns02_ES5.js] var a; var x, y, z, a1, a2, a3; -(x = a.x, y = a.y, z = a.z, a); -(a1 = a[0], a2 = a[1], a3 = a[2], a); +(x = a.x, y = a.y, z = a.z); +(a1 = a[0], a2 = a[1], a3 = a[2]); //// [emptyAssignmentPatterns02_ES5.d.ts] diff --git a/tests/baselines/reference/emptyAssignmentPatterns04_ES5.js b/tests/baselines/reference/emptyAssignmentPatterns04_ES5.js index e6b3cc7e3f2..91559d18252 100644 --- a/tests/baselines/reference/emptyAssignmentPatterns04_ES5.js +++ b/tests/baselines/reference/emptyAssignmentPatterns04_ES5.js @@ -9,9 +9,8 @@ let x, y, z, a1, a2, a3; //// [emptyAssignmentPatterns04_ES5.js] var a; var x, y, z, a1, a2, a3; -(_a = a, x = _a.x, y = _a.y, z = _a.z, _a); -(_b = a, a1 = _b[0], a2 = _b[1], a3 = _b[2], _b); -var _a, _b; +(x = a.x, y = a.y, z = a.z); +(a1 = a[0], a2 = a[1], a3 = a[2]); //// [emptyAssignmentPatterns04_ES5.d.ts] diff --git a/tests/baselines/reference/initializePropertiesWithRenamedLet.js b/tests/baselines/reference/initializePropertiesWithRenamedLet.js index d53fed8c0f7..4abe488aa08 100644 --- a/tests/baselines/reference/initializePropertiesWithRenamedLet.js +++ b/tests/baselines/reference/initializePropertiesWithRenamedLet.js @@ -28,7 +28,6 @@ if (true) { var x_1 = { x: 0 }.x; var y_1 = { y: 0 }.y; var z_1; - (_a = { z: 0 }, z_1 = _a.z, _a); - (_b = { z: 0 }, z_1 = _b.z, _b); + (z_1 = { z: 0 }.z); + (z_1 = { z: 0 }.z); } -var _a, _b; diff --git a/tests/baselines/reference/missingAndExcessProperties.js b/tests/baselines/reference/missingAndExcessProperties.js index b9fcadd9519..daefe18eed7 100644 --- a/tests/baselines/reference/missingAndExcessProperties.js +++ b/tests/baselines/reference/missingAndExcessProperties.js @@ -45,10 +45,10 @@ function f1() { // Missing properties function f2() { var x, y; - (_a = {}, x = _a.x, y = _a.y, _a); - (_b = {}, _c = _b.x, x = _c === void 0 ? 1 : _c, y = _b.y, _b); - (_d = {}, x = _d.x, _e = _d.y, y = _e === void 0 ? 1 : _e, _d); - (_f = {}, _g = _f.x, x = _g === void 0 ? 1 : _g, _h = _f.y, y = _h === void 0 ? 1 : _h, _f); + (_a = {}, x = _a.x, y = _a.y); + (_b = {}, _c = _b.x, x = _c === void 0 ? 1 : _c, y = _b.y); + (_d = {}, x = _d.x, _e = _d.y, y = _e === void 0 ? 1 : _e); + (_f = {}, _g = _f.x, x = _g === void 0 ? 1 : _g, _h = _f.y, y = _h === void 0 ? 1 : _h); var _a, _b, _c, _d, _e, _f, _g, _h; } // Excess properties @@ -62,8 +62,8 @@ function f3() { function f4() { var x, y; ({ x: 0, y: 0 }); - (_a = { x: 0, y: 0 }, x = _a.x, _a); - (_b = { x: 0, y: 0 }, y = _b.y, _b); - (_c = { x: 0, y: 0 }, x = _c.x, y = _c.y, _c); - var _a, _b, _c; + (x = { x: 0, y: 0 }.x); + (y = { x: 0, y: 0 }.y); + (_a = { x: 0, y: 0 }, x = _a.x, y = _a.y); + var _a; } diff --git a/tests/baselines/reference/objectRest.js b/tests/baselines/reference/objectRest.js index a295ff7b9f1..519283b32e5 100644 --- a/tests/baselines/reference/objectRest.js +++ b/tests/baselines/reference/objectRest.js @@ -53,9 +53,9 @@ let nestedrest; var { x } = nestedrest, _a = nestedrest.n1, { y } = _a, _b = _a.n2, { z } = _b, nr = __rest(_b.n3, []), restrest = __rest(nestedrest, ["x", "n1"]); let complex; var _c = complex.x, { ka } = _c, nested = __rest(_c, ["ka"]), { y: other } = complex, rest = __rest(complex, ["x", "y"]); -(_d = complex.x, { ka } = _d, nested = __rest(_d, ["ka"]), { y: other } = complex, rest = __rest(complex, ["x", "y"]), complex); +(_d = complex.x, { ka } = _d, nested = __rest(_d, ["ka"]), { y: other } = complex, rest = __rest(complex, ["x", "y"])); var _e = { x: 1, y: 2 }, { x } = _e, fresh = __rest(_e, ["x"]); -(_f = { x: 1, y: 2 }, { x } = _f, fresh = __rest(_f, ["x"]), _f); +(_f = { x: 1, y: 2 }, { x } = _f, fresh = __rest(_f, ["x"])); class Removable { set z(value) { } get both() { return 12; } diff --git a/tests/baselines/reference/objectRestAssignment.js b/tests/baselines/reference/objectRestAssignment.js index 41620d59b58..d4d266d3fe5 100644 --- a/tests/baselines/reference/objectRestAssignment.js +++ b/tests/baselines/reference/objectRestAssignment.js @@ -26,10 +26,10 @@ let nested; let other; let rest; let complex; -(_a = complex.x, { ka } = _a, nested = __rest(_a, ["ka"]), { y: other } = complex, rest = __rest(complex, ["x", "y"]), complex); +(_a = complex.x, { ka } = _a, nested = __rest(_a, ["ka"]), { y: other } = complex, rest = __rest(complex, ["x", "y"])); // should be: let overEmit; // var _g = overEmit.a, [_h, ...y] = _g, nested2 = __rest(_h, []), _j = overEmit.b, { z } = _j, c = __rest(_j, ["z"]), rest2 = __rest(overEmit, ["a", "b"]); var _b = overEmit.a, [_c, ...y] = _b, nested2 = __rest(_c, []), _d = overEmit.b, { z } = _d, c = __rest(_d, ["z"]), rest2 = __rest(overEmit, ["a", "b"]); -(_e = overEmit.a, [_f, ...y] = _e, nested2 = __rest(_f, []), _g = overEmit.b, { z } = _g, c = __rest(_g, ["z"]), rest2 = __rest(overEmit, ["a", "b"]), overEmit); -var _a, _e, _f, _g; +(_e = overEmit.a, [_f, ...y] = _e, nested2 = __rest(_f, []), _g = overEmit.b, { z } = _g, c = __rest(_g, ["z"]), rest2 = __rest(overEmit, ["a", "b"])); +var _a, _e, _g; diff --git a/tests/baselines/reference/objectRestNegative.js b/tests/baselines/reference/objectRestNegative.js index b3b49740762..6a710a22870 100644 --- a/tests/baselines/reference/objectRestNegative.js +++ b/tests/baselines/reference/objectRestNegative.js @@ -17,9 +17,9 @@ var __rest = (this && this.__rest) || function (s, e) { return t; }; var o = { a: 1, b: 'no' }; -var mustBeLast = o.mustBeLast, a = o.a; +var a = o.a; function stillMustBeLast(_a) { - var mustBeLast = _a.mustBeLast, a = _a.a; + var a = _a.a; } function generic(t) { var x = t.x, rest = __rest(t, ["x"]); diff --git a/tests/baselines/reference/shorthandPropertyAssignmentsInDestructuring.js b/tests/baselines/reference/shorthandPropertyAssignmentsInDestructuring.js index 981ee08e20c..6be02246e39 100644 --- a/tests/baselines/reference/shorthandPropertyAssignmentsInDestructuring.js +++ b/tests/baselines/reference/shorthandPropertyAssignmentsInDestructuring.js @@ -176,63 +176,63 @@ function foo({a = 4, b = { x: 5 }}) { }); (function () { var y; - (_a = { y: 1 }, _b = _a.y, y = _b === void 0 ? 5 : _b, _a); - var _a, _b; + (_a = { y: 1 }.y, y = _a === void 0 ? 5 : _a); + var _a; }); (function () { var y; - (_a = { y: 1 }, _b = _a.y, y = _b === void 0 ? 5 : _b, _a); - var _a, _b; + (_a = { y: 1 }.y, y = _a === void 0 ? 5 : _a); + var _a; }); (function () { var y0; - (_a = { y0: 1 }, _b = _a.y0, y0 = _b === void 0 ? 5 : _b, _a); - var _a, _b; + (_a = { y0: 1 }.y0, y0 = _a === void 0 ? 5 : _a); + var _a; }); (function () { var y0; - (_a = { y0: 1 }, _b = _a.y0, y0 = _b === void 0 ? 5 : _b, _a); - var _a, _b; + (_a = { y0: 1 }.y0, y0 = _a === void 0 ? 5 : _a); + var _a; }); (function () { var y1; - (_a = {}, _b = _a.y1, y1 = _b === void 0 ? 5 : _b, _a); - var _a, _b; + (_a = {}.y1, y1 = _a === void 0 ? 5 : _a); + var _a; }); (function () { var y1; - (_a = {}, _b = _a.y1, y1 = _b === void 0 ? 5 : _b, _a); - var _a, _b; + (_a = {}.y1, y1 = _a === void 0 ? 5 : _a); + var _a; }); (function () { var y2, y3; - (_a = {}, _b = _a.y2, y2 = _b === void 0 ? 5 : _b, _c = _a.y3, y3 = _c === void 0 ? { x: 1 } : _c, _a); + (_a = {}, _b = _a.y2, y2 = _b === void 0 ? 5 : _b, _c = _a.y3, y3 = _c === void 0 ? { x: 1 } : _c); var _a, _b, _c; }); (function () { var y2, y3; - (_a = {}, _b = _a.y2, y2 = _b === void 0 ? 5 : _b, _c = _a.y3, y3 = _c === void 0 ? { x: 1 } : _c, _a); + (_a = {}, _b = _a.y2, y2 = _b === void 0 ? 5 : _b, _c = _a.y3, y3 = _c === void 0 ? { x: 1 } : _c); var _a, _b, _c; }); (function () { var y4, y5; - (_a = {}, _b = _a.y4, y4 = _b === void 0 ? 5 : _b, _c = _a.y5, y5 = _c === void 0 ? { x: 1 } : _c, _a); + (_a = {}, _b = _a.y4, y4 = _b === void 0 ? 5 : _b, _c = _a.y5, y5 = _c === void 0 ? { x: 1 } : _c); var _a, _b, _c; }); (function () { var y4, y5; - (_a = {}, _b = _a.y4, y4 = _b === void 0 ? 5 : _b, _c = _a.y5, y5 = _c === void 0 ? { x: 1 } : _c, _a); + (_a = {}, _b = _a.y4, y4 = _b === void 0 ? 5 : _b, _c = _a.y5, y5 = _c === void 0 ? { x: 1 } : _c); var _a, _b, _c; }); (function () { var z; - (_a = { z: { x: 1 } }, _b = _a.z, z = _b === void 0 ? { x: 5 } : _b, _a); - var _a, _b; + (_a = { z: { x: 1 } }.z, z = _a === void 0 ? { x: 5 } : _a); + var _a; }); (function () { var z; - (_a = { z: { x: 1 } }, _b = _a.z, z = _b === void 0 ? { x: 5 } : _b, _a); - var _a, _b; + (_a = { z: { x: 1 } }.z, z = _a === void 0 ? { x: 5 } : _a); + var _a; }); (function () { var a = { s: s };