From 867f554a9dadc711b8506807b17833010e3fe800 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 14 Sep 2015 18:41:58 -0700 Subject: [PATCH] Partial cleanup to remove "transform" namespace. --- scripts/processTypes.ts | 30 +- src/compiler/transform.generated.ts | 981 +++++----- src/compiler/transform.ts | 306 ++-- src/compiler/transforms/chain.ts | 30 +- src/compiler/transforms/es5.ts | 1019 ++++++----- src/compiler/transforms/es6.ts | 2640 ++++++++++++++------------- src/compiler/types.ts | 9 - src/compiler/utilities.ts | 11 + 8 files changed, 2481 insertions(+), 2545 deletions(-) diff --git a/scripts/processTypes.ts b/scripts/processTypes.ts index da72de66693..b01b0193590 100644 --- a/scripts/processTypes.ts +++ b/scripts/processTypes.ts @@ -779,7 +779,7 @@ function generateTransform(outputFile: string) { writer.writeLine(); writer.write(`/* @internal */`); writer.writeLine(); - writer.write(`namespace ts.transform {`); + writer.write(`namespace ts {`); writer.writeLine(); writer.increaseIndent(); writeAcceptFunction(); @@ -790,7 +790,7 @@ function generateTransform(outputFile: string) { sys.writeFile(outputFile, writer.getText()); function writeAcceptFunction() { - writer.write(`export function accept(node: Node, visitor: (input: Node, write: (node: Node) => void) => void, write: (node: Node) => void): void {`); + writer.write(`export function accept(transformer: Transformer, node: Node, pipeline: Pipeline, write: PipelineOutput): void {`); writer.writeLine(); writer.increaseIndent(); @@ -805,6 +805,9 @@ function generateTransform(outputFile: string) { writer.write(`}`); writer.writeLine(); + writer.write(`let { visitNode, visitNodes } = transformer;`); + writer.writeLine(); + writer.write(`switch (node.kind) {`); writer.writeLine(); writer.increaseIndent(); @@ -821,7 +824,6 @@ function generateTransform(outputFile: string) { let updateFunctionName = getUpdateFunctionName(syntaxNode); writer.write(`return write(${updateFunctionName}(`); - writer.writeLine(); writer.increaseIndent(); writer.write(`<${syntaxNode.typeName}>node`); @@ -837,13 +839,27 @@ function generateTransform(outputFile: string) { writer.write(`(<${syntaxNode.typeName}>node).${member.propertyName}`); } else { + let isStatement = member.typeName === "Statement"; let visitorFunction = member.visitorFunction ? member.visitorFunction : - member.isNodeArray || member.isModifiersArray ? `>visitNodes` : - member.startsNewLexicalEnvironment ? `<${member.typeName}>visitNewLexicalEnvironment` : - `<${member.typeName}>visitNode`; + member.isNodeArray || member.isModifiersArray ? `visitNodes` : + `visitNode`; - writer.write(`${visitorFunction}((<${syntaxNode.typeName}>node).${member.propertyName}, visitor)`); + writer.write(`${visitorFunction}(`); + if (member.visitorFunction) { + writer.write(`transformer, `); + } + + writer.write(`(<${syntaxNode.typeName}>node).${member.propertyName}, pipeline`); + + if (isStatement) { + writer.write(`, PipelineFlags.StatementOrBlock`); + } + else if (member.startsNewLexicalEnvironment) { + writer.write(`, PipelineFlags.LexicalEnvironment`); + } + + writer.write(`)`); } } diff --git a/src/compiler/transform.generated.ts b/src/compiler/transform.generated.ts index 30ba1c6c669..7686261699d 100644 --- a/src/compiler/transform.generated.ts +++ b/src/compiler/transform.generated.ts @@ -2,693 +2,566 @@ /// /// /* @internal */ -namespace ts.transform { - export function accept(node: Node, visitor: (input: Node, write: (node: Node) => void) => void, write: (node: Node) => void): void { +namespace ts { + export function accept(transformer: Transformer, node: Node, pipeline: Pipeline, write: PipelineOutput): void { if (!node) { return; } + let { visitNode, visitNodes } = transformer; switch (node.kind) { case SyntaxKind.QualifiedName: - return write(updateQualifiedName( - node, - visitNode((node).left, visitor), - visitNode((node).right, visitor))); + return write(updateQualifiedName(node, + visitNode((node).left, pipeline), + visitNode((node).right, pipeline))); case SyntaxKind.ComputedPropertyName: - return write(updateComputedPropertyName( - node, - visitNode((node).expression, visitor))); + return write(updateComputedPropertyName(node, + visitNode((node).expression, pipeline))); case SyntaxKind.TypeParameter: - return write(updateTypeParameter( - node, - visitNode((node).name, visitor), - visitNode((node).constraint, visitor), - visitNode((node).expression, visitor))); + return write(updateTypeParameter(node, + visitNode((node).name, pipeline), + visitNode((node).constraint, pipeline), + visitNode((node).expression, pipeline))); case SyntaxKind.Parameter: - return write(updateParameter( - node, - >visitNodes((node).decorators, visitor), - >visitNodes((node).modifiers, visitor), - visitNode((node).name, visitor), + return write(updateParameter(node, + visitNodes((node).decorators, pipeline), + visitNodes((node).modifiers, pipeline), + visitNode((node).name, pipeline), (node).questionToken, - visitNode((node).type, visitor), - visitNode((node).initializer, visitor))); + visitNode((node).type, pipeline), + visitNode((node).initializer, pipeline))); case SyntaxKind.Decorator: - return write(updateDecorator( - node, - visitNode((node).expression, visitor))); + return write(updateDecorator(node, + visitNode((node).expression, pipeline))); case SyntaxKind.PropertySignature: - return write(updatePropertySignature( - node, - >visitNodes((node).decorators, visitor), - >visitNodes((node).modifiers, visitor), - visitNode((node).name, visitor), - visitNode((node).type, visitor))); + return write(updatePropertySignature(node, + visitNodes((node).decorators, pipeline), + visitNodes((node).modifiers, pipeline), + visitNode((node).name, pipeline), + visitNode((node).type, pipeline))); case SyntaxKind.PropertyDeclaration: - return write(updatePropertyDeclaration( - node, - >visitNodes((node).decorators, visitor), - >visitNodes((node).modifiers, visitor), - visitNode((node).name, visitor), - visitNode((node).type, visitor), - visitNode((node).initializer, visitor))); + return write(updatePropertyDeclaration(node, + visitNodes((node).decorators, pipeline), + visitNodes((node).modifiers, pipeline), + visitNode((node).name, pipeline), + visitNode((node).type, pipeline), + visitNode((node).initializer, pipeline))); case SyntaxKind.MethodSignature: - return write(updateMethodSignature( - node, - >visitNodes((node).decorators, visitor), - >visitNodes((node).modifiers, visitor), - visitNode((node).name, visitor), - >visitNodes((node).typeParameters, visitor), - >visitNodes((node).parameters, visitor), - visitNode((node).type, visitor))); + return write(updateMethodSignature(node, + visitNodes((node).decorators, pipeline), + visitNodes((node).modifiers, pipeline), + visitNode((node).name, pipeline), + visitNodes((node).typeParameters, pipeline), + visitNodes((node).parameters, pipeline), + visitNode((node).type, pipeline))); case SyntaxKind.MethodDeclaration: - return write(updateMethodDeclaration( - node, - >visitNodes((node).decorators, visitor), - >visitNodes((node).modifiers, visitor), - visitNode((node).name, visitor), - >visitNodes((node).typeParameters, visitor), - >visitNodes((node).parameters, visitor), - visitNode((node).type, visitor), - visitNewLexicalEnvironment((node).body, visitor))); + return write(updateMethodDeclaration(node, + visitNodes((node).decorators, pipeline), + visitNodes((node).modifiers, pipeline), + visitNode((node).name, pipeline), + visitNodes((node).typeParameters, pipeline), + visitNodes((node).parameters, pipeline), + visitNode((node).type, pipeline), + visitNode((node).body, pipeline, PipelineFlags.LexicalEnvironment))); case SyntaxKind.Constructor: - return write(updateConstructor( - node, - >visitNodes((node).decorators, visitor), - >visitNodes((node).modifiers, visitor), - >visitNodes((node).parameters, visitor), - visitNode((node).type, visitor), - visitNewLexicalEnvironment((node).body, visitor))); + return write(updateConstructor(node, + visitNodes((node).decorators, pipeline), + visitNodes((node).modifiers, pipeline), + visitNodes((node).parameters, pipeline), + visitNode((node).type, pipeline), + visitNode((node).body, pipeline, PipelineFlags.LexicalEnvironment))); case SyntaxKind.GetAccessor: - return write(updateGetAccessor( - node, - >visitNodes((node).decorators, visitor), - >visitNodes((node).modifiers, visitor), - visitNode((node).name, visitor), - >visitNodes((node).parameters, visitor), - visitNode((node).type, visitor), - visitNewLexicalEnvironment((node).body, visitor))); + return write(updateGetAccessor(node, + visitNodes((node).decorators, pipeline), + visitNodes((node).modifiers, pipeline), + visitNode((node).name, pipeline), + visitNodes((node).parameters, pipeline), + visitNode((node).type, pipeline), + visitNode((node).body, pipeline, PipelineFlags.LexicalEnvironment))); case SyntaxKind.SetAccessor: - return write(updateSetAccessor( - node, - >visitNodes((node).decorators, visitor), - >visitNodes((node).modifiers, visitor), - visitNode((node).name, visitor), - >visitNodes((node).parameters, visitor), - visitNode((node).type, visitor), - visitNewLexicalEnvironment((node).body, visitor))); + return write(updateSetAccessor(node, + visitNodes((node).decorators, pipeline), + visitNodes((node).modifiers, pipeline), + visitNode((node).name, pipeline), + visitNodes((node).parameters, pipeline), + visitNode((node).type, pipeline), + visitNode((node).body, pipeline, PipelineFlags.LexicalEnvironment))); case SyntaxKind.CallSignature: - return write(updateCallSignature( - node, - >visitNodes((node).typeParameters, visitor), - >visitNodes((node).parameters, visitor), - visitNode((node).type, visitor))); + return write(updateCallSignature(node, + visitNodes((node).typeParameters, pipeline), + visitNodes((node).parameters, pipeline), + visitNode((node).type, pipeline))); case SyntaxKind.ConstructSignature: - return write(updateConstructSignature( - node, - >visitNodes((node).typeParameters, visitor), - >visitNodes((node).parameters, visitor), - visitNode((node).type, visitor))); + return write(updateConstructSignature(node, + visitNodes((node).typeParameters, pipeline), + visitNodes((node).parameters, pipeline), + visitNode((node).type, pipeline))); case SyntaxKind.IndexSignature: - return write(updateIndexSignature( - node, - >visitNodes((node).decorators, visitor), - >visitNodes((node).modifiers, visitor), - >visitNodes((node).parameters, visitor), - visitNode((node).type, visitor))); + return write(updateIndexSignature(node, + visitNodes((node).decorators, pipeline), + visitNodes((node).modifiers, pipeline), + visitNodes((node).parameters, pipeline), + visitNode((node).type, pipeline))); case SyntaxKind.TypePredicate: - return write(updateTypePredicate( - node, - visitNode((node).parameterName, visitor), - visitNode((node).type, visitor))); + return write(updateTypePredicate(node, + visitNode((node).parameterName, pipeline), + visitNode((node).type, pipeline))); case SyntaxKind.TypeReference: - return write(updateTypeReference( - node, - visitNode((node).typeName, visitor), - >visitNodes((node).typeArguments, visitor))); + return write(updateTypeReference(node, + visitNode((node).typeName, pipeline), + visitNodes((node).typeArguments, pipeline))); case SyntaxKind.FunctionType: - return write(updateFunctionType( - node, - >visitNodes((node).typeParameters, visitor), - >visitNodes((node).parameters, visitor), - visitNode((node).type, visitor))); + return write(updateFunctionType(node, + visitNodes((node).typeParameters, pipeline), + visitNodes((node).parameters, pipeline), + visitNode((node).type, pipeline))); case SyntaxKind.ConstructorType: - return write(updateConstructorType( - node, - >visitNodes((node).typeParameters, visitor), - >visitNodes((node).parameters, visitor), - visitNode((node).type, visitor))); + return write(updateConstructorType(node, + visitNodes((node).typeParameters, pipeline), + visitNodes((node).parameters, pipeline), + visitNode((node).type, pipeline))); case SyntaxKind.TypeQuery: - return write(updateTypeQuery( - node, - visitNode((node).exprName, visitor))); + return write(updateTypeQuery(node, + visitNode((node).exprName, pipeline))); case SyntaxKind.TypeLiteral: - return write(updateTypeLiteral( - node, - >visitNodes((node).members, visitor))); + return write(updateTypeLiteral(node, + visitNodes((node).members, pipeline))); case SyntaxKind.ArrayType: - return write(updateArrayType( - node, - visitNode((node).elementType, visitor))); + return write(updateArrayType(node, + visitNode((node).elementType, pipeline))); case SyntaxKind.TupleType: - return write(updateTupleType( - node, - >visitNodes((node).elementTypes, visitor))); + return write(updateTupleType(node, + visitNodes((node).elementTypes, pipeline))); case SyntaxKind.UnionType: - return write(updateUnionType( - node, - >visitNodes((node).types, visitor))); + return write(updateUnionType(node, + visitNodes((node).types, pipeline))); case SyntaxKind.IntersectionType: - return write(updateIntersectionType( - node, - >visitNodes((node).types, visitor))); + return write(updateIntersectionType(node, + visitNodes((node).types, pipeline))); case SyntaxKind.ParenthesizedType: - return write(updateParenthesizedType( - node, - visitNode((node).type, visitor))); + return write(updateParenthesizedType(node, + visitNode((node).type, pipeline))); case SyntaxKind.ObjectBindingPattern: - return write(updateObjectBindingPattern( - node, - >visitNodes((node).elements, visitor))); + return write(updateObjectBindingPattern(node, + visitNodes((node).elements, pipeline))); case SyntaxKind.ArrayBindingPattern: - return write(updateArrayBindingPattern( - node, - >visitNodes((node).elements, visitor))); + return write(updateArrayBindingPattern(node, + visitNodes((node).elements, pipeline))); case SyntaxKind.BindingElement: - return write(updateBindingElement( - node, - >visitNodes((node).decorators, visitor), - >visitNodes((node).modifiers, visitor), - visitNode((node).propertyName, visitor), - visitNode((node).name, visitor), - visitNode((node).initializer, visitor))); + return write(updateBindingElement(node, + visitNodes((node).decorators, pipeline), + visitNodes((node).modifiers, pipeline), + visitNode((node).propertyName, pipeline), + visitNode((node).name, pipeline), + visitNode((node).initializer, pipeline))); case SyntaxKind.ArrayLiteralExpression: - return write(updateArrayLiteralExpression( - node, - >visitNodes((node).elements, visitor))); + return write(updateArrayLiteralExpression(node, + visitNodes((node).elements, pipeline))); case SyntaxKind.ObjectLiteralExpression: - return write(updateObjectLiteralExpression( - node, - >visitNodes((node).decorators, visitor), - >visitNodes((node).modifiers, visitor), - >visitNodes((node).properties, visitor))); + return write(updateObjectLiteralExpression(node, + visitNodes((node).decorators, pipeline), + visitNodes((node).modifiers, pipeline), + visitNodes((node).properties, pipeline))); case SyntaxKind.PropertyAccessExpression: - return write(updatePropertyAccessExpression( - node, - visitNode((node).expression, visitor), - visitNode((node).name, visitor))); + return write(updatePropertyAccessExpression(node, + visitNode((node).expression, pipeline), + visitNode((node).name, pipeline))); case SyntaxKind.ElementAccessExpression: - return write(updateElementAccessExpression( - node, - visitNode((node).expression, visitor), - visitNode((node).argumentExpression, visitor))); + return write(updateElementAccessExpression(node, + visitNode((node).expression, pipeline), + visitNode((node).argumentExpression, pipeline))); case SyntaxKind.CallExpression: - return write(updateCallExpression( - node, - visitNode((node).expression, visitor), - >visitNodes((node).typeArguments, visitor), - >visitNodes((node).arguments, visitor))); + return write(updateCallExpression(node, + visitNode((node).expression, pipeline), + visitNodes((node).typeArguments, pipeline), + visitNodes((node).arguments, pipeline))); case SyntaxKind.NewExpression: - return write(updateNewExpression( - node, - visitNode((node).expression, visitor), - >visitNodes((node).typeArguments, visitor), - >visitNodes((node).arguments, visitor))); + return write(updateNewExpression(node, + visitNode((node).expression, pipeline), + visitNodes((node).typeArguments, pipeline), + visitNodes((node).arguments, pipeline))); case SyntaxKind.TaggedTemplateExpression: - return write(updateTaggedTemplateExpression( - node, - visitNode((node).tag, visitor), - visitNode((node).template, visitor))); + return write(updateTaggedTemplateExpression(node, + visitNode((node).tag, pipeline), + visitNode((node).template, pipeline))); case SyntaxKind.TypeAssertionExpression: - return write(updateTypeAssertionExpression( - node, - visitNode((node).type, visitor), - visitNode((node).expression, visitor))); + return write(updateTypeAssertionExpression(node, + visitNode((node).type, pipeline), + visitNode((node).expression, pipeline))); case SyntaxKind.ParenthesizedExpression: - return write(updateParenthesizedExpression( - node, - visitNode((node).expression, visitor))); + return write(updateParenthesizedExpression(node, + visitNode((node).expression, pipeline))); case SyntaxKind.FunctionExpression: - return write(updateFunctionExpression( - node, - >visitNodes((node).decorators, visitor), - >visitNodes((node).modifiers, visitor), - visitNode((node).name, visitor), - >visitNodes((node).typeParameters, visitor), - >visitNodes((node).parameters, visitor), - visitNode((node).type, visitor), - visitNewLexicalEnvironment((node).body, visitor))); + return write(updateFunctionExpression(node, + visitNodes((node).decorators, pipeline), + visitNodes((node).modifiers, pipeline), + visitNode((node).name, pipeline), + visitNodes((node).typeParameters, pipeline), + visitNodes((node).parameters, pipeline), + visitNode((node).type, pipeline), + visitNode((node).body, pipeline, PipelineFlags.LexicalEnvironment))); case SyntaxKind.ArrowFunction: - return write(updateArrowFunction( - node, - >visitNodes((node).decorators, visitor), - >visitNodes((node).modifiers, visitor), - >visitNodes((node).typeParameters, visitor), - >visitNodes((node).parameters, visitor), - visitNode((node).type, visitor), - visitNewLexicalEnvironment((node).body, visitor))); + return write(updateArrowFunction(node, + visitNodes((node).decorators, pipeline), + visitNodes((node).modifiers, pipeline), + visitNodes((node).typeParameters, pipeline), + visitNodes((node).parameters, pipeline), + visitNode((node).type, pipeline), + visitNode((node).body, pipeline, PipelineFlags.LexicalEnvironment))); case SyntaxKind.DeleteExpression: - return write(updateDeleteExpression( - node, - visitNode((node).expression, visitor))); + return write(updateDeleteExpression(node, + visitNode((node).expression, pipeline))); case SyntaxKind.TypeOfExpression: - return write(updateTypeOfExpression( - node, - visitNode((node).expression, visitor))); + return write(updateTypeOfExpression(node, + visitNode((node).expression, pipeline))); case SyntaxKind.VoidExpression: - return write(updateVoidExpression( - node, - visitNode((node).expression, visitor))); + return write(updateVoidExpression(node, + visitNode((node).expression, pipeline))); case SyntaxKind.AwaitExpression: - return write(updateAwaitExpression( - node, - visitNode((node).expression, visitor))); + return write(updateAwaitExpression(node, + visitNode((node).expression, pipeline))); case SyntaxKind.PrefixUnaryExpression: - return write(updatePrefixUnaryExpression( - node, - visitNode((node).operand, visitor))); + return write(updatePrefixUnaryExpression(node, + visitNode((node).operand, pipeline))); case SyntaxKind.PostfixUnaryExpression: - return write(updatePostfixUnaryExpression( - node, - visitNode((node).operand, visitor))); + return write(updatePostfixUnaryExpression(node, + visitNode((node).operand, pipeline))); case SyntaxKind.BinaryExpression: - return write(updateBinaryExpression( - node, - visitNode((node).left, visitor), - visitNode((node).right, visitor))); + return write(updateBinaryExpression(node, + visitNode((node).left, pipeline), + visitNode((node).right, pipeline))); case SyntaxKind.ConditionalExpression: - return write(updateConditionalExpression( - node, - visitNode((node).condition, visitor), - visitNode((node).whenTrue, visitor), - visitNode((node).whenFalse, visitor))); + return write(updateConditionalExpression(node, + visitNode((node).condition, pipeline), + visitNode((node).whenTrue, pipeline), + visitNode((node).whenFalse, pipeline))); case SyntaxKind.TemplateExpression: - return write(updateTemplateExpression( - node, - visitNode((node).head, visitor), - >visitNodes((node).templateSpans, visitor))); + return write(updateTemplateExpression(node, + visitNode((node).head, pipeline), + visitNodes((node).templateSpans, pipeline))); case SyntaxKind.YieldExpression: - return write(updateYieldExpression( - node, - visitNode((node).expression, visitor))); + return write(updateYieldExpression(node, + visitNode((node).expression, pipeline))); case SyntaxKind.SpreadElementExpression: - return write(updateSpreadElementExpression( - node, - visitNode((node).expression, visitor))); + return write(updateSpreadElementExpression(node, + visitNode((node).expression, pipeline))); case SyntaxKind.ClassExpression: - return write(updateClassExpression( - node, - >visitNodes((node).decorators, visitor), - >visitNodes((node).modifiers, visitor), - visitNode((node).name, visitor), - >visitNodes((node).typeParameters, visitor), - >visitNodes((node).heritageClauses, visitor), - >visitNodes((node).members, visitor))); + return write(updateClassExpression(node, + visitNodes((node).decorators, pipeline), + visitNodes((node).modifiers, pipeline), + visitNode((node).name, pipeline), + visitNodes((node).typeParameters, pipeline), + visitNodes((node).heritageClauses, pipeline), + visitNodes((node).members, pipeline))); case SyntaxKind.ExpressionWithTypeArguments: - return write(updateExpressionWithTypeArguments( - node, - visitNode((node).expression, visitor), - >visitNodes((node).typeArguments, visitor))); + return write(updateExpressionWithTypeArguments(node, + visitNode((node).expression, pipeline), + visitNodes((node).typeArguments, pipeline))); case SyntaxKind.AsExpression: - return write(updateAsExpression( - node, - visitNode((node).expression, visitor), - visitNode((node).type, visitor))); + return write(updateAsExpression(node, + visitNode((node).expression, pipeline), + visitNode((node).type, pipeline))); case SyntaxKind.TemplateSpan: - return write(updateTemplateSpan( - node, - visitNode((node).expression, visitor), - visitNode((node).literal, visitor))); + return write(updateTemplateSpan(node, + visitNode((node).expression, pipeline), + visitNode((node).literal, pipeline))); case SyntaxKind.Block: - return write(updateBlock( - node, - >visitNodes((node).statements, visitor))); + return write(updateBlock(node, + visitNodes((node).statements, pipeline))); case SyntaxKind.VariableStatement: - return write(updateVariableStatement( - node, - >visitNodes((node).decorators, visitor), - >visitNodes((node).modifiers, visitor), - visitNode((node).declarationList, visitor))); + return write(updateVariableStatement(node, + visitNodes((node).decorators, pipeline), + visitNodes((node).modifiers, pipeline), + visitNode((node).declarationList, pipeline))); case SyntaxKind.ExpressionStatement: - return write(updateExpressionStatement( - node, - visitNode((node).expression, visitor))); + return write(updateExpressionStatement(node, + visitNode((node).expression, pipeline))); case SyntaxKind.IfStatement: - return write(updateIfStatement( - node, - visitNode((node).expression, visitor), - visitStatement((node).thenStatement, visitor), - visitStatement((node).elseStatement, visitor))); + return write(updateIfStatement(node, + visitNode((node).expression, pipeline), + visitNode((node).thenStatement, pipeline, PipelineFlags.StatementOrBlock), + visitNode((node).elseStatement, pipeline, PipelineFlags.StatementOrBlock))); case SyntaxKind.DoStatement: - return write(updateDoStatement( - node, - visitStatement((node).statement, visitor), - visitNode((node).expression, visitor))); + return write(updateDoStatement(node, + visitNode((node).statement, pipeline, PipelineFlags.StatementOrBlock), + visitNode((node).expression, pipeline))); case SyntaxKind.WhileStatement: - return write(updateWhileStatement( - node, - visitNode((node).expression, visitor), - visitStatement((node).statement, visitor))); + return write(updateWhileStatement(node, + visitNode((node).expression, pipeline), + visitNode((node).statement, pipeline, PipelineFlags.StatementOrBlock))); case SyntaxKind.ForStatement: - return write(updateForStatement( - node, - visitNode((node).initializer, visitor), - visitNode((node).condition, visitor), - visitNode((node).incrementor, visitor), - visitStatement((node).statement, visitor))); + return write(updateForStatement(node, + visitNode((node).initializer, pipeline), + visitNode((node).condition, pipeline), + visitNode((node).incrementor, pipeline), + visitNode((node).statement, pipeline, PipelineFlags.StatementOrBlock))); case SyntaxKind.ForInStatement: - return write(updateForInStatement( - node, - visitNode((node).initializer, visitor), - visitNode((node).expression, visitor), - visitStatement((node).statement, visitor))); + return write(updateForInStatement(node, + visitNode((node).initializer, pipeline), + visitNode((node).expression, pipeline), + visitNode((node).statement, pipeline, PipelineFlags.StatementOrBlock))); case SyntaxKind.ForOfStatement: - return write(updateForOfStatement( - node, - visitNode((node).initializer, visitor), - visitNode((node).expression, visitor), - visitStatement((node).statement, visitor))); + return write(updateForOfStatement(node, + visitNode((node).initializer, pipeline), + visitNode((node).expression, pipeline), + visitNode((node).statement, pipeline, PipelineFlags.StatementOrBlock))); case SyntaxKind.ContinueStatement: - return write(updateContinueStatement( - node, - visitNode((node).label, visitor))); + return write(updateContinueStatement(node, + visitNode((node).label, pipeline))); case SyntaxKind.BreakStatement: - return write(updateBreakStatement( - node, - visitNode((node).label, visitor))); + return write(updateBreakStatement(node, + visitNode((node).label, pipeline))); case SyntaxKind.ReturnStatement: - return write(updateReturnStatement( - node, - visitNode((node).expression, visitor))); + return write(updateReturnStatement(node, + visitNode((node).expression, pipeline))); case SyntaxKind.WithStatement: - return write(updateWithStatement( - node, - visitNode((node).expression, visitor), - visitStatement((node).statement, visitor))); + return write(updateWithStatement(node, + visitNode((node).expression, pipeline), + visitNode((node).statement, pipeline, PipelineFlags.StatementOrBlock))); case SyntaxKind.SwitchStatement: - return write(updateSwitchStatement( - node, - visitNode((node).expression, visitor), - visitNode((node).caseBlock, visitor))); + return write(updateSwitchStatement(node, + visitNode((node).expression, pipeline), + visitNode((node).caseBlock, pipeline))); case SyntaxKind.LabeledStatement: - return write(updateLabeledStatement( - node, - visitNode((node).label, visitor), - visitStatement((node).statement, visitor))); + return write(updateLabeledStatement(node, + visitNode((node).label, pipeline), + visitNode((node).statement, pipeline, PipelineFlags.StatementOrBlock))); case SyntaxKind.ThrowStatement: - return write(updateThrowStatement( - node, - visitNode((node).expression, visitor))); + return write(updateThrowStatement(node, + visitNode((node).expression, pipeline))); case SyntaxKind.TryStatement: - return write(updateTryStatement( - node, - visitNode((node).tryBlock, visitor), - visitNode((node).catchClause, visitor), - visitNode((node).finallyBlock, visitor))); + return write(updateTryStatement(node, + visitNode((node).tryBlock, pipeline), + visitNode((node).catchClause, pipeline), + visitNode((node).finallyBlock, pipeline))); case SyntaxKind.VariableDeclaration: - return write(updateVariableDeclaration( - node, - visitNode((node).name, visitor), - visitNode((node).type, visitor), - visitNode((node).initializer, visitor))); + return write(updateVariableDeclaration(node, + visitNode((node).name, pipeline), + visitNode((node).type, pipeline), + visitNode((node).initializer, pipeline))); case SyntaxKind.VariableDeclarationList: - return write(updateVariableDeclarationList( - node, - >visitNodes((node).declarations, visitor))); + return write(updateVariableDeclarationList(node, + visitNodes((node).declarations, pipeline))); case SyntaxKind.FunctionDeclaration: - return write(updateFunctionDeclaration( - node, - >visitNodes((node).decorators, visitor), - >visitNodes((node).modifiers, visitor), - visitNode((node).name, visitor), - >visitNodes((node).typeParameters, visitor), - >visitNodes((node).parameters, visitor), - visitNode((node).type, visitor), - visitNewLexicalEnvironment((node).body, visitor))); + return write(updateFunctionDeclaration(node, + visitNodes((node).decorators, pipeline), + visitNodes((node).modifiers, pipeline), + visitNode((node).name, pipeline), + visitNodes((node).typeParameters, pipeline), + visitNodes((node).parameters, pipeline), + visitNode((node).type, pipeline), + visitNode((node).body, pipeline, PipelineFlags.LexicalEnvironment))); case SyntaxKind.ClassDeclaration: - return write(updateClassDeclaration( - node, - >visitNodes((node).decorators, visitor), - >visitNodes((node).modifiers, visitor), - visitNode((node).name, visitor), - >visitNodes((node).typeParameters, visitor), - >visitNodes((node).heritageClauses, visitor), - >visitNodes((node).members, visitor))); + return write(updateClassDeclaration(node, + visitNodes((node).decorators, pipeline), + visitNodes((node).modifiers, pipeline), + visitNode((node).name, pipeline), + visitNodes((node).typeParameters, pipeline), + visitNodes((node).heritageClauses, pipeline), + visitNodes((node).members, pipeline))); case SyntaxKind.InterfaceDeclaration: - return write(updateInterfaceDeclaration( - node, - >visitNodes((node).decorators, visitor), - >visitNodes((node).modifiers, visitor), - visitNode((node).name, visitor), - >visitNodes((node).typeParameters, visitor), - >visitNodes((node).heritageClauses, visitor), - >visitNodes((node).members, visitor))); + return write(updateInterfaceDeclaration(node, + visitNodes((node).decorators, pipeline), + visitNodes((node).modifiers, pipeline), + visitNode((node).name, pipeline), + visitNodes((node).typeParameters, pipeline), + visitNodes((node).heritageClauses, pipeline), + visitNodes((node).members, pipeline))); case SyntaxKind.TypeAliasDeclaration: - return write(updateTypeAliasDeclaration( - node, - >visitNodes((node).decorators, visitor), - >visitNodes((node).modifiers, visitor), - visitNode((node).name, visitor), - >visitNodes((node).typeParameters, visitor), - visitNode((node).type, visitor))); + return write(updateTypeAliasDeclaration(node, + visitNodes((node).decorators, pipeline), + visitNodes((node).modifiers, pipeline), + visitNode((node).name, pipeline), + visitNodes((node).typeParameters, pipeline), + visitNode((node).type, pipeline))); case SyntaxKind.EnumDeclaration: - return write(updateEnumDeclaration( - node, - >visitNodes((node).decorators, visitor), - >visitNodes((node).modifiers, visitor), - visitNode((node).name, visitor), - >visitNodes((node).members, visitor))); + return write(updateEnumDeclaration(node, + visitNodes((node).decorators, pipeline), + visitNodes((node).modifiers, pipeline), + visitNode((node).name, pipeline), + visitNodes((node).members, pipeline))); case SyntaxKind.ModuleDeclaration: - return write(updateModuleDeclaration( - node, - >visitNodes((node).decorators, visitor), - >visitNodes((node).modifiers, visitor), - visitNode((node).name, visitor), - visitNewLexicalEnvironment((node).body, visitor))); + return write(updateModuleDeclaration(node, + visitNodes((node).decorators, pipeline), + visitNodes((node).modifiers, pipeline), + visitNode((node).name, pipeline), + visitNode((node).body, pipeline, PipelineFlags.LexicalEnvironment))); case SyntaxKind.ModuleBlock: - return write(updateModuleBlock( - node, - >visitNodes((node).statements, visitor))); + return write(updateModuleBlock(node, + visitNodes((node).statements, pipeline))); case SyntaxKind.CaseBlock: - return write(updateCaseBlock( - node, - >visitNodes((node).clauses, visitor))); + return write(updateCaseBlock(node, + visitNodes((node).clauses, pipeline))); case SyntaxKind.ImportEqualsDeclaration: - return write(updateImportEqualsDeclaration( - node, - >visitNodes((node).decorators, visitor), - >visitNodes((node).modifiers, visitor), - visitNode((node).name, visitor), - visitNode((node).moduleReference, visitor))); + return write(updateImportEqualsDeclaration(node, + visitNodes((node).decorators, pipeline), + visitNodes((node).modifiers, pipeline), + visitNode((node).name, pipeline), + visitNode((node).moduleReference, pipeline))); case SyntaxKind.ImportDeclaration: - return write(updateImportDeclaration( - node, - >visitNodes((node).decorators, visitor), - >visitNodes((node).modifiers, visitor), - visitNode((node).importClause, visitor), - visitNode((node).moduleSpecifier, visitor))); + return write(updateImportDeclaration(node, + visitNodes((node).decorators, pipeline), + visitNodes((node).modifiers, pipeline), + visitNode((node).importClause, pipeline), + visitNode((node).moduleSpecifier, pipeline))); case SyntaxKind.ImportClause: - return write(updateImportClause( - node, - visitNode((node).name, visitor), - visitNode((node).namedBindings, visitor))); + return write(updateImportClause(node, + visitNode((node).name, pipeline), + visitNode((node).namedBindings, pipeline))); case SyntaxKind.NamespaceImport: - return write(updateNamespaceImport( - node, - visitNode((node).name, visitor))); + return write(updateNamespaceImport(node, + visitNode((node).name, pipeline))); case SyntaxKind.NamedImports: - return write(updateNamedImports( - node, - >visitNodes((node).elements, visitor))); + return write(updateNamedImports(node, + visitNodes((node).elements, pipeline))); case SyntaxKind.ImportSpecifier: - return write(updateImportSpecifier( - node, - visitNode((node).propertyName, visitor), - visitNode((node).name, visitor))); + return write(updateImportSpecifier(node, + visitNode((node).propertyName, pipeline), + visitNode((node).name, pipeline))); case SyntaxKind.ExportAssignment: - return write(updateExportAssignment( - node, - >visitNodes((node).decorators, visitor), - >visitNodes((node).modifiers, visitor), - visitNode((node).expression, visitor))); + return write(updateExportAssignment(node, + visitNodes((node).decorators, pipeline), + visitNodes((node).modifiers, pipeline), + visitNode((node).expression, pipeline))); case SyntaxKind.ExportDeclaration: - return write(updateExportDeclaration( - node, - >visitNodes((node).decorators, visitor), - >visitNodes((node).modifiers, visitor), - visitNode((node).exportClause, visitor), - visitNode((node).moduleSpecifier, visitor))); + return write(updateExportDeclaration(node, + visitNodes((node).decorators, pipeline), + visitNodes((node).modifiers, pipeline), + visitNode((node).exportClause, pipeline), + visitNode((node).moduleSpecifier, pipeline))); case SyntaxKind.NamedExports: - return write(updateNamedExports( - node, - >visitNodes((node).elements, visitor))); + return write(updateNamedExports(node, + visitNodes((node).elements, pipeline))); case SyntaxKind.ExportSpecifier: - return write(updateExportSpecifier( - node, - visitNode((node).propertyName, visitor), - visitNode((node).name, visitor))); + return write(updateExportSpecifier(node, + visitNode((node).propertyName, pipeline), + visitNode((node).name, pipeline))); case SyntaxKind.MissingDeclaration: - return write(updateMissingDeclaration( - node, - >visitNodes((node).decorators, visitor), - >visitNodes((node).modifiers, visitor))); + return write(updateMissingDeclaration(node, + visitNodes((node).decorators, pipeline), + visitNodes((node).modifiers, pipeline))); case SyntaxKind.ExternalModuleReference: - return write(updateExternalModuleReference( - node, - visitNode((node).expression, visitor))); + return write(updateExternalModuleReference(node, + visitNode((node).expression, pipeline))); case SyntaxKind.JsxElement: - return write(updateJsxElement( - node, - visitNode((node).openingElement, visitor), - >visitNodes((node).children, visitor), - visitNode((node).closingElement, visitor))); + return write(updateJsxElement(node, + visitNode((node).openingElement, pipeline), + visitNodes((node).children, pipeline), + visitNode((node).closingElement, pipeline))); case SyntaxKind.JsxSelfClosingElement: - return write(updateJsxSelfClosingElement( - node, - visitNode((node).tagName, visitor), - >visitNodes((node).attributes, visitor))); + return write(updateJsxSelfClosingElement(node, + visitNode((node).tagName, pipeline), + visitNodes((node).attributes, pipeline))); case SyntaxKind.JsxOpeningElement: - return write(updateJsxOpeningElement( - node, - visitNode((node).tagName, visitor), - >visitNodes((node).attributes, visitor))); + return write(updateJsxOpeningElement(node, + visitNode((node).tagName, pipeline), + visitNodes((node).attributes, pipeline))); case SyntaxKind.JsxClosingElement: - return write(updateJsxClosingElement( - node, - visitNode((node).tagName, visitor))); + return write(updateJsxClosingElement(node, + visitNode((node).tagName, pipeline))); case SyntaxKind.JsxAttribute: - return write(updateJsxAttribute( - node, - visitNode((node).name, visitor), - visitNode((node).initializer, visitor))); + return write(updateJsxAttribute(node, + visitNode((node).name, pipeline), + visitNode((node).initializer, pipeline))); case SyntaxKind.JsxSpreadAttribute: - return write(updateJsxSpreadAttribute( - node, - visitNode((node).expression, visitor))); + return write(updateJsxSpreadAttribute(node, + visitNode((node).expression, pipeline))); case SyntaxKind.JsxExpression: - return write(updateJsxExpression( - node, - visitNode((node).expression, visitor))); + return write(updateJsxExpression(node, + visitNode((node).expression, pipeline))); case SyntaxKind.CaseClause: - return write(updateCaseClause( - node, - visitNode((node).expression, visitor), - >visitNodes((node).statements, visitor))); + return write(updateCaseClause(node, + visitNode((node).expression, pipeline), + visitNodes((node).statements, pipeline))); case SyntaxKind.DefaultClause: - return write(updateDefaultClause( - node, - >visitNodes((node).statements, visitor))); + return write(updateDefaultClause(node, + visitNodes((node).statements, pipeline))); case SyntaxKind.HeritageClause: - return write(updateHeritageClause( - node, - >visitNodes((node).types, visitor))); + return write(updateHeritageClause(node, + visitNodes((node).types, pipeline))); case SyntaxKind.CatchClause: - return write(updateCatchClause( - node, - visitNode((node).variableDeclaration, visitor), - visitNode((node).block, visitor))); + return write(updateCatchClause(node, + visitNode((node).variableDeclaration, pipeline), + visitNode((node).block, pipeline))); case SyntaxKind.PropertyAssignment: - return write(updatePropertyAssignment( - node, - visitNode((node).name, visitor), - visitNode((node).initializer, visitor))); + return write(updatePropertyAssignment(node, + visitNode((node).name, pipeline), + visitNode((node).initializer, pipeline))); case SyntaxKind.ShorthandPropertyAssignment: - return write(updateShorthandPropertyAssignment( - node, - visitNode((node).name, visitor))); + return write(updateShorthandPropertyAssignment(node, + visitNode((node).name, pipeline))); case SyntaxKind.EnumMember: - return write(updateEnumMember( - node, - visitNode((node).name, visitor), - visitNode((node).initializer, visitor))); + return write(updateEnumMember(node, + visitNode((node).name, pipeline), + visitNode((node).initializer, pipeline))); case SyntaxKind.JSDocTypeExpression: - return write(updateJSDocTypeExpression( - node, - visitNode((node).type, visitor))); + return write(updateJSDocTypeExpression(node, + visitNode((node).type, pipeline))); case SyntaxKind.JSDocArrayType: - return write(updateJSDocArrayType( - node, - visitNode((node).elementType, visitor))); + return write(updateJSDocArrayType(node, + visitNode((node).elementType, pipeline))); case SyntaxKind.JSDocUnionType: - return write(updateJSDocUnionType( - node, - >visitNodes((node).types, visitor))); + return write(updateJSDocUnionType(node, + visitNodes((node).types, pipeline))); case SyntaxKind.JSDocTupleType: - return write(updateJSDocTupleType( - node, - >visitNodes((node).types, visitor))); + return write(updateJSDocTupleType(node, + visitNodes((node).types, pipeline))); case SyntaxKind.JSDocNullableType: - return write(updateJSDocNullableType( - node, - visitNode((node).type, visitor))); + return write(updateJSDocNullableType(node, + visitNode((node).type, pipeline))); case SyntaxKind.JSDocNonNullableType: - return write(updateJSDocNonNullableType( - node, - visitNode((node).type, visitor))); + return write(updateJSDocNonNullableType(node, + visitNode((node).type, pipeline))); case SyntaxKind.JSDocRecordType: - return write(updateJSDocRecordType( - node, - >visitNodes((node).members, visitor))); + return write(updateJSDocRecordType(node, + visitNodes((node).members, pipeline))); case SyntaxKind.JSDocRecordMember: - return write(updateJSDocRecordMember( - node, - visitNode((node).name, visitor), - visitNode((node).type, visitor))); + return write(updateJSDocRecordMember(node, + visitNode((node).name, pipeline), + visitNode((node).type, pipeline))); case SyntaxKind.JSDocTypeReference: - return write(updateJSDocTypeReference( - node, - visitNode((node).name, visitor), - >visitNodes((node).typeArguments, visitor))); + return write(updateJSDocTypeReference(node, + visitNode((node).name, pipeline), + visitNodes((node).typeArguments, pipeline))); case SyntaxKind.JSDocOptionalType: - return write(updateJSDocOptionalType( - node, - visitNode((node).type, visitor))); + return write(updateJSDocOptionalType(node, + visitNode((node).type, pipeline))); case SyntaxKind.JSDocFunctionType: - return write(updateJSDocFunctionType( - node, - >visitNodes((node).parameters, visitor), - visitNode((node).type, visitor))); + return write(updateJSDocFunctionType(node, + visitNodes((node).parameters, pipeline), + visitNode((node).type, pipeline))); case SyntaxKind.JSDocVariadicType: - return write(updateJSDocVariadicType( - node, - visitNode((node).type, visitor))); + return write(updateJSDocVariadicType(node, + visitNode((node).type, pipeline))); case SyntaxKind.JSDocConstructorType: - return write(updateJSDocConstructorType( - node, - visitNode((node).type, visitor))); + return write(updateJSDocConstructorType(node, + visitNode((node).type, pipeline))); case SyntaxKind.JSDocThisType: - return write(updateJSDocThisType( - node, - visitNode((node).type, visitor))); + return write(updateJSDocThisType(node, + visitNode((node).type, pipeline))); case SyntaxKind.JSDocComment: - return write(updateJSDocComment( - node, - >visitNodes((node).tags, visitor))); + return write(updateJSDocComment(node, + visitNodes((node).tags, pipeline))); case SyntaxKind.JSDocTag: - return write(updateJSDocTag( - node, - visitNode((node).tagName, visitor))); + return write(updateJSDocTag(node, + visitNode((node).tagName, pipeline))); case SyntaxKind.JSDocParameterTag: - return write(updateJSDocParameterTag( - node, - visitNode((node).preParameterName, visitor), - visitNode((node).typeExpression, visitor), - visitNode((node).postParameterName, visitor), - visitNode((node).tagName, visitor))); + return write(updateJSDocParameterTag(node, + visitNode((node).preParameterName, pipeline), + visitNode((node).typeExpression, pipeline), + visitNode((node).postParameterName, pipeline), + visitNode((node).tagName, pipeline))); case SyntaxKind.JSDocReturnTag: - return write(updateJSDocReturnTag( - node, - visitNode((node).typeExpression, visitor), - visitNode((node).tagName, visitor))); + return write(updateJSDocReturnTag(node, + visitNode((node).typeExpression, pipeline), + visitNode((node).tagName, pipeline))); case SyntaxKind.JSDocTypeTag: - return write(updateJSDocTypeTag( - node, - visitNode((node).typeExpression, visitor), - visitNode((node).tagName, visitor))); + return write(updateJSDocTypeTag(node, + visitNode((node).typeExpression, pipeline), + visitNode((node).tagName, pipeline))); case SyntaxKind.JSDocTemplateTag: - return write(updateJSDocTemplateTag( - node, - >visitNodes((node).typeParameters, visitor), - visitNode((node).tagName, visitor))); + return write(updateJSDocTemplateTag(node, + visitNodes((node).typeParameters, pipeline), + visitNode((node).tagName, pipeline))); default: return write(node); } diff --git a/src/compiler/transform.ts b/src/compiler/transform.ts index 593be59c8b9..b0ef922fc2f 100644 --- a/src/compiler/transform.ts +++ b/src/compiler/transform.ts @@ -9,6 +9,7 @@ namespace ts { export type PipelineOutput = (node: TOut) => void; export type Pipeline = (input: TIn, output: PipelineOutput, offset?: number) => void; export type NodeTest = (node: Node) => node is T; + export type TransformationChain = (transformer: Transformer, statements: NodeArray) => NodeArray; /** * Computes the transform flags for a node, given the transform flags of its subtree @@ -256,7 +257,7 @@ namespace ts { return node.transformFlags = subtreeFlags; } - export function runTransformationChain(statements: NodeArray, chain: (statements: NodeArray) => NodeArray, + export function runTransformationChain(statements: NodeArray, chain: TransformationChain, _compilerOptions: CompilerOptions, _currentSourceFile: SourceFile, _resolver: EmitResolver, _generatedNameSet: Map, _nodeToGeneratedName: string[]) { return transform.runTransformationChain(statements, chain, _compilerOptions, _currentSourceFile, _resolver, _generatedNameSet, _nodeToGeneratedName); } @@ -264,43 +265,44 @@ namespace ts { export const enum PipelineFlags { LexicalEnvironment = 1 << 1, StatementOrBlock = 1 << 2, - //ExpressionOrBlock = 1 << 3, + ExpressionOrBlock = 1 << 3, } - // interface Transformer { - // getEmitResolver(): EmitResolver; - // getCompilerOptions(): CompilerOptions; - // makeUniqueName(baseName: string): string; - // getGeneratedNameForNode(node: Node): Identifier; - // nodeHasGeneratedName(node: Node): boolean; - // createUniqueIdentifier(baseName: string): Identifier; - // createTempVariable(loopVariable: boolean): Identifier; - // declareLocal(baseName?: string): Identifier; - // hoistVariableDeclaration(name: Identifier): void; - // hoistFunctionDeclaration(func: FunctionDeclaration): void; - // createParentNavigator(): ParentNavigator; - // getParentNode(): Node; - // getCurrentNode(): Node; - // findAncestorNode(match: (node: Node) => node is T): T; - // findAncestorNode(match: (node: Node) => boolean): Node; - // getDeclarationName(node: DeclarationStatement): Identifier; - // getDeclarationName(node: ClassExpression): Identifier; - // getDeclarationName(node: Declaration): DeclarationName; - // getClassMemberPrefix(node: ClassLikeDeclaration, member: ClassElement): Expression; - // pipeNode(input: TIn, pipeline: Pipeline, output: PipelineOutput, flags?: PipelineFlags): void; - // pipeNodes(input: TIn[], pipeline: Pipeline, output: PipelineOutput, flags?: PipelineFlags): void; - // emitNode(input: TIn, pipeline: Pipeline, output: TOut[], flags?: PipelineFlags, nodeTest?: NodeTest): void; - // emitNodes(input: TIn[], pipeline: Pipeline, output: TOut[], flags?: PipelineFlags, nodeTest?: NodeTest): void; - // visitNode(input: T, pipeline: Pipeline, flags?: PipelineFlags, nodeTest?: NodeTest): T; - // visitNode(input: T, pipeline: Pipeline, flags?: PipelineFlags, nodeTest?: NodeTest): T; - // visitNode(input: TIn, pipeline: Pipeline, flags?: PipelineFlags, nodeTest?: NodeTest): TOut; - // visitNodes(input: T[], pipeline: Pipeline, flags?: PipelineFlags, nodeTest?: NodeTest): NodeArray; - // visitNodes(input: T[], pipeline: Pipeline, flags?: PipelineFlags, nodeTest?: NodeTest): NodeArray; - // visitNodes(input: TIn[], pipeline: Pipeline, flags?: PipelineFlags, nodeTest?: NodeTest): NodeArray; - // accept(node: T, pipeline: Pipeline, write: PipelineOutput): void; - // } + export interface Transformer { + getEmitResolver(): EmitResolver; + getCompilerOptions(): CompilerOptions; + makeUniqueName(baseName: string): string; + getGeneratedNameForNode(node: Node): Identifier; + nodeHasGeneratedName(node: Node): boolean; + createUniqueIdentifier(baseName: string): Identifier; + createTempVariable(loopVariable: boolean): Identifier; + declareLocal(baseName?: string): Identifier; + hoistVariableDeclaration(name: Identifier): void; + hoistFunctionDeclaration(func: FunctionDeclaration): void; + createParentNavigator(): ParentNavigator; + getRootNode(): SourceFile; + getParentNode(): Node; + getCurrentNode(): Node; + findAncestorNode(match: (node: Node) => node is T): T; + findAncestorNode(match: (node: Node) => boolean): Node; + getDeclarationName(node: DeclarationStatement): Identifier; + getDeclarationName(node: ClassLikeDeclaration): Identifier; + getDeclarationName(node: Declaration): DeclarationName; + getClassMemberPrefix(node: ClassLikeDeclaration, member: ClassElement): Expression; + pipeNode(input: TIn, pipeline: Pipeline, output: PipelineOutput, flags?: PipelineFlags): void; + pipeNodes(input: TIn[], pipeline: Pipeline, output: PipelineOutput, flags?: PipelineFlags): void; + emitNode(input: TIn, pipeline: Pipeline, output: TOut[], flags?: PipelineFlags, nodeTest?: NodeTest): void; + emitNodes(input: TIn[], pipeline: Pipeline, output: TOut[], flags?: PipelineFlags, nodeTest?: NodeTest): void; + visitNode(input: T, pipeline: Pipeline, flags?: PipelineFlags, nodeTest?: NodeTest): T; + visitNode(input: T, pipeline: Pipeline, flags?: PipelineFlags, nodeTest?: NodeTest): T; + visitNode(input: TIn, pipeline: Pipeline, flags?: PipelineFlags, nodeTest?: NodeTest): TOut; + visitNodes(input: T[], pipeline: Pipeline, flags?: PipelineFlags, nodeTest?: NodeTest): NodeArray; + visitNodes(input: T[], pipeline: Pipeline, flags?: PipelineFlags, nodeTest?: NodeTest): NodeArray; + visitNodes(input: TIn[], pipeline: Pipeline, flags?: PipelineFlags, nodeTest?: NodeTest): NodeArray; + accept(node: T, pipeline: Pipeline, write: PipelineOutput): void; + } - export namespace transform { + namespace transform { // Flags enum to track count of temp variables and a few dedicated names const enum TempFlags { Auto = 0x00000000, // No preferred name @@ -333,15 +335,47 @@ namespace ts { let writeNodeWithOrWithoutNodeTest: (node: Node) => void; let writeNodeFastOrSlow: (node: Node) => void; - export function runTransformationChain(statements: NodeArray, chain: (statements: NodeArray) => NodeArray, + export function runTransformationChain(statements: NodeArray, chain: TransformationChain, _compilerOptions: CompilerOptions, _currentSourceFile: SourceFile, _resolver: EmitResolver, _generatedNameSet: Map, _nodeToGeneratedName: string[]) { Debug.assert(!transformationRunning, "Transformation already running"); + initializeTransformation(_compilerOptions, _currentSourceFile, _resolver, _generatedNameSet, _nodeToGeneratedName); - let result = chain(statements); + + let transformer: Transformer = { + getEmitResolver, + getCompilerOptions, + makeUniqueName, + getGeneratedNameForNode, + nodeHasGeneratedName, + createUniqueIdentifier, + createTempVariable, + declareLocal, + hoistVariableDeclaration, + hoistFunctionDeclaration, + createParentNavigator, + getRootNode, + getParentNode, + getCurrentNode, + findAncestorNode, + getDeclarationName, + getClassMemberPrefix, + pipeNode, + pipeNodes, + emitNode, + emitNodes, + visitNode, + visitNodes, + accept(node: Node, pipeline: Pipeline, write: PipelineOutput) { + ts.accept(transformer, node, pipeline, write); + } + }; + + let result = chain(transformer, statements); cleanupTransformation(); return result; } + function initializeTransformation(_compilerOptions: CompilerOptions, _currentSourceFile: SourceFile, _resolver: EmitResolver, _generatedNameSet: Map, _nodeToGeneratedName: string[]) { compilerOptions = _compilerOptions; @@ -398,7 +432,7 @@ namespace ts { // in global scope. The name is formed by adding an '_n' suffix to the specified base name, // where n is a positive integer. Note that names generated by makeTempVariableName and // makeUniqueName are guaranteed to never conflict. - export function makeUniqueName(baseName: string): string { + function makeUniqueName(baseName: string): string { // Find the first unique 'name_n', where n is a positive number if (baseName.charCodeAt(baseName.length - 1) !== CharacterCodes._) { baseName += "_"; @@ -413,12 +447,12 @@ namespace ts { } } - export function getGeneratedNameForNode(node: Node) { + function getGeneratedNameForNode(node: Node) { let id = getNodeId(node); return nodeToGeneratedIdentifier[id] || (nodeToGeneratedIdentifier[id] = createIdentifier(getGeneratedNameTextForNode(node, id))); } - export function nodeHasGeneratedName(node: Node) { + function nodeHasGeneratedName(node: Node) { let id = getNodeId(node); return nodeToGeneratedName[id] !== undefined; } @@ -513,16 +547,16 @@ namespace ts { return nodeStack.getParent(); } - export function findAncestorNode(match: (node: Node) => node is T): T; - export function findAncestorNode(match: (node: Node) => boolean): Node; - export function findAncestorNode(match: (node: Node) => boolean) { + function findAncestorNode(match: (node: Node) => node is T): T; + function findAncestorNode(match: (node: Node) => boolean): Node; + function findAncestorNode(match: (node: Node) => boolean) { return nodeStack.findAncestorNode(match); } - export function getDeclarationName(node: DeclarationStatement): Identifier; - export function getDeclarationName(node: ClassLikeDeclaration): Identifier; - export function getDeclarationName(node: Declaration): DeclarationName; - export function getDeclarationName(node: Declaration): T | Identifier { + function getDeclarationName(node: DeclarationStatement): Identifier; + function getDeclarationName(node: ClassLikeDeclaration): Identifier; + function getDeclarationName(node: Declaration): DeclarationName; + function getDeclarationName(node: Declaration): T | Identifier { let name = node.name; if (name) { return nodeIsSynthesized(name) ? name : cloneNode(name); @@ -532,7 +566,7 @@ namespace ts { } } - export function getClassMemberPrefix(node: ClassLikeDeclaration, member: ClassElement) { + function getClassMemberPrefix(node: ClassLikeDeclaration, member: ClassElement) { let expression: Expression = getDeclarationName(node); if (!(member.flags & NodeFlags.Static)) { expression = createPropertyAccessExpression2( @@ -544,17 +578,17 @@ namespace ts { return expression; } - export function createUniqueIdentifier(baseName: string): Identifier { + function createUniqueIdentifier(baseName: string): Identifier { let name = makeUniqueName(baseName); return createIdentifier(name); } - export function createTempVariable(loopVariable: boolean): Identifier { + function createTempVariable(loopVariable: boolean): Identifier { let name = makeTempVariableName(loopVariable ? TempFlags._i : TempFlags.Auto); return createIdentifier(name); } - export function declareLocal(baseName?: string): Identifier { + function declareLocal(baseName?: string): Identifier { let local = baseName ? createUniqueIdentifier(baseName) : createTempVariable(/*loopVariable*/ false); @@ -562,7 +596,7 @@ namespace ts { return local; } - export function hoistVariableDeclaration(name: Identifier): void { + function hoistVariableDeclaration(name: Identifier): void { if (!hoistedVariableDeclarations) { hoistedVariableDeclarations = []; } @@ -570,7 +604,7 @@ namespace ts { hoistedVariableDeclarations.push(createVariableDeclaration2(name)); } - export function hoistFunctionDeclaration(func: FunctionDeclaration): void { + function hoistFunctionDeclaration(func: FunctionDeclaration): void { if (!hoistedFunctionDeclarations) { hoistedFunctionDeclarations = []; } @@ -578,17 +612,6 @@ namespace ts { hoistedFunctionDeclarations.push(func); } - export function childNodeStartPositionIsOnSameLine(parent: Node, child: Node) { - if (nodeIsSynthesized(child)) { - return !(child).startsOnNewLine; - } - if (nodeIsSynthesized(parent)) { - return false; - } - return getLineOfLocalPosition(currentSourceFile, skipTrivia(currentSourceFile.text, parent.pos)) === - getLineOfLocalPosition(currentSourceFile, skipTrivia(currentSourceFile.text, child.pos)); - } - function aggregateTransformFlags(node: Node) { if (!node) { return; @@ -667,13 +690,43 @@ namespace ts { Debug.fail("Node already written"); } - function writeStatementOrBlockSlow(node: Statement) { + function writeExpressionOrBlockSlow(node: Node) { + if (!updatedNode) { + if (isExpressionNode(node)) { + updatedNode = node; + } + else { + updatedNode = createBlock([]); + writeNodeFastOrSlow = writeExpressionOrBlockFast; + writeNodeFastOrSlow(node); + } + } + else { + let previousNode = updatedNode; + updatedNode = createBlock([]); + writeNodeFastOrSlow = writeExpressionOrBlockFast; + writeNodeFastOrSlow(previousNode); + writeNodeFastOrSlow(node); + } + } + + function writeExpressionOrBlockFast(node: Node) { + if (isExpressionNode(node)) { + (updatedNode).statements.push(createReturnStatement(node)); + } + else { + Debug.assert(isStatementNode(node)); + (updatedNode).statements.push(node); + } + } + + function writeStatementOrBlockSlow(node: Node) { Debug.assert(isStatementNode(node)); if (!updatedNode) { updatedNode = node; } else { - let previousNode = updatedNode; + let previousNode = updatedNode; updatedNode = createBlock([]); writeNodeFastOrSlow = writeStatementOrBlockFast; writeNodeFastOrSlow(previousNode); @@ -681,8 +734,9 @@ namespace ts { } } - function writeStatementOrBlockFast(node: Statement) { - (updatedNode).statements.push(node); + function writeStatementOrBlockFast(node: Node) { + Debug.assert(isStatementNode(node)); + (updatedNode).statements.push(node); } function writeNodeToNodeArraySlow(node: Node) { @@ -789,12 +843,23 @@ namespace ts { hoistedFunctionDeclarations = savedHoistedFunctionDeclarations; } } - - function emitOne(input: TIn, pipeline: Pipeline, flags: PipelineFlags, nodeTest: NodeTest): TOut { - if (!input) { - return undefined; + + function emitOneWithLexicalEnvironment(input: Node, pipeline: Pipeline, flags: PipelineFlags, nodeTest: NodeTest): Node { + if (isBlock(input)) { + return updateBlock(input, visitNodes(input.statements, pipeline, flags)); } - + else if (isModuleBlock(input)) { + return updateModuleBlock(input, visitNodes(input.statements, pipeline, flags)); + } + else if (isExpression(input)) { + return emitOne(input, pipeline, flags | PipelineFlags.ExpressionOrBlock, nodeTest); + } + else { + return emitOne(input, pipeline, flags, nodeTest); + } + } + + function emitOne(input: TIn, pipeline: Pipeline, flags: PipelineFlags, nodeTest: NodeTest): TOut { // Preserve the current environment on the call stack as we descend into the tree let savedUpdatedNode = updatedNode; let savedNodeTestCallback = nodeTestCallback; @@ -805,7 +870,11 @@ namespace ts { updatedNode = undefined; nodeTestCallback = nodeTest; writeNodeWithOrWithoutNodeTest = nodeTest ? writeNodeWithNodeTest : writeNodeWithoutNodeTest; - writeNodeFastOrSlow = flags & PipelineFlags.StatementOrBlock ? writeStatementOrBlockSlow : writeNodeSlow; + writeNodeFastOrSlow = flags & PipelineFlags.StatementOrBlock + ? writeStatementOrBlockSlow + : flags & PipelineFlags.ExpressionOrBlock + ? writeExpressionOrBlockSlow + : writeNodeSlow; // Pipe the input node into the output pipeOneOrMany(input, undefined, pipeline, writeNode, flags); @@ -902,7 +971,7 @@ namespace ts { * @param output The callback passed to `visitor` to write each visited node. * @param flags Flags that affect the pipeline. */ - export function pipeNode(input: TIn, pipeline: Pipeline, output: PipelineOutput, flags?: PipelineFlags): void { + function pipeNode(input: TIn, pipeline: Pipeline, output: PipelineOutput, flags?: PipelineFlags): void { pipeOneOrMany(input, undefined, pipeline, output, flags); } @@ -913,7 +982,7 @@ namespace ts { * @param output The callback passed to `visitor` to write each visited node. * @param flags Flags that affect the pipeline. */ - export function pipeNodes(input: TIn[], pipeline: Pipeline, output: PipelineOutput, flags?: PipelineFlags): void { + function pipeNodes(input: TIn[], pipeline: Pipeline, output: PipelineOutput, flags?: PipelineFlags): void { pipeOneOrMany(undefined, input, pipeline, output, flags); } @@ -924,14 +993,10 @@ namespace ts { * @param output The destination node array to which to write the results from visiting each node. * @param flags Flags that affect the pipeline. */ - export function emitNode(input: TIn, pipeline: Pipeline, output: TOut[], flags?: PipelineFlags, nodeTest?: NodeTest): void { + function emitNode(input: TIn, pipeline: Pipeline, output: TOut[], flags?: PipelineFlags, nodeTest?: NodeTest): void { emitOneOrMany(input, undefined, pipeline, output, flags, nodeTest); } - export function flatMapNode(input: TIn, pipeline: Pipeline, flags?: PipelineFlags, nodeTest?: NodeTest): NodeArray { - return emitOneOrMany(input, undefined, pipeline, [], flags, nodeTest); - } - /** * Writes the result from visiting each node from an input source to an output node array. * @param input The source nodes to visit. @@ -939,77 +1004,26 @@ namespace ts { * @param output The destination node array to which to write the results from visiting each node. * @param flags Flags that affect the pipeline. */ - export function emitNodes(input: TIn[], pipeline: Pipeline, output: TOut[], flags?: PipelineFlags, nodeTest?: NodeTest): void { + function emitNodes(input: TIn[], pipeline: Pipeline, output: TOut[], flags?: PipelineFlags, nodeTest?: NodeTest): void { emitOneOrMany(undefined, input, pipeline, output, flags, nodeTest); } - export function visitNode(node: T, visitor: Visitor, flags?: PipelineFlags): T; - export function visitNode(node: TIn, visitor: Pipeline, flags?: PipelineFlags, nodeTest?: NodeTest): TOut; - export function visitNode(node: TIn, visitor: Pipeline, flags?: PipelineFlags, nodeTest?: NodeTest): TOut { - return emitOne(node, visitor, flags, nodeTest); - } - - export function visitStatement(node: Statement, visitor: Visitor, flags?: PipelineFlags) { - return emitOne(node, visitor, flags | PipelineFlags.StatementOrBlock, undefined); - } - - export function visitNodes(nodes: T[], pipeline: Visitor, flags?: PipelineFlags): NodeArray; - export function visitNodes(nodes: TIn[], pipeline: Pipeline, flags?: PipelineFlags, nodeTest?: NodeTest): NodeArray; - export function visitNodes(nodes: TIn[], pipeline: Pipeline, flags?: PipelineFlags, nodeTest?: NodeTest): NodeArray { - return emitOneOrMany(undefined, nodes, pipeline, undefined, flags, nodeTest); - } - - export function visitNewLexicalEnvironment(node: LexicalEnvironmentBody, pipeline: Visitor): LexicalEnvironmentBody { - if (isBlock(node)) { - return updateBlock(node, visitNodes(node.statements, pipeline, PipelineFlags.LexicalEnvironment)); + function visitNode(node: T, visitor: Visitor, flags?: PipelineFlags): T; + function visitNode(node: TIn, visitor: Pipeline, flags?: PipelineFlags, nodeTest?: NodeTest): TOut; + function visitNode(node: TIn, visitor: Pipeline, flags?: PipelineFlags, nodeTest?: NodeTest): TOut { + if (!node) { + return undefined; } - else if (isModuleBlock(node)) { - return updateModuleBlock(node, visitNodes(node.statements, pipeline, PipelineFlags.LexicalEnvironment)); - } - else if (isExpressionNode(node)) { - return visitExpressionFunctionBodyInNewLexicalEnvironment(node, pipeline); - } - else { - return visitNode(node, pipeline); - } - } - - function visitExpressionFunctionBodyInNewLexicalEnvironment(node: Expression, pipeline: Pipeline): Expression | Block { - let savedTempFlags = tempFlags; - let savedHoistedVariableDeclarations = hoistedVariableDeclarations; - let savedHoistedFunctionDeclarations = hoistedFunctionDeclarations; - tempFlags = 0; - hoistedVariableDeclarations = undefined; - hoistedFunctionDeclarations = undefined; + return flags & PipelineFlags.LexicalEnvironment + ? emitOneWithLexicalEnvironment(node, visitor, flags, nodeTest) + : emitOne(node, visitor, flags, nodeTest); + } - let visited = visitNode(node, pipeline); - let result: Block | Expression; - if (hoistedVariableDeclarations || hoistedFunctionDeclarations) { - let block = createBlock([]); - if (visited) { - let returnStmt = createReturnStatement(visited); - block.statements.push(returnStmt); - } - - if (hoistedVariableDeclarations) { - block.statements.push(createVariableStatement2(createVariableDeclarationList(hoistedVariableDeclarations))); - } - - if (hoistedFunctionDeclarations) { - block.statements.push(...hoistedFunctionDeclarations); - } - - result = block; - } - else { - result = visited; - } - - tempFlags = savedTempFlags; - hoistedVariableDeclarations = savedHoistedVariableDeclarations; - hoistedFunctionDeclarations = savedHoistedFunctionDeclarations; - return result; + function visitNodes(nodes: T[], pipeline: Visitor, flags?: PipelineFlags): NodeArray; + function visitNodes(nodes: TIn[], pipeline: Pipeline, flags?: PipelineFlags, nodeTest?: NodeTest): NodeArray; + function visitNodes(nodes: TIn[], pipeline: Pipeline, flags?: PipelineFlags, nodeTest?: NodeTest): NodeArray { + return emitOneOrMany(undefined, nodes, pipeline, undefined, flags, nodeTest); } } } \ No newline at end of file diff --git a/src/compiler/transforms/chain.ts b/src/compiler/transforms/chain.ts index a2e827a2f88..08deafe9189 100644 --- a/src/compiler/transforms/chain.ts +++ b/src/compiler/transforms/chain.ts @@ -7,8 +7,6 @@ namespace ts.transform { export let transformTime = 0; - export type TransformationChain = (statements: NodeArray) => NodeArray; - export function getTransformationChain(options: CompilerOptions): TransformationChain { if ((options.target || ScriptTarget.ES3) < ScriptTarget.ES6) { return chainTransformations([toES6, toES5]); @@ -27,47 +25,47 @@ namespace ts.transform { } } - function runTransformation(chain: TransformationChain, statements: NodeArray) { + function runTransformation(chain: TransformationChain, transformer: Transformer, statements: NodeArray) { let start = new Date().getTime(); - let transformed = chain(statements); + let transformed = chain(transformer, statements); transformTime += new Date().getTime() - start; return transformed; } function createUnaryTransformationChain(only: TransformationChain) { - return function (statements: NodeArray) { - if (only) statements = runTransformation(only, statements); + return function (transformer: Transformer, statements: NodeArray) { + if (only) statements = runTransformation(only, transformer, statements); return statements; }; } function createBinaryTransformationChain(first: TransformationChain, second: TransformationChain) { - return function (statements: NodeArray) { - if (first) statements = runTransformation(first, statements); - if (second) statements = runTransformation(second, statements); + return function (transformer: Transformer, statements: NodeArray) { + if (first) statements = runTransformation(first, transformer, statements); + if (second) statements = runTransformation(second, transformer, statements); return statements; }; } function createTrinaryTransformationChain(first: TransformationChain, second: TransformationChain, third: TransformationChain) { - return function (statements: NodeArray) { - if (first) statements = runTransformation(first, statements); - if (second) statements = runTransformation(second, statements); - if (third) statements = runTransformation(third, statements); + return function (transformer: Transformer, statements: NodeArray) { + if (first) statements = runTransformation(first, transformer, statements); + if (second) statements = runTransformation(second, transformer, statements); + if (third) statements = runTransformation(third, transformer, statements); return statements; }; } function createNaryTransformationChain(transformations: TransformationChain[]) { - return function (statements: NodeArray) { + return function (transformer: Transformer, statements: NodeArray) { for (let transformation of transformations) { - if (transformation) statements = transformation(statements); + if (transformation) statements = runTransformation(transformation, transformer, statements); } return statements; }; } - function identityTransformation(statements: NodeArray): NodeArray { + function identityTransformation(transformer: Transformer, statements: NodeArray): NodeArray { return statements; } } \ No newline at end of file diff --git a/src/compiler/transforms/es5.ts b/src/compiler/transforms/es5.ts index 37a080c5ba1..d880c6427a4 100644 --- a/src/compiler/transforms/es5.ts +++ b/src/compiler/transforms/es5.ts @@ -1,539 +1,560 @@ /// /*@internal*/ -namespace ts.transform { - export function toES5(statements: NodeArray) { +namespace ts { + export function toES5(transformer: Transformer, statements: NodeArray) { + let { + accept, + visitNode, + visitNodes, + emitNode, + emitNodes, + pipeNode, + pipeNodes, + getParentNode, + findAncestorNode, + declareLocal, + getGeneratedNameForNode, + createTempVariable, + hoistVariableDeclaration, + getDeclarationName, + getClassMemberPrefix, + } = transformer; + let currentSourceFile = transformer.getRootNode(); + return visitNodes(statements, transformNode, PipelineFlags.LexicalEnvironment); - } - /** - * Transforms a node from ES6 to ES5 if it requires any transformations. - * @param context Context information for the transform. - * @param node The node to transform. - * @remarks - * This function is intentionally kept small to keep its overhead low. - * - * If the node needs direct transformation, it will be passed on to the - * `transformNodeWorker` function. - * - * If any part of its subtree needs transformation, the node will be - * passed to the fallback `accept` function which will ensure any changes - * to the subtree will generate new nodes. - * - * If no part of this node or its subtree requires transformation, the node - * is returned, unchanged. - */ - function transformNode(node: Node, write: (node: Node) => void): void { - if (!node) { - return; - } - - // Debug.assert( - // !needsTransform(node, TransformFlags.ThisNodeNeedsTransformToES6), - // "Cannot transform node with post-ES6 syntax."); - - if (node.transformFlags & TransformFlags.ThisNodeNeedsTransformToES5) { - transformNodeWorker(node, write); - } - else if (node.flags & NodeFlags.GeneratedSuper && isExpressionStatement(node)) { - transformInitialSuperCall(node, write); - } - else if (node.transformFlags & TransformFlags.SubtreeNeedsTransformToES5) { - accept(node, transformNode, write); - } - else { - write(node); - } - } - - /** - * Transforms a node from ES6 to ES5. - * @param context Context information for the transform. - * @param node The node to transform. - */ - function transformNodeWorker(node: Node, write: (node: Node) => void): void { - switch (node.kind) { - case SyntaxKind.ClassDeclaration: - return transformClassDeclaration(node, write); - - case SyntaxKind.ClassExpression: - return transformClassExpression(node, write); - - case SyntaxKind.Parameter: - return transformParameter(node, write); - - case SyntaxKind.FunctionDeclaration: - return transformFunctionDeclaration(node, write); - - case SyntaxKind.ArrowFunction: - case SyntaxKind.FunctionExpression: - return transformFunctionExpression(node, write); - - case SyntaxKind.BindingElement: - case SyntaxKind.ArrayBindingPattern: - case SyntaxKind.ObjectBindingPattern: - case SyntaxKind.ComputedPropertyName: - case SyntaxKind.ObjectLiteralExpression: - case SyntaxKind.ArrayLiteralExpression: - case SyntaxKind.SpreadElementExpression: - case SyntaxKind.CallExpression: - case SyntaxKind.NewExpression: - case SyntaxKind.ForOfStatement: - case SyntaxKind.SuperKeyword: - case SyntaxKind.ThisKeyword: - case SyntaxKind.YieldExpression: - case SyntaxKind.ImportDeclaration: - case SyntaxKind.ExportDeclaration: - case SyntaxKind.VariableStatement: - case SyntaxKind.VariableDeclarationList: - case SyntaxKind.VariableDeclaration: - case SyntaxKind.BinaryExpression: - case SyntaxKind.NoSubstitutionTemplateLiteral: - case SyntaxKind.TaggedTemplateExpression: - case SyntaxKind.TemplateExpression: - // TODO (rbuckton): ES6 syntax we're not yet transforming and are leaving for the emitter to handle. - - default: - return accept(node, transformNode, write); - } - } - - function transformClassDeclaration(node: ClassDeclaration, write: (node: Statement) => void) { - let name = getDeclarationName(node); - let classInitExpr = transformClassLikeDeclaration(node, name); - let baseTypeNode = getClassExtendsHeritageClauseElement(node); - let varDecl = createVariableDeclaration2(name, classInitExpr); - let varDecls = createVariableDeclarationList([varDecl]); - let varStmt = createVariableStatement2(varDecls, /*location*/ node); - return write(varStmt); - } - - function transformClassExpression(node: ClassExpression, write: (node: LeftHandSideExpression) => void) { - let name = getDeclarationName(node); - return write(transformClassLikeDeclaration(node, name)); - } - - function transformClassLikeDeclaration(node: ClassLikeDeclaration, name: Identifier): LeftHandSideExpression { - let baseTypeNode = getClassExtendsHeritageClauseElement(node); - let classBody = createBlock([]); - emitNode(node, transformClassBody, classBody.statements); - - let superExpr = baseTypeNode ? visitNode(baseTypeNode.expression, transformNode) : undefined; - let superName = baseTypeNode ? createIdentifier("_super") : undefined; - let superParam = baseTypeNode ? createParameter2(superName) : undefined; - let classDecl = createFunctionExpression2(/*name*/ undefined, baseTypeNode ? [superParam] : [], classBody); - let parenExpr = createParenthesizedExpression(classDecl); - let callExpr = createCallExpression2(parenExpr, baseTypeNode ? [superExpr] : undefined); - return callExpr; - } - - function transformClassBody(node: ClassLikeDeclaration, write: (node: Statement) => void): void { - let name = getDeclarationName(node); - let baseTypeNode = getClassExtendsHeritageClauseElement(node); - emitExtendsCall(node, name, baseTypeNode, write); - emitConstructor(node, name, baseTypeNode, write); - emitMemberFunctions(node, write); - - let returnStmt = createReturnStatement(name); - write(returnStmt); - } - - function emitExtendsCall(node: ClassLikeDeclaration, name: Identifier, baseTypeNode: ExpressionWithTypeArguments, write: (node: Statement) => void): void { - if (!baseTypeNode) { - return; - } - - let extendsExpr = createExtendsHelperCall(name); - let extendsStmt = createExpressionStatement(extendsExpr); - write(extendsStmt); - } - - function emitConstructor(node: ClassLikeDeclaration, name: Identifier, baseTypeNode: ExpressionWithTypeArguments, write: (node: Statement) => void) { - let constructor = getFirstConstructorWithBody(node); - let parameters: ParameterDeclaration[] = constructor ? visitNodes(constructor.parameters, transformNode) : []; - let body = createBlock([]); - - if (constructor) { - emitNode(constructor, transformConstructor, body.statements, PipelineFlags.LexicalEnvironment); - } - else if (baseTypeNode) { - let superCall = createDefaultSuperCall(); - body.statements.push(superCall); - } - - let constructorFunc = createFunctionDeclaration2(name, parameters, body); - write(constructorFunc); - } - - function transformConstructor(constructor: ConstructorDeclaration, write: (node: Statement) => void) { - emitCaptureThisForNode(constructor, write); - emitDefaultValueAssignments(constructor, write); - emitRestParameter(constructor, write); - pipeNodes(constructor.body.statements, transformNode, write); - } - - function transformParameter(node: ParameterDeclaration, write: (node: ParameterDeclaration) => void) { - if (isBindingPattern(node.name)) { - write(createParameter2( - getGeneratedNameForNode(node), - /*initializer*/ undefined, - /*location*/ node - )); - } - else if (node.initializer) { - write(createParameter2( - node.name, - /*initializer*/ undefined, - /*location*/ node - )); - } - else if (!node.dotDotDotToken) { - // rest parameters are elided, other parameters are included. - write(node); - } - } - - function emitDefaultValueAssignments(node: FunctionLikeDeclaration, write: (node: Statement) => void) { - if (!(node.transformFlags & (TransformFlags.SubtreeContainsParameterInitializer | TransformFlags.SubtreeContainsParameterBindingPattern))) { - return; - } - - for (let parameter of node.parameters) { - let { name, initializer, dotDotDotToken } = parameter; - - // A rest parameter cannot have a binding pattern or an initializer, - // so let's just ignore it. - if (dotDotDotToken) { - continue; + /** + * Transforms a node from ES6 to ES5 if it requires any transformations. + * @param context Context information for the transform. + * @param node The node to transform. + * @remarks + * This function is intentionally kept small to keep its overhead low. + * + * If the node needs direct transformation, it will be passed on to the + * `transformNodeWorker` function. + * + * If any part of its subtree needs transformation, the node will be + * passed to the fallback `accept` function which will ensure any changes + * to the subtree will generate new nodes. + * + * If no part of this node or its subtree requires transformation, the node + * is returned, unchanged. + */ + function transformNode(node: Node, write: (node: Node) => void): void { + if (!node) { + return; } + + // Debug.assert( + // !needsTransform(node, TransformFlags.ThisNodeNeedsTransformToES6), + // "Cannot transform node with post-ES6 syntax."); + + if (node.transformFlags & TransformFlags.ThisNodeNeedsTransformToES5) { + transformNodeWorker(node, write); + } + else if (node.flags & NodeFlags.GeneratedSuper && isExpressionStatement(node)) { + transformInitialSuperCall(node, write); + } + else if (node.transformFlags & TransformFlags.SubtreeNeedsTransformToES5) { + accept(node, transformNode, write); + } + else { + write(node); + } + } + + /** + * Transforms a node from ES6 to ES5. + * @param context Context information for the transform. + * @param node The node to transform. + */ + function transformNodeWorker(node: Node, write: (node: Node) => void): void { + switch (node.kind) { + case SyntaxKind.ClassDeclaration: + return transformClassDeclaration(node, write); + + case SyntaxKind.ClassExpression: + return transformClassExpression(node, write); + + case SyntaxKind.Parameter: + return transformParameter(node, write); + + case SyntaxKind.FunctionDeclaration: + return transformFunctionDeclaration(node, write); + + case SyntaxKind.ArrowFunction: + case SyntaxKind.FunctionExpression: + return transformFunctionExpression(node, write); + + case SyntaxKind.BindingElement: + case SyntaxKind.ArrayBindingPattern: + case SyntaxKind.ObjectBindingPattern: + case SyntaxKind.ComputedPropertyName: + case SyntaxKind.ObjectLiteralExpression: + case SyntaxKind.ArrayLiteralExpression: + case SyntaxKind.SpreadElementExpression: + case SyntaxKind.CallExpression: + case SyntaxKind.NewExpression: + case SyntaxKind.ForOfStatement: + case SyntaxKind.SuperKeyword: + case SyntaxKind.ThisKeyword: + case SyntaxKind.YieldExpression: + case SyntaxKind.ImportDeclaration: + case SyntaxKind.ExportDeclaration: + case SyntaxKind.VariableStatement: + case SyntaxKind.VariableDeclarationList: + case SyntaxKind.VariableDeclaration: + case SyntaxKind.BinaryExpression: + case SyntaxKind.NoSubstitutionTemplateLiteral: + case SyntaxKind.TaggedTemplateExpression: + case SyntaxKind.TemplateExpression: + // TODO (rbuckton): ES6 syntax we're not yet transforming and are leaving for the emitter to handle. + + default: + return accept(node, transformNode, write); + } + } - if (isBindingPattern(name)) { - emitDefaultValueAssignmentForBindingPattern(parameter, name, initializer, write); + function transformClassDeclaration(node: ClassDeclaration, write: (node: Statement) => void) { + let name = getDeclarationName(node); + let classInitExpr = transformClassLikeDeclaration(node, name); + let baseTypeNode = getClassExtendsHeritageClauseElement(node); + let varDecl = createVariableDeclaration2(name, classInitExpr); + let varDecls = createVariableDeclarationList([varDecl]); + let varStmt = createVariableStatement2(varDecls, /*location*/ node); + return write(varStmt); + } + + function transformClassExpression(node: ClassExpression, write: (node: LeftHandSideExpression) => void) { + let name = getDeclarationName(node); + return write(transformClassLikeDeclaration(node, name)); + } + + function transformClassLikeDeclaration(node: ClassLikeDeclaration, name: Identifier): LeftHandSideExpression { + let baseTypeNode = getClassExtendsHeritageClauseElement(node); + let classBody = createBlock([]); + emitNode(node, transformClassBody, classBody.statements); + + let superExpr = baseTypeNode ? visitNode(baseTypeNode.expression, transformNode) : undefined; + let superName = baseTypeNode ? createIdentifier("_super") : undefined; + let superParam = baseTypeNode ? createParameter2(superName) : undefined; + let classDecl = createFunctionExpression2(/*name*/ undefined, baseTypeNode ? [superParam] : [], classBody); + let parenExpr = createParenthesizedExpression(classDecl); + let callExpr = createCallExpression2(parenExpr, baseTypeNode ? [superExpr] : undefined); + return callExpr; + } + + function transformClassBody(node: ClassLikeDeclaration, write: (node: Statement) => void): void { + let name = getDeclarationName(node); + let baseTypeNode = getClassExtendsHeritageClauseElement(node); + emitExtendsCall(node, name, baseTypeNode, write); + emitConstructor(node, name, baseTypeNode, write); + emitMemberFunctions(node, write); + + let returnStmt = createReturnStatement(name); + write(returnStmt); + } + + function emitExtendsCall(node: ClassLikeDeclaration, name: Identifier, baseTypeNode: ExpressionWithTypeArguments, write: (node: Statement) => void): void { + if (!baseTypeNode) { + return; + } + + let extendsExpr = createExtendsHelperCall(name); + let extendsStmt = createExpressionStatement(extendsExpr); + write(extendsStmt); + } + + function emitConstructor(node: ClassLikeDeclaration, name: Identifier, baseTypeNode: ExpressionWithTypeArguments, write: (node: Statement) => void) { + let constructor = getFirstConstructorWithBody(node); + let parameters: ParameterDeclaration[] = constructor ? visitNodes(constructor.parameters, transformNode) : []; + let body = createBlock([]); + + if (constructor) { + emitNode(constructor, transformConstructor, body.statements, PipelineFlags.LexicalEnvironment); + } + else if (baseTypeNode) { + let superCall = createDefaultSuperCall(); + body.statements.push(superCall); + } + + let constructorFunc = createFunctionDeclaration2(name, parameters, body); + write(constructorFunc); + } + + function transformConstructor(constructor: ConstructorDeclaration, write: (node: Statement) => void) { + emitCaptureThisForNode(constructor, write); + emitDefaultValueAssignments(constructor, write); + emitRestParameter(constructor, write); + pipeNodes(constructor.body.statements, transformNode, write); + } + + function transformParameter(node: ParameterDeclaration, write: (node: ParameterDeclaration) => void) { + if (isBindingPattern(node.name)) { + write(createParameter2( + getGeneratedNameForNode(node), + /*initializer*/ undefined, + /*location*/ node + )); + } + else if (node.initializer) { + write(createParameter2( + node.name, + /*initializer*/ undefined, + /*location*/ node + )); + } + else if (!node.dotDotDotToken) { + // rest parameters are elided, other parameters are included. + write(node); + } + } + + function emitDefaultValueAssignments(node: FunctionLikeDeclaration, write: (node: Statement) => void) { + if (!(node.transformFlags & (TransformFlags.SubtreeContainsParameterInitializer | TransformFlags.SubtreeContainsParameterBindingPattern))) { + return; + } + + for (let parameter of node.parameters) { + let { name, initializer, dotDotDotToken } = parameter; + + // A rest parameter cannot have a binding pattern or an initializer, + // so let's just ignore it. + if (dotDotDotToken) { + continue; + } + + if (isBindingPattern(name)) { + emitDefaultValueAssignmentForBindingPattern(parameter, name, initializer, write); + } + else if (initializer) { + emitDefaultValueAssignmentForInitializer(parameter, name, initializer, write); + } + } + } + + function emitDefaultValueAssignmentForBindingPattern(parameter: ParameterDeclaration, name: BindingPattern, initializer: Expression, write: (node: Statement) => void): void { + let tempName = getGeneratedNameForNode(parameter); + + // In cases where a binding pattern is simply '[]' or '{}', + // we usually don't want to emit a var declaration; however, in the presence + // of an initializer, we must emit that expression to preserve side effects. + let hasBindingElements = name.elements.length > 0; + if (hasBindingElements) { + let varDecls = createVariableDeclarationList([]); + transformBindingElement(parameter, tempName, varDecls.declarations, /*assignments*/ undefined); + + let varStmt = createVariableStatement2(varDecls); + startOnNewLine(varStmt); + write(varStmt); } else if (initializer) { - emitDefaultValueAssignmentForInitializer(parameter, name, initializer, write); + let initExpr = visitNode(initializer, transformNode); + let assignExpr = createAssignmentExpression(tempName, initExpr); + let assignStmt = createExpressionStatement(assignExpr); + startOnNewLine(assignStmt); + write(assignStmt); } } - } - - function emitDefaultValueAssignmentForBindingPattern(parameter: ParameterDeclaration, name: BindingPattern, initializer: Expression, write: (node: Statement) => void): void { - let tempName = getGeneratedNameForNode(parameter); - // In cases where a binding pattern is simply '[]' or '{}', - // we usually don't want to emit a var declaration; however, in the presence - // of an initializer, we must emit that expression to preserve side effects. - let hasBindingElements = name.elements.length > 0; - if (hasBindingElements) { - let varDecls = createVariableDeclarationList([]); - transformBindingElement(parameter, tempName, varDecls.declarations, /*assignments*/ undefined); + function transformBindingElement(target: BindingElement, value: Expression, declarations: VariableDeclaration[], assignments: Expression[]): void { + if (target.initializer) { + // Combine value and initializer + let initializer = visitNode(target.initializer, transformNode); + value = value ? createDefaultValueCheck(value, initializer, declarations, assignments) : initializer; + } + else if (!value) { + // Use 'void 0' in absence of value and initializer + value = createVoidZeroExpression(); + } + let name = target.name; + if (isBindingPattern(name)) { + let elements = name.elements; + if (elements.length !== 1) { + // For anything but a single element destructuring we need to generate a temporary + // to ensure value is evaluated exactly once. + value = ensureIdentifier(value, declarations, assignments); + } + for (let i = 0; i < elements.length; i++) { + let element = elements[i]; + if (name.kind === SyntaxKind.ObjectBindingPattern) { + // Rewrite element to a declaration with an initializer that fetches property + let propName = element.propertyName || element.name; + transformBindingElement(element, createPropertyOrElementAccessExpression(value, propName), declarations, assignments); + } + else if (element.kind !== SyntaxKind.OmittedExpression) { + if (!element.dotDotDotToken) { + // Rewrite element to a declaration that accesses array element at index i + transformBindingElement(element, createElementAccessExpression3(value, i), declarations, assignments); + } + else if (i === elements.length - 1) { + transformBindingElement(element, createSliceCall(value, i), declarations, assignments); + } + } + } + } + else { + appendAssignment(name, value, declarations, assignments); + } + } + + function appendAssignment(left: Identifier, right: Expression, declarations: VariableDeclaration[], assignments: Expression[]): void { + let varDecl = createVariableDeclaration2(left, assignments ? undefined : right); + declarations.push(varDecl); + + if (assignments) { + let assignExpr = createAssignmentExpression(left, right); + assignments.push(assignExpr); + } + } + + function ensureIdentifier(value: Expression, declarations: VariableDeclaration[], assignments: Expression[]) { + if (isIdentifier(value)) { + return value; + } + else { + let tempVar = createTempVariable(/*loopVariable*/ false); + appendAssignment(tempVar, value, declarations, assignments); + return tempVar; + } + } + + function createDefaultValueCheck(value: Expression, defaultValue: Expression, declarations: VariableDeclaration[], assignments: Expression[]) { + value = ensureIdentifier(value, declarations, assignments); + let equalityExpr = createStrictEqualityExpression(value, createVoidZeroExpression()); + let conditionalExpr = createConditionalExpression2(equalityExpr, defaultValue, value); + return conditionalExpr; + } + + function emitDefaultValueAssignmentForInitializer(parameter: ParameterDeclaration, name: Identifier, initializer: Expression, write: (node: Statement) => void): void { + name = cloneNode(name); + let equalityExpr = createStrictEqualityExpression(name, createVoidZeroExpression()); + let initExpr = visitNode(initializer, transformNode); + let assignExpr = createAssignmentExpression(name, initExpr); + let assignStmt = createExpressionStatement(assignExpr); + let trueStmt = createBlock([assignStmt]); + let ifStmt = createIfStatement(equalityExpr, trueStmt); + startOnNewLine(ifStmt); + write(ifStmt); + } + + function emitRestParameter(node: FunctionLikeDeclaration, write: (node: Statement) => void): void { + if (!(node.transformFlags & TransformFlags.SubtreeContainsRestParameter)) { + return; + } + + let restParam = lastOrUndefined(node.parameters); + if (!restParam.dotDotDotToken || restParam.flags & NodeFlags.GeneratedRest) { + return; + } + + let restIndex = node.parameters.length - 1; + let _i = createTempVariable(/*loopVariable*/ true); + let name = getDeclarationName(restParam); + let arrayExpr = createArrayLiteralExpression([]); + let paramVarDecl = createVariableDeclaration2(name, arrayExpr); + let paramVarDecls = createVariableDeclarationList([paramVarDecl]); + let paramVarStmt = createVariableStatement2(paramVarDecls); + startOnNewLine(paramVarStmt); + write(paramVarStmt); + + let restIndexExpr = createNumericLiteral2(restIndex); + let initializerVarDecl = createVariableDeclaration2(_i, restIndexExpr); + let initializerVarDecls = createVariableDeclarationList([initializerVarDecl]); + let argumentsName = createIdentifier("arguments"); + let lengthName = createIdentifier("length"); + let argumentsLengthExpr = createPropertyAccessExpression2(argumentsName, lengthName); + let conditionExpr = createBinaryExpression2(_i, SyntaxKind.LessThanToken, argumentsLengthExpr); + let incrementerExpr = createPostfixUnaryExpression(_i, SyntaxKind.PlusPlusToken); + let arrayOffsetExpr = restIndex === 0 ? _i : createBinaryExpression2(_i, SyntaxKind.MinusToken, restIndexExpr); + let arrayElementExpr = createElementAccessExpression2(name, arrayOffsetExpr); + let argumentsElementExpr = createElementAccessExpression2(argumentsName, _i); + let assignExpr = createAssignmentExpression(arrayElementExpr, argumentsElementExpr); + let assignStmt = createExpressionStatement(assignExpr); + startOnNewLine(assignStmt); + + let forBody = createBlock([assignStmt]); + let forStmt = createForStatement(initializerVarDecls, conditionExpr, incrementerExpr, forBody); + startOnNewLine(forStmt); + write(forStmt); + } + + function emitCaptureThisForNode(node: Node, write: (node: Statement) => void): void { + if (isArrowFunction(node) || !(node.transformFlags & TransformFlags.SubtreeCapturesLexicalThis)) { + return; + } + + let thisName = createIdentifier("_this"); + let thisExpr = createThisKeyword(); + let varDecl = createVariableDeclaration2(thisName, thisExpr); + let varDecls = createVariableDeclarationList([varDecl]); let varStmt = createVariableStatement2(varDecls); startOnNewLine(varStmt); write(varStmt); } - else if (initializer) { - let initExpr = visitNode(initializer, transformNode); - let assignExpr = createAssignmentExpression(tempName, initExpr); - let assignStmt = createExpressionStatement(assignExpr); + + function emitMemberFunctions(node: ClassLikeDeclaration, write: (node: Statement) => void): void { + for (let member of node.members) { + if (isSemicolonClassElement(member)) { + transformSemicolonClassElement(member, write); + } + else if (isMethodDeclaration(member)) { + transformMethodDeclaration(node, member, write); + } + else if (isGetAccessor(member) || isSetAccessor(member)) { + let accessors = getAllAccessorDeclarations(node.members, member); + if (member === accessors.firstAccessor) { + transformAccessorDeclaration(node, accessors, write); + } + } + } + } + + function transformSemicolonClassElement(member: SemicolonClassElement, write: (node: Statement) => void): void { + let stmt = createEmptyStatement(); + startOnNewLine(stmt); + write(stmt); + } + + function transformMethodDeclaration(node: ClassLikeDeclaration, member: MethodDeclaration, write: (node: Statement) => void): void { + let prefix = getClassMemberPrefix(node, member); + let propExpr = getMemberAccessForPropertyName(node, member); + let funcExpr = transformFunctionLikeExpressionToFunctionExpression(member, /*name*/ undefined, /*location*/ undefined); + let assignExpr = createAssignmentExpression(propExpr, funcExpr); + let assignStmt = createExpressionStatement(assignExpr, /*location*/ member); startOnNewLine(assignStmt); write(assignStmt); } - } - - function transformBindingElement(target: BindingElement, value: Expression, declarations: VariableDeclaration[], assignments: Expression[]): void { - if (target.initializer) { - // Combine value and initializer - let initializer = visitNode(target.initializer, transformNode); - value = value ? createDefaultValueCheck(value, initializer, declarations, assignments) : initializer; - } - else if (!value) { - // Use 'void 0' in absence of value and initializer - value = createVoidZeroExpression(); + + function transformAccessorDeclaration(node: ClassLikeDeclaration, accessors: AllAccessorDeclarations, write: (node: Statement) => void): void { + let firstAccessor = accessors.firstAccessor; + let prefix = getClassMemberPrefix(node, firstAccessor); + let name = getExpressionForPropertyName(firstAccessor); + let descriptorExpr = createObjectLiteralExpression2(); + if (accessors.getAccessor) { + let funcExpr = transformFunctionLikeExpressionToFunctionExpression(accessors.getAccessor, /*name*/ undefined, /*location*/ accessors.getAccessor); + let getName = createIdentifier("get"); + let getProp = createPropertyAssignment(getName, funcExpr); + startOnNewLine(getProp); + descriptorExpr.properties.push(getProp); + } + + if (accessors.setAccessor) { + let funcExpr = transformFunctionLikeExpressionToFunctionExpression(accessors.setAccessor, /*name*/ undefined, /*location*/ accessors.setAccessor); + let setName = createIdentifier("set"); + let setProp = createPropertyAssignment(setName, funcExpr); + startOnNewLine(setProp); + descriptorExpr.properties.push(setProp); + } + + let trueExpr = createTrueKeyword(); + let enumerableName = createIdentifier("enumerable"); + let enumerableProp = createPropertyAssignment(enumerableName, trueExpr) + startOnNewLine(enumerableProp); + descriptorExpr.properties.push(enumerableProp); + + let configurableName = createIdentifier("configurable"); + let configurableProp = createPropertyAssignment(configurableName, trueExpr); + startOnNewLine(configurableProp); + descriptorExpr.properties.push(configurableProp); + + let definePropertyExpr = createDefinePropertyCall(prefix, name, descriptorExpr); + let definePropertyStmt = createExpressionStatement(definePropertyExpr); + write(definePropertyStmt); } - let name = target.name; - if (isBindingPattern(name)) { - let elements = name.elements; - if (elements.length !== 1) { - // For anything but a single element destructuring we need to generate a temporary - // to ensure value is evaluated exactly once. - value = ensureIdentifier(value, declarations, assignments); + function transformFunctionExpression(node: FunctionExpression, write: (node: Expression) => void): void { + write(transformFunctionLikeExpressionToFunctionExpression(node, node.name, /*location*/ node)); + } + + function transformFunctionLikeExpressionToFunctionExpression(node: FunctionLikeDeclaration, name: Identifier, location: TextRange): FunctionExpression { + let parameters = visitNodes(node.parameters, transformNode); + let statements: Statement[] = []; + emitNode(node, transformFunctionBody, statements, PipelineFlags.LexicalEnvironment); + return createFunctionExpression2(name, parameters, createBlock(statements), location); + } + + function transformFunctionDeclaration(node: FunctionDeclaration, write: (node: Statement) => void): void { + let parameters = visitNodes(node.parameters, transformNode); + let statements: Statement[] = []; + emitNode(node, transformFunctionBody, statements, PipelineFlags.LexicalEnvironment); + write(createFunctionDeclaration2(node.name, parameters, createBlock(statements), /*location*/ node)); + } + + function transformFunctionBody(node: FunctionLikeDeclaration, write: (node: Statement) => void) { + emitCaptureThisForNode(node, write); + emitDefaultValueAssignments(node, write); + emitRestParameter(node, write); + + let body = node.body; + if (isBlock(body)) { + pipeNodes(body.statements, transformNode, write); } - for (let i = 0; i < elements.length; i++) { - let element = elements[i]; - if (name.kind === SyntaxKind.ObjectBindingPattern) { - // Rewrite element to a declaration with an initializer that fetches property - let propName = element.propertyName || element.name; - transformBindingElement(element, createPropertyOrElementAccessExpression(value, propName), declarations, assignments); - } - else if (element.kind !== SyntaxKind.OmittedExpression) { - if (!element.dotDotDotToken) { - // Rewrite element to a declaration that accesses array element at index i - transformBindingElement(element, createElementAccessExpression3(value, i), declarations, assignments); - } - else if (i === elements.length - 1) { - transformBindingElement(element, createSliceCall(value, i), declarations, assignments); + else { + let expr = visitNode(body, transformNode); + if (expr) { + let returnStmt = createReturnStatement(expr); + if (!childNodeStartPositionIsOnSameLine(currentSourceFile, node, body)) { + startOnNewLine(returnStmt); } + + write(returnStmt); } } } - else { - appendAssignment(name, value, declarations, assignments); - } - } - - function appendAssignment(left: Identifier, right: Expression, declarations: VariableDeclaration[], assignments: Expression[]): void { - let varDecl = createVariableDeclaration2(left, assignments ? undefined : right); - declarations.push(varDecl); - if (assignments) { - let assignExpr = createAssignmentExpression(left, right); - assignments.push(assignExpr); - } - } - - function ensureIdentifier(value: Expression, declarations: VariableDeclaration[], assignments: Expression[]) { - if (isIdentifier(value)) { - return value; - } - else { - let tempVar = createTempVariable(/*loopVariable*/ false); - appendAssignment(tempVar, value, declarations, assignments); - return tempVar; - } - } - - function createDefaultValueCheck(value: Expression, defaultValue: Expression, declarations: VariableDeclaration[], assignments: Expression[]) { - value = ensureIdentifier(value, declarations, assignments); - let equalityExpr = createStrictEqualityExpression(value, createVoidZeroExpression()); - let conditionalExpr = createConditionalExpression2(equalityExpr, defaultValue, value); - return conditionalExpr; - } - - function emitDefaultValueAssignmentForInitializer(parameter: ParameterDeclaration, name: Identifier, initializer: Expression, write: (node: Statement) => void): void { - name = cloneNode(name); - let equalityExpr = createStrictEqualityExpression(name, createVoidZeroExpression()); - let initExpr = visitNode(initializer, transformNode); - let assignExpr = createAssignmentExpression(name, initExpr); - let assignStmt = createExpressionStatement(assignExpr); - let trueStmt = createBlock([assignStmt]); - let ifStmt = createIfStatement(equalityExpr, trueStmt); - startOnNewLine(ifStmt); - write(ifStmt); - } - - function emitRestParameter(node: FunctionLikeDeclaration, write: (node: Statement) => void): void { - if (!(node.transformFlags & TransformFlags.SubtreeContainsRestParameter)) { - return; - } - - let restParam = lastOrUndefined(node.parameters); - if (!restParam.dotDotDotToken || restParam.flags & NodeFlags.GeneratedRest) { - return; - } - - let restIndex = node.parameters.length - 1; - let _i = createTempVariable(/*loopVariable*/ true); - let name = getDeclarationName(restParam); - let arrayExpr = createArrayLiteralExpression([]); - let paramVarDecl = createVariableDeclaration2(name, arrayExpr); - let paramVarDecls = createVariableDeclarationList([paramVarDecl]); - let paramVarStmt = createVariableStatement2(paramVarDecls); - startOnNewLine(paramVarStmt); - write(paramVarStmt); - - let restIndexExpr = createNumericLiteral2(restIndex); - let initializerVarDecl = createVariableDeclaration2(_i, restIndexExpr); - let initializerVarDecls = createVariableDeclarationList([initializerVarDecl]); - let argumentsName = createIdentifier("arguments"); - let lengthName = createIdentifier("length"); - let argumentsLengthExpr = createPropertyAccessExpression2(argumentsName, lengthName); - let conditionExpr = createBinaryExpression2(_i, SyntaxKind.LessThanToken, argumentsLengthExpr); - let incrementerExpr = createPostfixUnaryExpression(_i, SyntaxKind.PlusPlusToken); - let arrayOffsetExpr = restIndex === 0 ? _i : createBinaryExpression2(_i, SyntaxKind.MinusToken, restIndexExpr); - let arrayElementExpr = createElementAccessExpression2(name, arrayOffsetExpr); - let argumentsElementExpr = createElementAccessExpression2(argumentsName, _i); - let assignExpr = createAssignmentExpression(arrayElementExpr, argumentsElementExpr); - let assignStmt = createExpressionStatement(assignExpr); - startOnNewLine(assignStmt); - - let forBody = createBlock([assignStmt]); - let forStmt = createForStatement(initializerVarDecls, conditionExpr, incrementerExpr, forBody); - startOnNewLine(forStmt); - write(forStmt); - } - - function emitCaptureThisForNode(node: Node, write: (node: Statement) => void): void { - if (isArrowFunction(node) || !(node.transformFlags & TransformFlags.SubtreeCapturesLexicalThis)) { - return; - } - - let thisName = createIdentifier("_this"); - let thisExpr = createThisKeyword(); - let varDecl = createVariableDeclaration2(thisName, thisExpr); - let varDecls = createVariableDeclarationList([varDecl]); - let varStmt = createVariableStatement2(varDecls); - startOnNewLine(varStmt); - write(varStmt); - } - - function emitMemberFunctions(node: ClassLikeDeclaration, write: (node: Statement) => void): void { - for (let member of node.members) { - if (isSemicolonClassElement(member)) { - transformSemicolonClassElement(member, write); + function getExpressionForPropertyName(member: ClassElement): Expression { + let memberName = member.name; + if (isIdentifier(memberName)) { + return createStringLiteral(memberName.text); } - else if (isMethodDeclaration(member)) { - transformMethodDeclaration(node, member, write); + else if (isComputedPropertyName(memberName)) { + return visitNode(memberName.expression, transformNode); } - else if (isGetAccessor(member) || isSetAccessor(member)) { - let accessors = getAllAccessorDeclarations(node.members, member); - if (member === accessors.firstAccessor) { - transformAccessorDeclaration(node, accessors, write); - } + else { + return cloneNode(memberName); } } - } - - function transformSemicolonClassElement(member: SemicolonClassElement, write: (node: Statement) => void): void { - let stmt = createEmptyStatement(); - startOnNewLine(stmt); - write(stmt); - } - - function transformMethodDeclaration(node: ClassLikeDeclaration, member: MethodDeclaration, write: (node: Statement) => void): void { - let prefix = getClassMemberPrefix(node, member); - let propExpr = getMemberAccessForPropertyName(node, member); - let funcExpr = transformFunctionLikeExpressionToFunctionExpression(member, /*name*/ undefined, /*location*/ undefined); - let assignExpr = createAssignmentExpression(propExpr, funcExpr); - let assignStmt = createExpressionStatement(assignExpr, /*location*/ member); - startOnNewLine(assignStmt); - write(assignStmt); - } - - function transformAccessorDeclaration(node: ClassLikeDeclaration, accessors: AllAccessorDeclarations, write: (node: Statement) => void): void { - let firstAccessor = accessors.firstAccessor; - let prefix = getClassMemberPrefix(node, firstAccessor); - let name = getExpressionForPropertyName(firstAccessor); - let descriptorExpr = createObjectLiteralExpression2(); - if (accessors.getAccessor) { - let funcExpr = transformFunctionLikeExpressionToFunctionExpression(accessors.getAccessor, /*name*/ undefined, /*location*/ accessors.getAccessor); - let getName = createIdentifier("get"); - let getProp = createPropertyAssignment(getName, funcExpr); - startOnNewLine(getProp); - descriptorExpr.properties.push(getProp); - } - if (accessors.setAccessor) { - let funcExpr = transformFunctionLikeExpressionToFunctionExpression(accessors.setAccessor, /*name*/ undefined, /*location*/ accessors.setAccessor); - let setName = createIdentifier("set"); - let setProp = createPropertyAssignment(setName, funcExpr); - startOnNewLine(setProp); - descriptorExpr.properties.push(setProp); - } - - let trueExpr = createTrueKeyword(); - let enumerableName = createIdentifier("enumerable"); - let enumerableProp = createPropertyAssignment(enumerableName, trueExpr) - startOnNewLine(enumerableProp); - descriptorExpr.properties.push(enumerableProp); - - let configurableName = createIdentifier("configurable"); - let configurableProp = createPropertyAssignment(configurableName, trueExpr); - startOnNewLine(configurableProp); - descriptorExpr.properties.push(configurableProp); - - let definePropertyExpr = createDefinePropertyCall(prefix, name, descriptorExpr); - let definePropertyStmt = createExpressionStatement(definePropertyExpr); - write(definePropertyStmt); - } - - function transformFunctionExpression(node: FunctionExpression, write: (node: Expression) => void): void { - write(transformFunctionLikeExpressionToFunctionExpression(node, node.name, /*location*/ node)); - } - - function transformFunctionLikeExpressionToFunctionExpression(node: FunctionLikeDeclaration, name: Identifier, location: TextRange): FunctionExpression { - let parameters = visitNodes(node.parameters, transformNode); - let statements = flatMapNode(node, transformFunctionBody, PipelineFlags.LexicalEnvironment); - return createFunctionExpression2(name, parameters, createBlock(statements), location); - } - - function transformFunctionDeclaration(node: FunctionDeclaration, write: (node: Statement) => void): void { - let parameters = visitNodes(node.parameters, transformNode); - let statements = flatMapNode(node, transformFunctionBody, PipelineFlags.LexicalEnvironment); - write(createFunctionDeclaration2(node.name, parameters, createBlock(statements), /*location*/ node)); - } - - function transformFunctionBody(node: FunctionLikeDeclaration, write: (node: Statement) => void) { - emitCaptureThisForNode(node, write); - emitDefaultValueAssignments(node, write); - emitRestParameter(node, write); - - let body = node.body; - if (isBlock(body)) { - pipeNodes(body.statements, transformNode, write); - } - else { - let expr = visitNode(body, transformNode); - if (expr) { - let returnStmt = createReturnStatement(expr); - if (!childNodeStartPositionIsOnSameLine(node, body)) { - startOnNewLine(returnStmt); - } - - write(returnStmt); + function getMemberAccessForPropertyName(node: ClassLikeDeclaration, member: ClassElement): LeftHandSideExpression { + let target = getClassMemberPrefix(node, member); + let memberName = member.name; + if (isIdentifier(memberName)) { + return createPropertyAccessExpression2(target, cloneNode(memberName)); + } + else if (isComputedPropertyName(memberName)) { + let expression = visitNode(memberName.expression, transformNode); + return createElementAccessExpression2(target, expression); + } + else { + return createElementAccessExpression2(target, cloneNode(memberName)); } } - } + + function transformThisKeyword(node: LeftHandSideExpression): LeftHandSideExpression { + let container = getThisContainer(transformer, /*includeArrowFunctions*/ true); + if (isArrowFunction(container)) { + let thisName = createIdentifier("_this"); + return thisName; + } - function getExpressionForPropertyName(member: ClassElement): Expression { - let memberName = member.name; - if (isIdentifier(memberName)) { - return createStringLiteral(memberName.text); + return node; } - else if (isComputedPropertyName(memberName)) { - return visitNode(memberName.expression, transformNode); + + function transformInitialSuperCall(node: ExpressionStatement, write: (node: Statement) => void) { + let statement = createDefaultSuperCall(); + return write(statement); } - else { - return cloneNode(memberName); + + function createDefaultSuperCall() { + let superName = createIdentifier("_super"); + let thisExpr = createThisKeyword(); + let argumentsName = createIdentifier("arguments"); + let applyExpr = createApplyCall(superName, thisExpr, argumentsName); + let statement = createExpressionStatement(applyExpr); + startOnNewLine(statement); + return statement; } } - - function getMemberAccessForPropertyName(node: ClassLikeDeclaration, member: ClassElement): LeftHandSideExpression { - let target = getClassMemberPrefix(node, member); - let memberName = member.name; - if (isIdentifier(memberName)) { - return createPropertyAccessExpression2(target, cloneNode(memberName)); - } - else if (isComputedPropertyName(memberName)) { - let expression = visitNode(memberName.expression, transformNode); - return createElementAccessExpression2(target, expression); - } - else { - return createElementAccessExpression2(target, cloneNode(memberName)); - } - } - - function transformThisKeyword(node: LeftHandSideExpression): LeftHandSideExpression { - let container = getThisContainer(transform, /*includeArrowFunctions*/ true); - if (isArrowFunction(container)) { - let thisName = createIdentifier("_this"); - return thisName; - } - - return node; - } - - function transformInitialSuperCall(node: ExpressionStatement, write: (node: Statement) => void) { - let statement = createDefaultSuperCall(); - return write(statement); - } - - function createDefaultSuperCall() { - let superName = createIdentifier("_super"); - let thisExpr = createThisKeyword(); - let argumentsName = createIdentifier("arguments"); - let applyExpr = createApplyCall(superName, thisExpr, argumentsName); - let statement = createExpressionStatement(applyExpr); - startOnNewLine(statement); - return statement; - } } \ No newline at end of file diff --git a/src/compiler/transforms/es6.ts b/src/compiler/transforms/es6.ts index d7b1486fcfe..c79c95df071 100644 --- a/src/compiler/transforms/es6.ts +++ b/src/compiler/transforms/es6.ts @@ -1,1362 +1,1374 @@ /// /*@internal*/ -namespace ts.transform { - let resolver: EmitResolver; - let compilerOptions: CompilerOptions; - let languageVersion: ScriptTarget; - let currentModuleDeclaration: ModuleDeclaration; - let currentClassLikeDeclaration: ClassLikeDeclaration; - let currentBaseTypeNode: ExpressionWithTypeArguments; - let currentConstructor: ConstructorDeclaration; - let currentParametersWithPropertyAssignments: ParameterDeclaration[]; - let currentInstancePropertyAssignments: PropertyDeclaration[]; - let currentEnumLocalName: Identifier; - let currentParameterIndex: number; - - export function toES6(statements: NodeArray) { - resolver = getEmitResolver(); - compilerOptions = getCompilerOptions(); - languageVersion = compilerOptions.target || ScriptTarget.ES3; +namespace ts { + export function toES6(transformer: Transformer, statements: NodeArray) { + let { + accept, + visitNode, + visitNodes, + emitNode, + emitNodes, + pipeNode, + pipeNodes, + getParentNode, + findAncestorNode, + declareLocal, + getGeneratedNameForNode, + hoistVariableDeclaration, + getDeclarationName, + getClassMemberPrefix, + } = transformer; + + let resolver = transformer.getEmitResolver(); + let compilerOptions = transformer.getCompilerOptions(); + let languageVersion = compilerOptions.target || ScriptTarget.ES3; + + let currentModuleDeclaration: ModuleDeclaration; + let currentClassLikeDeclaration: ClassLikeDeclaration; + let currentBaseTypeNode: ExpressionWithTypeArguments; + let currentConstructor: ConstructorDeclaration; + let currentParametersWithPropertyAssignments: ParameterDeclaration[]; + let currentInstancePropertyAssignments: PropertyDeclaration[]; + let currentEnumLocalName: Identifier; + let currentParameterIndex: number; + return visitNodes(statements, transformNode, PipelineFlags.LexicalEnvironment); - } - /** - * Transforms a node from TypeScript to ES6 if it requires any transformations. - * @param context Context information for the transform. - * @param node The node to transform. - * @remarks - * This function is intentionally kept small to keep its overhead low. - * - * If the node needs direct transformation, it will be passed on to the - * `transformNodeWorker` function. - * - * If any part of its subtree needs transformation, the node will be - * passed to the fallback `accept` function which will ensure any changes - * to the subtree will generate new nodes. - * - * If no part of this node or its subtree requires transformation, the node - * is returned, unchanged. - */ - function transformNode(node: Node, write: (node: Node) => void): void { - // Debug.assert( - // !needsTransform(node, TransformFlags.ThisNodeNeedsTransformToES7), - // "Cannot transform node with post-ES7 syntax."); - - if (node.transformFlags & TransformFlags.ThisNodeNeedsTransformToES6) { - transformNodeWorker(node, write); - } - else if (node.transformFlags & TransformFlags.SubtreeNeedsTransformToES6) { - accept(node, transformNode, write); - } - else { - write(node); - } - } - - function transformModuleElement(node: Node, write: (node: Node) => void): void { - if (node.flags & NodeFlags.Export) { - transformNodeWorker(node, write); - } - else { - transformNode(node, write); - } - } - - /** - * Transforms a node from TypeScript to ES6. - * @param context Context information for the transform. - * @param node The node to transform. - */ - function transformNodeWorker(node: Node, write: (node: Node) => void): void { - // TypeScript ambient declarations are elided. - if (node.flags & NodeFlags.Ambient) { - return; - } - - switch (node.kind) { - case SyntaxKind.PublicKeyword: - case SyntaxKind.PrivateKeyword: - case SyntaxKind.ProtectedKeyword: - case SyntaxKind.AbstractKeyword: - case SyntaxKind.AsyncKeyword: - case SyntaxKind.ConstKeyword: - case SyntaxKind.DeclareKeyword: - // TypeScript accessibility modifiers are elided. - return; - - case SyntaxKind.ArrayType: - case SyntaxKind.TupleType: - case SyntaxKind.TypeLiteral: - case SyntaxKind.TypePredicate: - case SyntaxKind.TypeParameter: - case SyntaxKind.AnyKeyword: - case SyntaxKind.BooleanKeyword: - case SyntaxKind.StringKeyword: - case SyntaxKind.NumberKeyword: - case SyntaxKind.VoidKeyword: - case SyntaxKind.ConstructorType: - case SyntaxKind.FunctionType: - case SyntaxKind.TypeQuery: - case SyntaxKind.TypeReference: - case SyntaxKind.UnionType: - case SyntaxKind.IntersectionType: - // TypeScript type nodes are elided. - return; - - case SyntaxKind.IndexSignature: - // TypeScript index signatures are elided. - return; - - case SyntaxKind.Decorator: - // TypeScript decorators are elided. They will be emitted as part of transformClassDeclaration. - return; - - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.TypeAliasDeclaration: - // TypeScript type-only declarations are elided - return; - - case SyntaxKind.PropertyDeclaration: - // TypeScript property declarations are elided. - return; - - case SyntaxKind.Constructor: - // TypeScript constructors are elided. The constructor of a class will be - // reordered to the start of the member list in `transformClassDeclaration`. - return; - - case SyntaxKind.ClassDeclaration: - // This is a class declaration with TypeScript syntax extensions. - // - // TypeScript class syntax extensions include: - // - decorators - // - optional `implements` heritage clause - // - parameter property assignments in the constructor - // - property declarations - // - index signatures - // - method overload signatures - // - async methods - return transformClassDeclaration(node, write); - - case SyntaxKind.ClassExpression: - // This is a class expression with TypeScript syntax extensions. - // - // TypeScript class syntax extensions include: - // - decorators - // - optional `implements` heritage clause - // - parameter property assignments in the constructor - // - property declarations - // - index signatures - // - method overload signatures - // - async methods - return transformClassExpression(node, write); + /** + * Transforms a node from TypeScript to ES6 if it requires any transformations. + * @param context Context information for the transform. + * @param node The node to transform. + * @remarks + * This function is intentionally kept small to keep its overhead low. + * + * If the node needs direct transformation, it will be passed on to the + * `transformNodeWorker` function. + * + * If any part of its subtree needs transformation, the node will be + * passed to the fallback `accept` function which will ensure any changes + * to the subtree will generate new nodes. + * + * If no part of this node or its subtree requires transformation, the node + * is returned, unchanged. + */ + function transformNode(node: Node, write: (node: Node) => void): void { + // Debug.assert( + // !needsTransform(node, TransformFlags.ThisNodeNeedsTransformToES7), + // "Cannot transform node with post-ES7 syntax."); - case SyntaxKind.HeritageClause: - // This is a heritage clause with TypeScript syntax extensions. - // - // TypeScript heritage clause extensions include: - // - `implements` clause - return visitHeritageClause(node, write); - - case SyntaxKind.ExpressionWithTypeArguments: - // TypeScript supports type arguments on an expression in an `extends` heritage clause. - return visitExpressionWithTypeArguments(node, write); - - case SyntaxKind.MethodDeclaration: - // TypeScript method declarations may be 'async', and may have decorators, modifiers - // or type annotations. - return transformMethodDeclaration(node, write); - - case SyntaxKind.GetAccessor: - // Get Accessors can have TypeScript modifiers, decorators, and type annotations. - return transformGetAccessor(node, write); - - case SyntaxKind.SetAccessor: - // Set Accessors can have TypeScript modifiers, decorators, and type annotations. - return transformSetAccessor(node, write); - - case SyntaxKind.FunctionDeclaration: - // TypeScript function declarations may be 'async' - return transformFunctionDeclaration(node, write); - - case SyntaxKind.FunctionExpression: - // TypeScript function expressions may be 'async' - return transformFunctionExpression(node, write); - - case SyntaxKind.ArrowFunction: - // TypeScript arrow functions may be 'async' - return transformArrowFunction(node, write); - - case SyntaxKind.Parameter: - // This is a parameter declaration with TypeScript syntax extensions. - // - // TypeScript parameter declaration syntax extensions include: - // - decorators - // - accessibility modifiers - // - the question mark (?) token for optional parameters - // - type annotations - return transformParameter(node, write); - - case SyntaxKind.TypeAssertionExpression: - // TypeScript type assertions are removed, but their subtrees are preserved. - return write((node).expression); - - case SyntaxKind.AsExpression: - // TypeScript `as` expressions are removed, but their subtrees are preserved. - return write((node).expression); - - case SyntaxKind.EnumDeclaration: - // TypeScript enum declarations do not exist in ES6 and must be rewritten. - return transformEnumDeclaration(node, write); - - case SyntaxKind.AwaitExpression: - // TypeScript 'await' expressions must be transformed. - return transformAwaitExpression(node, write); - - case SyntaxKind.VariableStatement: - // TypeScript namespace exports for variable statements must be transformed. - return transformVariableStatement(node, write); - - case SyntaxKind.ModuleDeclaration: - // TypeScript namespace declarations must be transformed. - return transformModuleDeclaration(node, write); - - case SyntaxKind.ImportEqualsDeclaration: - case SyntaxKind.ExportAssignment: - // TODO(rbuckton): TypeScript extensions we're not yet transforming. - - default: - // Fall back to the default visit behavior as some part of this node's - // subtree requires a transformation. - return accept(node, transformNode, write); - } - } - - /** - * Transforms a TypeScript class declaration with syntax extensions into compatible ES6. - * @param context Context information for the transform. - * @param node The node to transform. - */ - function transformClassDeclaration(node: ClassDeclaration, write: (node: Statement) => void) { - let savedCurrentBaseTypeNode = currentBaseTypeNode; - let savedCurrentClassLikeDeclaration = currentClassLikeDeclaration; - currentClassLikeDeclaration = node; - currentBaseTypeNode = getAndVisitClassExtendsHeritageClauseElement(node); - - let classMembers: ClassElement[] = []; - emitNode(node, emitConstructor, classMembers); - emitNodes(node.members, transformNode, classMembers); - - if (nodeIsDecorated(node)) { - // If the class has been decorated, we need to emit the class as part of a `let` declaration - // to avoid the pitfalls of the doubly-bound class name. - let classExpr = createClassExpression3(currentBaseTypeNode, classMembers); - let varStmt = createLetStatement(getDeclarationName(node), classExpr, /*location*/ node, isTopLevelNonDefaultExport(node)); - write(setOriginalNode(varStmt, node)); - } - else { - let exportFlags = isTopLevelExport(node) ? node.flags & (NodeFlags.Export | NodeFlags.Default) : undefined; - let classDecl = createClassDeclaration2(getDeclarationName(node), currentBaseTypeNode, classMembers, /*location*/ node, exportFlags); - write(setOriginalNode(classDecl, node)); - } - - pipeNodes(getInitializedProperties(node, /*isStatic*/ true), transformPropertyDeclarationToStatement, write); - - transformDecoratorsOfMembers(node, /*isStatic*/ false, write); - transformDecoratorsOfMembers(node, /*isStatic*/ true, write); - transformDecoratorsOfConstructor(node, write); - - if (isNamespaceLevelExport(node)) { - write(createExpressionStatement(createAssignmentExpression(getModuleMemberName(node), getDeclarationName(node)))); - } - else if (isTopLevelDefaultExport(node) && nodeIsDecorated(node)) { - write(createExportDefaultStatement(getDeclarationName(node))); - } - - currentClassLikeDeclaration = savedCurrentClassLikeDeclaration; - currentBaseTypeNode = savedCurrentBaseTypeNode; - } - - function transformClassExpression(node: ClassExpression, write: (node: LeftHandSideExpression) => void) { - let savedCurrentClassLikeDeclaration = currentClassLikeDeclaration; - let savedCurrentBaseTypeNode = currentBaseTypeNode; - currentClassLikeDeclaration = node; - currentBaseTypeNode = getAndVisitClassExtendsHeritageClauseElement(node); - - let classMembers: ClassElement[] = []; - emitNode(node, emitConstructor, classMembers); - emitNodes(node.members, transformNode, classMembers); - - let classExpr = createClassExpression2(getDeclarationName(node), currentBaseTypeNode, classMembers); - let staticPropertyAssignments = getInitializedProperties(node, /*isStatic*/ true); - if (staticPropertyAssignments) { - let expressions: Expression[] = []; - let tempVar = declareLocal(); - expressions.push(createAssignmentExpression(tempVar, classExpr)); - emitNodes(staticPropertyAssignments, transformPropertyDeclarationToExpression, expressions); - expressions.push(tempVar); - write(createParenthesizedExpression(inlineExpressions(expressions))); - } - else { - write(classExpr); - } - - currentClassLikeDeclaration = savedCurrentClassLikeDeclaration; - currentBaseTypeNode = savedCurrentBaseTypeNode; - } - - function getAndVisitClassExtendsHeritageClauseElement(node: ClassLikeDeclaration) { - let heritageClauses = visitNodes(node.heritageClauses, visitHeritageClause); - let extendsClause = heritageClauses && firstOrUndefined(heritageClauses); - let baseTypeNode = extendsClause && firstOrUndefined(extendsClause.types); - return baseTypeNode; - } - - function emitConstructor(node: ClassLikeDeclaration, write: (node: ClassElement) => void): void { - // Check if we have a property assignment inside class declaration. - // If there is a property assignment, we need to emit constructor whether users define it or not - // If there is no property assignment, we can omit constructor if users do not define it - let constructor = getFirstConstructorWithBody(node); - let parameterPropertyAssignments = constructor ? getParametersWithPropertyAssignments(constructor) : undefined; - let instancePropertyAssignments = getInitializedProperties(node, /*isStatic*/ false); - - // For target ES6 and above, if there is no property assignment - // do not emit constructor in class declaration. - if (!parameterPropertyAssignments && !instancePropertyAssignments) { - write(constructor); - return; - } - - let parameters: ParameterDeclaration[] = []; - if (constructor) { - emitNodes(constructor.parameters, transformNode, parameters); - } - else if (currentBaseTypeNode) { - parameters.push(createRestParameter(createIdentifier("args"), /*location*/ undefined, NodeFlags.GeneratedRest)); - } - - let savedCurrentConstructor = currentConstructor; - let savedCurrentParametersWithPropertyAssignments = currentParametersWithPropertyAssignments; - let savedCurrentInstancePropertyAssignments = currentInstancePropertyAssignments; - - let statements = flatMapNode(node, emitConstructorBody, PipelineFlags.LexicalEnvironment) - write(createConstructor2(parameters, createBlock(statements), /*location*/ constructor)); - - currentConstructor = savedCurrentConstructor; - currentParametersWithPropertyAssignments = savedCurrentParametersWithPropertyAssignments; - currentInstancePropertyAssignments = savedCurrentInstancePropertyAssignments; - } - - function emitConstructorBody(node: ClassLikeDeclaration, write: (node: Statement) => void) { - let superCall: ExpressionStatement; - if (currentConstructor) { - if (currentBaseTypeNode) { - superCall = findInitialSuperCall(currentConstructor); - if (superCall) { - write(superCall); - } + if (node.transformFlags & TransformFlags.ThisNodeNeedsTransformToES6) { + transformNodeWorker(node, write); } - - pipeNodes(currentParametersWithPropertyAssignments, emitParameterPropertyAssignment, write); - } - else if (currentBaseTypeNode) { - let callExpr = createCallExpression2(createSuperKeyword(), [createSpreadElementExpression(createIdentifier("args"))]); - write(startOnNewLine(createExpressionStatement(callExpr, /*location*/ undefined, NodeFlags.GeneratedSuper))); - } - - pipeNodes(currentInstancePropertyAssignments, transformPropertyDeclarationToStatement, write); - - if (currentConstructor) { - pipeNodes(skip(currentConstructor.body.statements, superCall ? 1 : 0), transformNode, write); - } - } - - function emitParameterPropertyAssignment(node: ParameterDeclaration, write: (node: Statement) => void) { - let name = cloneNode(node.name); - let propExpr = createPropertyAccessExpression2(createThisKeyword(), name); - let assignExpr = createAssignmentExpression(propExpr, name); - write(startOnNewLine(createExpressionStatement(assignExpr))); - } - - function visitHeritageClause(node: HeritageClause, write: (node: HeritageClause) => void) { - if (node.token === SyntaxKind.ExtendsKeyword) { - write(updateHeritageClause(node, take(visitNodes(node.types, visitExpressionWithTypeArguments), 1))); - } - } - - function visitExpressionWithTypeArguments(node: ExpressionWithTypeArguments, write: (node: ExpressionWithTypeArguments) => void) { - write(updateExpressionWithTypeArguments(node, visitNode(node.expression, transformNode), /*typeArguments*/ undefined)); - } - - function transformPropertyDeclarationToStatement(node: PropertyDeclaration, write: (node: Statement) => void): void { - transformPropertyDeclarationToExpressionOrStatement(node, undefined, write); - } - - function transformPropertyDeclarationToExpression(node: PropertyDeclaration, write: (node: Expression) => void): void { - transformPropertyDeclarationToExpressionOrStatement(node, write, undefined); - } - - function transformPropertyDeclarationToExpressionOrStatement(node: PropertyDeclaration, writeExpression: (node: Expression) => void, writeStatement: (node: Statement) => void): void { - let isStatic = (node.flags & NodeFlags.Static) !== 0; - let target = isStatic ? getDeclarationName(currentClassLikeDeclaration) : createThisKeyword(); - let left = createMemberAccessForPropertyName(target, transformPropertyName(node), /*location*/ node.name); - let initializer = visitNode(node.initializer, transformNode); - let assignExpr = createAssignmentExpression(left, initializer); - setTextRange(assignExpr, node); - if (writeExpression) { - writeExpression(assignExpr); - } - else { - writeStatement(createExpressionStatement(assignExpr)); - } - } - - // emitter.ts:4074 - function getInitializedProperties(node: ClassLikeDeclaration, isStatic: boolean): PropertyDeclaration[] { - let properties: PropertyDeclaration[]; - for (let member of node.members) { - if (member.kind === SyntaxKind.PropertyDeclaration && isStatic === ((member.flags & NodeFlags.Static) !== 0) && (member).initializer) { - if (!properties) { - properties = []; - } - - properties.push(member); + else if (node.transformFlags & TransformFlags.SubtreeNeedsTransformToES6) { + accept(node, transformNode, write); + } + else { + write(node); } } - return properties; - } - - function getParametersWithPropertyAssignments(node: ConstructorDeclaration): ParameterDeclaration[] { - let parameters: ParameterDeclaration[]; - for (let parameter of node.parameters) { - if (isIdentifier(parameter.name) && parameter.flags & NodeFlags.AccessibilityModifier) { - if (!parameters) { - parameters = []; - } - - parameters.push(parameter); + function transformModuleElement(node: Node, write: (node: Node) => void): void { + if (node.flags & NodeFlags.Export) { + transformNodeWorker(node, write); + } + else { + transformNode(node, write); } } - return parameters; - } + /** + * Transforms a node from TypeScript to ES6. + * @param context Context information for the transform. + * @param node The node to transform. + */ + function transformNodeWorker(node: Node, write: (node: Node) => void): void { + // TypeScript ambient declarations are elided. + if (node.flags & NodeFlags.Ambient) { + return; + } + + switch (node.kind) { + case SyntaxKind.PublicKeyword: + case SyntaxKind.PrivateKeyword: + case SyntaxKind.ProtectedKeyword: + case SyntaxKind.AbstractKeyword: + case SyntaxKind.AsyncKeyword: + case SyntaxKind.ConstKeyword: + case SyntaxKind.DeclareKeyword: + // TypeScript accessibility modifiers are elided. + return; + + case SyntaxKind.ArrayType: + case SyntaxKind.TupleType: + case SyntaxKind.TypeLiteral: + case SyntaxKind.TypePredicate: + case SyntaxKind.TypeParameter: + case SyntaxKind.AnyKeyword: + case SyntaxKind.BooleanKeyword: + case SyntaxKind.StringKeyword: + case SyntaxKind.NumberKeyword: + case SyntaxKind.VoidKeyword: + case SyntaxKind.ConstructorType: + case SyntaxKind.FunctionType: + case SyntaxKind.TypeQuery: + case SyntaxKind.TypeReference: + case SyntaxKind.UnionType: + case SyntaxKind.IntersectionType: + // TypeScript type nodes are elided. + return; + + case SyntaxKind.IndexSignature: + // TypeScript index signatures are elided. + return; + + case SyntaxKind.Decorator: + // TypeScript decorators are elided. They will be emitted as part of transformClassDeclaration. + return; + + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.TypeAliasDeclaration: + // TypeScript type-only declarations are elided + return; - function findInitialSuperCall(ctor: ConstructorDeclaration): ExpressionStatement { - if (ctor.body) { - let statement = firstOrUndefined(ctor.body.statements); - if (isExpressionStatement(statement)) { - let expr = statement.expression; - if (isCallExpression(expr)) { - let func = expr.expression; - if (isSuperKeyword(func)) { - return statement; + case SyntaxKind.PropertyDeclaration: + // TypeScript property declarations are elided. + return; + + case SyntaxKind.Constructor: + // TypeScript constructors are elided. The constructor of a class will be + // reordered to the start of the member list in `transformClassDeclaration`. + return; + + case SyntaxKind.ClassDeclaration: + // This is a class declaration with TypeScript syntax extensions. + // + // TypeScript class syntax extensions include: + // - decorators + // - optional `implements` heritage clause + // - parameter property assignments in the constructor + // - property declarations + // - index signatures + // - method overload signatures + // - async methods + return transformClassDeclaration(node, write); + + case SyntaxKind.ClassExpression: + // This is a class expression with TypeScript syntax extensions. + // + // TypeScript class syntax extensions include: + // - decorators + // - optional `implements` heritage clause + // - parameter property assignments in the constructor + // - property declarations + // - index signatures + // - method overload signatures + // - async methods + return transformClassExpression(node, write); + + case SyntaxKind.HeritageClause: + // This is a heritage clause with TypeScript syntax extensions. + // + // TypeScript heritage clause extensions include: + // - `implements` clause + return visitHeritageClause(node, write); + + case SyntaxKind.ExpressionWithTypeArguments: + // TypeScript supports type arguments on an expression in an `extends` heritage clause. + return visitExpressionWithTypeArguments(node, write); + + case SyntaxKind.MethodDeclaration: + // TypeScript method declarations may be 'async', and may have decorators, modifiers + // or type annotations. + return transformMethodDeclaration(node, write); + + case SyntaxKind.GetAccessor: + // Get Accessors can have TypeScript modifiers, decorators, and type annotations. + return transformGetAccessor(node, write); + + case SyntaxKind.SetAccessor: + // Set Accessors can have TypeScript modifiers, decorators, and type annotations. + return transformSetAccessor(node, write); + + case SyntaxKind.FunctionDeclaration: + // TypeScript function declarations may be 'async' + return transformFunctionDeclaration(node, write); + + case SyntaxKind.FunctionExpression: + // TypeScript function expressions may be 'async' + return transformFunctionExpression(node, write); + + case SyntaxKind.ArrowFunction: + // TypeScript arrow functions may be 'async' + return transformArrowFunction(node, write); + + case SyntaxKind.Parameter: + // This is a parameter declaration with TypeScript syntax extensions. + // + // TypeScript parameter declaration syntax extensions include: + // - decorators + // - accessibility modifiers + // - the question mark (?) token for optional parameters + // - type annotations + return transformParameter(node, write); + + case SyntaxKind.TypeAssertionExpression: + // TypeScript type assertions are removed, but their subtrees are preserved. + return write((node).expression); + + case SyntaxKind.AsExpression: + // TypeScript `as` expressions are removed, but their subtrees are preserved. + return write((node).expression); + + case SyntaxKind.EnumDeclaration: + // TypeScript enum declarations do not exist in ES6 and must be rewritten. + return transformEnumDeclaration(node, write); + + case SyntaxKind.AwaitExpression: + // TypeScript 'await' expressions must be transformed. + return transformAwaitExpression(node, write); + + case SyntaxKind.VariableStatement: + // TypeScript namespace exports for variable statements must be transformed. + return transformVariableStatement(node, write); + + case SyntaxKind.ModuleDeclaration: + // TypeScript namespace declarations must be transformed. + return transformModuleDeclaration(node, write); + + case SyntaxKind.ImportEqualsDeclaration: + case SyntaxKind.ExportAssignment: + // TODO(rbuckton): TypeScript extensions we're not yet transforming. + + default: + // Fall back to the default visit behavior as some part of this node's + // subtree requires a transformation. + return accept(node, transformNode, write); + } + } + + /** + * Transforms a TypeScript class declaration with syntax extensions into compatible ES6. + * @param context Context information for the transform. + * @param node The node to transform. + */ + function transformClassDeclaration(node: ClassDeclaration, write: (node: Statement) => void) { + let savedCurrentBaseTypeNode = currentBaseTypeNode; + let savedCurrentClassLikeDeclaration = currentClassLikeDeclaration; + currentClassLikeDeclaration = node; + currentBaseTypeNode = getAndVisitClassExtendsHeritageClauseElement(node); + + let classMembers: ClassElement[] = []; + emitNode(node, emitConstructor, classMembers); + emitNodes(node.members, transformNode, classMembers); + + if (nodeIsDecorated(node)) { + // If the class has been decorated, we need to emit the class as part of a `let` declaration + // to avoid the pitfalls of the doubly-bound class name. + let classExpr = createClassExpression3(currentBaseTypeNode, classMembers); + let varStmt = createLetStatement(getDeclarationName(node), classExpr, /*location*/ node, isTopLevelNonDefaultExport(node)); + write(setOriginalNode(varStmt, node)); + } + else { + let exportFlags = isTopLevelExport(node) ? node.flags & (NodeFlags.Export | NodeFlags.Default) : undefined; + let classDecl = createClassDeclaration2(getDeclarationName(node), currentBaseTypeNode, classMembers, /*location*/ node, exportFlags); + write(setOriginalNode(classDecl, node)); + } + + pipeNodes(getInitializedProperties(node, /*isStatic*/ true), transformPropertyDeclarationToStatement, write); + + transformDecoratorsOfMembers(node, /*isStatic*/ false, write); + transformDecoratorsOfMembers(node, /*isStatic*/ true, write); + transformDecoratorsOfConstructor(node, write); + + if (isNamespaceLevelExport(node)) { + write(createExpressionStatement(createAssignmentExpression(getModuleMemberName(node), getDeclarationName(node)))); + } + else if (isTopLevelDefaultExport(node) && nodeIsDecorated(node)) { + write(createExportDefaultStatement(getDeclarationName(node))); + } + + currentClassLikeDeclaration = savedCurrentClassLikeDeclaration; + currentBaseTypeNode = savedCurrentBaseTypeNode; + } + + function transformClassExpression(node: ClassExpression, write: (node: LeftHandSideExpression) => void) { + let savedCurrentClassLikeDeclaration = currentClassLikeDeclaration; + let savedCurrentBaseTypeNode = currentBaseTypeNode; + currentClassLikeDeclaration = node; + currentBaseTypeNode = getAndVisitClassExtendsHeritageClauseElement(node); + + let classMembers: ClassElement[] = []; + emitNode(node, emitConstructor, classMembers); + emitNodes(node.members, transformNode, classMembers); + + let classExpr = createClassExpression2(getDeclarationName(node), currentBaseTypeNode, classMembers); + let staticPropertyAssignments = getInitializedProperties(node, /*isStatic*/ true); + if (staticPropertyAssignments) { + let expressions: Expression[] = []; + let tempVar = declareLocal(); + expressions.push(createAssignmentExpression(tempVar, classExpr)); + emitNodes(staticPropertyAssignments, transformPropertyDeclarationToExpression, expressions); + expressions.push(tempVar); + write(createParenthesizedExpression(inlineExpressions(expressions))); + } + else { + write(classExpr); + } + + currentClassLikeDeclaration = savedCurrentClassLikeDeclaration; + currentBaseTypeNode = savedCurrentBaseTypeNode; + } + + function getAndVisitClassExtendsHeritageClauseElement(node: ClassLikeDeclaration) { + let heritageClauses = visitNodes(node.heritageClauses, visitHeritageClause); + let extendsClause = heritageClauses && firstOrUndefined(heritageClauses); + let baseTypeNode = extendsClause && firstOrUndefined(extendsClause.types); + return baseTypeNode; + } + + function emitConstructor(node: ClassLikeDeclaration, write: (node: ClassElement) => void): void { + // Check if we have a property assignment inside class declaration. + // If there is a property assignment, we need to emit constructor whether users define it or not + // If there is no property assignment, we can omit constructor if users do not define it + let constructor = getFirstConstructorWithBody(node); + let parameterPropertyAssignments = constructor ? getParametersWithPropertyAssignments(constructor) : undefined; + let instancePropertyAssignments = getInitializedProperties(node, /*isStatic*/ false); + + // For target ES6 and above, if there is no property assignment + // do not emit constructor in class declaration. + if (!parameterPropertyAssignments && !instancePropertyAssignments) { + write(constructor); + return; + } + + let parameters: ParameterDeclaration[] = []; + if (constructor) { + emitNodes(constructor.parameters, transformNode, parameters); + } + else if (currentBaseTypeNode) { + parameters.push(createRestParameter(createIdentifier("args"), /*location*/ undefined, NodeFlags.GeneratedRest)); + } + + let savedCurrentConstructor = currentConstructor; + let savedCurrentParametersWithPropertyAssignments = currentParametersWithPropertyAssignments; + let savedCurrentInstancePropertyAssignments = currentInstancePropertyAssignments; + + let statements: Statement[] = []; + emitNode(node, emitConstructorBody, statements, PipelineFlags.LexicalEnvironment) + write(createConstructor2(parameters, createBlock(statements), /*location*/ constructor)); + + currentConstructor = savedCurrentConstructor; + currentParametersWithPropertyAssignments = savedCurrentParametersWithPropertyAssignments; + currentInstancePropertyAssignments = savedCurrentInstancePropertyAssignments; + } + + function emitConstructorBody(node: ClassLikeDeclaration, write: (node: Statement) => void) { + let superCall: ExpressionStatement; + if (currentConstructor) { + if (currentBaseTypeNode) { + superCall = findInitialSuperCall(currentConstructor); + if (superCall) { + write(superCall); + } + } + + pipeNodes(currentParametersWithPropertyAssignments, emitParameterPropertyAssignment, write); + } + else if (currentBaseTypeNode) { + let callExpr = createCallExpression2(createSuperKeyword(), [createSpreadElementExpression(createIdentifier("args"))]); + write(startOnNewLine(createExpressionStatement(callExpr, /*location*/ undefined, NodeFlags.GeneratedSuper))); + } + + pipeNodes(currentInstancePropertyAssignments, transformPropertyDeclarationToStatement, write); + + if (currentConstructor) { + pipeNodes(skip(currentConstructor.body.statements, superCall ? 1 : 0), transformNode, write); + } + } + + function emitParameterPropertyAssignment(node: ParameterDeclaration, write: (node: Statement) => void) { + let name = cloneNode(node.name); + let propExpr = createPropertyAccessExpression2(createThisKeyword(), name); + let assignExpr = createAssignmentExpression(propExpr, name); + write(startOnNewLine(createExpressionStatement(assignExpr))); + } + + function visitHeritageClause(node: HeritageClause, write: (node: HeritageClause) => void) { + if (node.token === SyntaxKind.ExtendsKeyword) { + write(updateHeritageClause(node, take(visitNodes(node.types, visitExpressionWithTypeArguments), 1))); + } + } + + function visitExpressionWithTypeArguments(node: ExpressionWithTypeArguments, write: (node: ExpressionWithTypeArguments) => void) { + write(updateExpressionWithTypeArguments(node, visitNode(node.expression, transformNode), /*typeArguments*/ undefined)); + } + + function transformPropertyDeclarationToStatement(node: PropertyDeclaration, write: (node: Statement) => void): void { + transformPropertyDeclarationToExpressionOrStatement(node, undefined, write); + } + + function transformPropertyDeclarationToExpression(node: PropertyDeclaration, write: (node: Expression) => void): void { + transformPropertyDeclarationToExpressionOrStatement(node, write, undefined); + } + + function transformPropertyDeclarationToExpressionOrStatement(node: PropertyDeclaration, writeExpression: (node: Expression) => void, writeStatement: (node: Statement) => void): void { + let isStatic = (node.flags & NodeFlags.Static) !== 0; + let target = isStatic ? getDeclarationName(currentClassLikeDeclaration) : createThisKeyword(); + let left = createMemberAccessForPropertyName(target, transformPropertyName(node), /*location*/ node.name); + let initializer = visitNode(node.initializer, transformNode); + let assignExpr = createAssignmentExpression(left, initializer); + setTextRange(assignExpr, node); + if (writeExpression) { + writeExpression(assignExpr); + } + else { + writeStatement(createExpressionStatement(assignExpr)); + } + } + + // emitter.ts:4074 + function getInitializedProperties(node: ClassLikeDeclaration, isStatic: boolean): PropertyDeclaration[] { + let properties: PropertyDeclaration[]; + for (let member of node.members) { + if (member.kind === SyntaxKind.PropertyDeclaration && isStatic === ((member.flags & NodeFlags.Static) !== 0) && (member).initializer) { + if (!properties) { + properties = []; + } + + properties.push(member); + } + } + + return properties; + } + + function getParametersWithPropertyAssignments(node: ConstructorDeclaration): ParameterDeclaration[] { + let parameters: ParameterDeclaration[]; + for (let parameter of node.parameters) { + if (isIdentifier(parameter.name) && parameter.flags & NodeFlags.AccessibilityModifier) { + if (!parameters) { + parameters = []; + } + + parameters.push(parameter); + } + } + + return parameters; + } + + function findInitialSuperCall(ctor: ConstructorDeclaration): ExpressionStatement { + if (ctor.body) { + let statement = firstOrUndefined(ctor.body.statements); + if (isExpressionStatement(statement)) { + let expr = statement.expression; + if (isCallExpression(expr)) { + let func = expr.expression; + if (isSuperKeyword(func)) { + return statement; + } } } } - } - - return undefined; - } - - function transformMethodDeclaration(node: MethodDeclaration, write: (node: ClassElement) => void) { - if (!node.body || node.flags & NodeFlags.Abstract) { - // Abstract methods and method overloads are elided. - return; - } - - let name = transformPropertyName(node); - let parameters = visitNodes(node.parameters, transformNode); - let body = transformFunctionBody(node); - let staticFlag = node.flags & NodeFlags.Static; - write(createMethodDeclaration2(name, parameters, body, /*location*/ node, staticFlag)); - } - - function transformGetAccessor(node: GetAccessorDeclaration, write: (node: ClassElement) => void) { - if (node.flags & NodeFlags.Abstract) { - // Abstract accessors are elided. - return; - } - - let name = transformPropertyName(node); - let parameters = visitNodes(node.parameters, transformNode); - let body = visitNode(node.body, transformNode); - let staticFlag = node.flags & NodeFlags.Static; - write(createGetAccessor2(name, parameters, body, /*location*/ node, staticFlag)); - } - - function transformSetAccessor(node: SetAccessorDeclaration, write: (node: ClassElement) => void) { - if (node.flags & NodeFlags.Abstract) { - // Abstract accessors are elided. - return; - } - - let name = transformPropertyName(node); - let parameters = visitNodes(node.parameters, transformNode); - let body = visitNode(node.body, transformNode); - let staticFlag = node.flags & NodeFlags.Static; - write(createSetAccessor2(name, parameters, body, /*location*/ node, staticFlag)); - } - - function transformFunctionDeclaration(node: FunctionDeclaration, write: (node: Statement) => void) { - if (!node.body) { - // Function overloads are elided. - return; - } - - let thisNodeIsNamespaceExport = isNamespaceLevelExport(node); - let parameters = visitNodes(node.parameters, transformNode); - let body = transformFunctionBody(node); - let flags = !thisNodeIsNamespaceExport ? node.flags & (NodeFlags.Default | NodeFlags.Export) : undefined; - write(createFunctionDeclaration3(node.asteriskToken, node.name, parameters, body, /*location*/ node, flags)); - - if (thisNodeIsNamespaceExport) { - write(createExpressionStatement(createAssignmentExpression(getModuleMemberName(node), cloneNode(node.name)))); - } - } - - function transformFunctionExpression(node: FunctionExpression, write: (node: FunctionExpression) => void) { - let parameters = visitNodes(node.parameters, transformNode); - let body = transformFunctionBody(node); - write(createFunctionExpression3(node.asteriskToken, node.name, parameters, body, /*location*/ node)); - } - - function transformArrowFunction(node: ArrowFunction, write: (node: ArrowFunction) => void) { - let parameters = visitNodes(node.parameters, transformNode); - let body = transformFunctionBody(node); - write(createArrowFunction2(parameters, body, /*location*/ node)); - } - - function transformFunctionBody(node: FunctionLikeDeclaration): Block | Expression { - return isAsyncFunctionLike(node) - ? transformAsyncFunctionBody(node) - : visitNewLexicalEnvironment(node.body, transformNode); - } - - function transformAsyncFunctionBody(node: FunctionLikeDeclaration): Block | Expression { - let promiseConstructor = getEntityNameFromTypeNode(node.type); - let hasLexicalArguments = (resolver.getNodeCheckFlags(node) & NodeCheckFlags.CaptureArguments) !== 0; - let args: string; - - // An async function is emit as an outer function that calls an inner - // generator function. To preserve lexical bindings, we pass the current - // `this` and `arguments` objects to `__awaiter`. The generator function - // passed to `__awaiter` is executed inside of the callback to the - // promise constructor. - // - // The emit for an async arrow without a lexical `arguments` binding might be: - // - // // input - // let a = async (b) => { await b; } - // - // // output - // let a = (b) => __awaiter(this, void 0, void 0, function* () { - // yield b; - // }); - // - // The emit for an async arrow with a lexical `arguments` binding might be: - // - // // input - // let a = async (b) => { await arguments[0]; } - // - // // output - // let a = (b) => __awaiter(this, arguments, void 0, function* (arguments) { - // yield arguments[0]; - // }); - // - // The emit for an async function expression without a lexical `arguments` binding - // might be: - // - // // input - // let a = async function (b) { - // await b; - // } - // - // // output - // let a = function (b) { - // return __awaiter(this, void 0, void 0, function* () { - // yield b; - // }); - // } - // - // The emit for an async function expression with a lexical `arguments` binding - // might be: - // - // // input - // let a = async function (b) { - // await arguments[0]; - // } - // - // // output - // let a = function (b) { - // return __awaiter(this, arguments, void 0, function* (_arguments) { - // yield _arguments[0]; - // }); - // } - // - // The emit for an async function expression with a lexical `arguments` binding - // and a return type annotation might be: - // - // // input - // let a = async function (b): MyPromise { - // await arguments[0]; - // } - // - // // output - // let a = function (b) { - // return __awaiter(this, arguments, MyPromise, function* (_arguments) { - // yield _arguments[0]; - // }); - // } - // - - let body = node.body; - let generatorBody: Block; - if (!isBlock(body)) { - let bodyExpr = visitNode(body, transformNode); - let returnStmt = createReturnStatement(bodyExpr); - generatorBody = createBlock([returnStmt]); - } - else { - generatorBody = visitNode(body, transformNode); - } - - let callExpr = createAwaiterHelperCall(hasLexicalArguments, promiseConstructor, generatorBody); - if (isArrowFunction(node)) { - return callExpr; - } - else { - return createBlock([createReturnStatement(callExpr)], /*location*/ body); - } - } - - function transformParameter(node: ParameterDeclaration, write: (node: ParameterDeclaration) => void) { - let name = visitNode(node.name, transformNode); - let initializer = visitNode(node.initializer, transformNode); - let flags = node.flags & ~NodeFlags.AccessibilityModifier; - write(createParameter2(name, initializer, /*location*/ node, flags)); - } - - function transformVariableStatement(node: VariableStatement, write: (node: Statement) => void) { - pipeNode(node.declarationList, transformVariableDeclarationListToExpressionStatement, write); - } - - function transformVariableDeclarationListToExpressionStatement(node: VariableDeclarationList, write: (node: Statement) => void) { - let expressions = visitNodes(node.declarations, transformVariableDeclarationToExpression); - if (expressions.length) { - write(createExpressionStatement(inlineExpressions(expressions))); - } - } - - function transformVariableDeclarationToExpression(node: VariableDeclaration, write: (node: Expression) => void) { - if (!node.initializer) { - return; - } - - transformBindingElementToExpressionWithParenthesisIfNeeded(node, write, /*parenthesizeObjectLiteralAssignment*/ true); - - let name = node.name; - if (isBindingPattern(name)) { - let expr = visitNode(name, transformBindingPatternToExpression); - let initializer = visitNode(node.initializer, transformNode); - let assignExpr = createAssignmentExpression(expr, initializer); - let parenExpr = createParenthesizedExpression(assignExpr); - write(parenExpr); - } - else { - let name = getModuleMemberName(node); - let initializer = visitNode(node.initializer, transformNode); - let assignExpr = createAssignmentExpression(name, initializer); - write(assignExpr); - } - } - - function transformBindingPatternToExpression(node: BindingPattern, write: (node: Expression) => void) { - switch (node.kind) { - case SyntaxKind.ObjectBindingPattern: - return transformObjectBindingPatternToExpression(node, write); - case SyntaxKind.ArrayBindingPattern: - return transformArrayBindingPatternToExpression(node, write); - } - } - - function transformObjectBindingPatternToExpression(node: ObjectBindingPattern, write: (node: Expression) => void) { - let properties = visitNodes(node.elements, transformBindingElementToObjectLiteralElement); - write(createObjectLiteralExpression2(properties)); - } - - function transformArrayBindingPatternToExpression(node: ArrayBindingPattern, write: (node: Expression) => void) { - let elements = visitNodes(node.elements, transformBindingElementToExpression); - write(createArrayLiteralExpression(elements)); - } - - function transformBindingElementToObjectLiteralElement(node: BindingElement, write: (node: ObjectLiteralElement) => void) { - let propertyName = node.propertyName || node.name; - let expr = visitNode(node, transformBindingElementToExpression); - write(createPropertyAssignment(propertyName, expr)); - } - - function transformBindingElementToExpression(node: BindingElement, write: (node: Expression) => void) { - transformBindingElementToExpressionWithParenthesisIfNeeded(node, write, /*parenthesizeObjectLiteralAssignment*/ false); - } - - function transformBindingElementToExpressionWithParenthesisIfNeeded(node: BindingElement, write: (node: Expression) => void, parenthesizeObjectLiteralAssignment?: boolean) { - let name = node.name; - let expr = isBindingPattern(name) - ? visitNode(name, transformBindingPatternToExpression) - : getModuleMemberName(node); - - let initializer = visitNode(node.initializer, transformNode); - if (initializer) { - expr = createAssignmentExpression(expr, initializer); + return undefined; } - if (parenthesizeObjectLiteralAssignment && isObjectBindingPattern(name)) { - expr = createParenthesizedExpression(expr); - } - else if (node.dotDotDotToken) { - expr = createSpreadElementExpression(expr); - } - - write(expr); - } - - function transformModuleDeclaration(node: ModuleDeclaration, write: (node: Statement) => void) { - if (!shouldEmitModuleDeclaration(node)) { - return; - } - - let savedCurrentModuleDeclaration = currentModuleDeclaration; - currentModuleDeclaration = node; - - let location = node; - if (!isModuleMergedWithClass(node)) { - let exportFlags = isTopLevelExport(node) ? NodeFlags.Export : undefined; - let varDecl = createVariableDeclaration2(node.name); - let varDecls = createVariableDeclarationList([varDecl]); - let varStmt = createVariableStatement2(varDecls, location, exportFlags); - write(varStmt); - location = undefined; - } - - let localName = getGeneratedNameForNode(node); - let localParam = createParameter2(localName); - - let body = node.body; - let moduleBody: Block; - if (isModuleBlock(body)) { - moduleBody = createBlock(visitNodes(body.statements, transformModuleElement, PipelineFlags.LexicalEnvironment)); - } - else { - let inner = visitStatement(body, transformNode); - moduleBody = isBlock(inner) ? inner : createBlock([inner]); - } - - let funcExpr = createFunctionExpression2(/*name*/ undefined, [localParam], moduleBody); - let parenExpr = createParenthesizedExpression(funcExpr); - let moduleMemberName = getModuleMemberName(node); - let moduleStorageObjExpr = createObjectLiteralExpression2(); - let moduleStorageInitExpr = createAssignmentExpression(moduleMemberName, moduleStorageObjExpr); - let moduleStorageExpr = createLogicalOrExpression(moduleMemberName, moduleStorageInitExpr); - let moduleParam: Expression = moduleStorageExpr; - if (isNamespaceLevelExport(node)) { - moduleParam = createAssignmentExpression(cloneNode(node.name), moduleStorageExpr); - } - - let callExpr = createCallExpression2(parenExpr, [moduleParam]); - let callStmt = createExpressionStatement(callExpr, location, NodeFlags.GeneratedNamespace); - write(setOriginalNode(callStmt, node)); - - currentModuleDeclaration = savedCurrentModuleDeclaration; - } - - function shouldEmitModuleDeclaration(node: ModuleDeclaration) { - return isInstantiatedModule(node, compilerOptions.preserveConstEnums || compilerOptions.isolatedModules); - } - - function isModuleMergedWithClass(node: ModuleDeclaration) { - return !!(resolver.getNodeCheckFlags(node) & NodeCheckFlags.LexicalModuleMergesWithClass); - } - - function getExpressionForPropertyName(container: Declaration): Expression { - let name = container.name; - if (isIdentifier(name)) { - return createStringLiteral(name.text); - } - else if (isComputedPropertyName(name)) { - return getGeneratedNameForNode(name); - } - else { - return cloneNode(name); - } - } - - function transformPropertyName(container: ClassElement): PropertyName { - let name = container.name; - if (isComputedPropertyName(name)) { - let expression = visitNode(name.expression, transformNode); - if (nodeCanBeDecorated(container) && nodeIsDecorated(container)) { - let generatedName = getGeneratedNameForNode(name); - hoistVariableDeclaration(generatedName); - expression = createAssignmentExpression(generatedName, expression); - } - - return updateComputedPropertyName(name, expression); - } - else { - return cloneNode(name); - } - } - - function transformEnumDeclaration(node: EnumDeclaration, write: (node: Statement) => void) { - if (!shouldEmitEnumDeclaration(node)) { - // Const enum declarations may be elided. - return; - } - - let savedCurrentEnumLocalName = currentEnumLocalName; - currentEnumLocalName = getGeneratedNameForNode(node); - - let location: TextRange = node; - if (!isNamespaceLevelExport(node)) { - write(createVariableStatement3(node.name, /*initializer*/ undefined, location, isTopLevelExport(node) ? NodeFlags.Export : undefined)); - location = undefined; - } - - let enumStatements: Statement[] = []; - emitNodes(node.members, emitEnumMember, enumStatements, undefined, isStatementNode); - - let enumBody = createBlock(enumStatements); - let localNameParam = createParameter2(currentEnumLocalName); - let enumDecl = createFunctionExpression2(/*name*/ undefined, [localNameParam], enumBody); - let parenExpr = createParenthesizedExpression(enumDecl); - let moduleMemberName = getModuleMemberName(node); - let enumStorageObjectExpr = createObjectLiteralExpression2(); - let enumStorageInitExpr = createAssignmentExpression(moduleMemberName, enumStorageObjectExpr); - let enumStorageExpr = createLogicalOrExpression(moduleMemberName, enumStorageInitExpr); - let callExpr = createCallExpression2(parenExpr, [enumStorageExpr]); - write(createExpressionStatement(callExpr, location)); - - if (isNamespaceLevelExport(node)) { - write(createVariableStatement3(node.name, moduleMemberName)); - } - - currentEnumLocalName = savedCurrentEnumLocalName; - } - - function emitEnumMember(node: EnumMember, write: (node: Statement) => void) { - let enumNameExpr = getExpressionForPropertyName(node); - let enumValueExpr = getEnumMemberDeclarationValue(node); - let enumNameElemExpr = createElementAccessExpression2(currentEnumLocalName, enumNameExpr); - let enumValueAssignExpr = createAssignmentExpression(enumNameElemExpr, enumValueExpr); - let enumValueElemExpr = createElementAccessExpression2(currentEnumLocalName, enumValueAssignExpr); - let enumNameAssignExpr = createAssignmentExpression(enumValueElemExpr, enumNameExpr); - write(createExpressionStatement(enumNameAssignExpr, /*location*/ node)); - } - - function getEnumMemberDeclarationValue(member: EnumMember): Expression { - let value = resolver.getConstantValue(member); - if (value !== undefined) { - return createNumericLiteral2(value); - } - else if (member.initializer) { - return visitNode(member.initializer, transformNode); - } - else { - return createVoidZeroExpression(); - } - } - - function shouldEmitEnumDeclaration(node: EnumDeclaration) { - return isConst(node) || compilerOptions.preserveConstEnums || compilerOptions.isolatedModules; - } - - function transformAwaitExpression(node: AwaitExpression, write: (node: Expression) => void) { - let expression = visitNode(node.expression, transformNode); - let yieldExpr = createYieldExpression(/*asteriskToken*/ undefined, expression, /*location*/ node); - if (needsParenthesisForAwaitExpressionAsYield(node)) { - write(createParenthesizedExpression(yieldExpr)); - } - else { - write(yieldExpr); - } - } - - function needsParenthesisForAwaitExpressionAsYield(node: AwaitExpression) { - let parentNode = getParentNode(); - if (isBinaryExpression(parentNode) && !isAssignmentOperator(parentNode.operatorToken.kind)) { - return true; - } - else if (isConditionalExpression(parentNode) && parentNode.condition === node) { - return true; - } - - return false; - } - - function transformDecoratorsOfMembers(node: ClassLikeDeclaration, isStatic: boolean, statements: (node: Statement) => void) { - for (let member of node.members) { - // only emit members in the correct group - if (isStatic !== ((member.flags & NodeFlags.Static) !== 0)) { - continue; - } - - // skip members that cannot be decorated (such as the constructor) - // skip a member if it or any of its parameters are not decorated - if (!nodeCanBeDecorated(member) || !nodeOrChildIsDecorated(member)) { - continue; - } - - transformDecoratorsOfMember(node, member, statements); - } - } - - function transformDecoratorsOfConstructor(node: ClassLikeDeclaration, write: (node: Statement) => void) { - let decorators = node.decorators; - let constructor = getFirstConstructorWithBody(node); - let hasDecoratedParameters = constructor && forEach(constructor.parameters, nodeIsDecorated); - - // skip decoration of the constructor if neither it nor its parameters are decorated - if (!decorators && !hasDecoratedParameters) { - return; - } - - // Emit the call to __decorate. Given the class: - // - // @dec - // class C { - // } - // - // The emit for the class is: - // - // C = __decorate([dec], C); - // - - let decoratorExpressions: Expression[] = []; - emitNodes(decorators, transformDecoratorToExpression, decoratorExpressions); - emitNode(constructor, emitDecoratorsOfParameters, decoratorExpressions); - - if (compilerOptions.emitDecoratorMetadata) { - emitNode(node, emitSerializedTypeMetadata, decoratorExpressions); - } - - let name = getDeclarationName(node); - let callExpr = createDecorateHelperCall(decoratorExpressions, name); - let statement = createExpressionStatement(callExpr); - write(statement); - } - - function transformDecoratorToExpression(node: Decorator, write: (node: Expression) => void) { - return visitNode(node.expression, transformNode); - } - - function transformDecoratorsOfMember(node: ClassLikeDeclaration, member: ClassElement, write: (node: Statement) => void) { - let decorators: Decorator[]; - let parameters: ParameterDeclaration[]; - - // skip an accessor declaration if it is not the first accessor - if (isAccessor(member) && member.body) { - let accessors = getAllAccessorDeclarations(node.members, member); - if (member !== accessors.firstAccessor) { + function transformMethodDeclaration(node: MethodDeclaration, write: (node: ClassElement) => void) { + if (!node.body || node.flags & NodeFlags.Abstract) { + // Abstract methods and method overloads are elided. return; } - // get the decorators from the first accessor with decorators - decorators = accessors.firstAccessor.decorators; - if (!decorators && accessors.secondAccessor) { - decorators = accessors.secondAccessor.decorators; - } - - // we only decorate parameters of the set accessor - parameters = accessors.setAccessor - ? accessors.setAccessor.parameters - : undefined; + let name = transformPropertyName(node); + let parameters = visitNodes(node.parameters, transformNode); + let body = transformFunctionBody(node); + let staticFlag = node.flags & NodeFlags.Static; + write(createMethodDeclaration2(name, parameters, body, /*location*/ node, staticFlag)); } - else { - decorators = member.decorators; - - // we only decorate the parameters here if this is a method - if (isMethodDeclaration(member) && member.body) { - parameters = member.parameters; + + function transformGetAccessor(node: GetAccessorDeclaration, write: (node: ClassElement) => void) { + if (node.flags & NodeFlags.Abstract) { + // Abstract accessors are elided. + return; + } + + let name = transformPropertyName(node); + let parameters = visitNodes(node.parameters, transformNode); + let body = visitNode(node.body, transformNode); + let staticFlag = node.flags & NodeFlags.Static; + write(createGetAccessor2(name, parameters, body, /*location*/ node, staticFlag)); + } + + function transformSetAccessor(node: SetAccessorDeclaration, write: (node: ClassElement) => void) { + if (node.flags & NodeFlags.Abstract) { + // Abstract accessors are elided. + return; + } + + let name = transformPropertyName(node); + let parameters = visitNodes(node.parameters, transformNode); + let body = visitNode(node.body, transformNode); + let staticFlag = node.flags & NodeFlags.Static; + write(createSetAccessor2(name, parameters, body, /*location*/ node, staticFlag)); + } + + function transformFunctionDeclaration(node: FunctionDeclaration, write: (node: Statement) => void) { + if (!node.body) { + // Function overloads are elided. + return; + } + + let thisNodeIsNamespaceExport = isNamespaceLevelExport(node); + let parameters = visitNodes(node.parameters, transformNode); + let body = transformFunctionBody(node); + let flags = !thisNodeIsNamespaceExport ? node.flags & (NodeFlags.Default | NodeFlags.Export) : undefined; + write(createFunctionDeclaration3(node.asteriskToken, node.name, parameters, body, /*location*/ node, flags)); + + if (thisNodeIsNamespaceExport) { + write(createExpressionStatement(createAssignmentExpression(getModuleMemberName(node), cloneNode(node.name)))); + } + } + + function transformFunctionExpression(node: FunctionExpression, write: (node: FunctionExpression) => void) { + let parameters = visitNodes(node.parameters, transformNode); + let body = transformFunctionBody(node); + write(createFunctionExpression3(node.asteriskToken, node.name, parameters, body, /*location*/ node)); + } + + function transformArrowFunction(node: ArrowFunction, write: (node: ArrowFunction) => void) { + let parameters = visitNodes(node.parameters, transformNode); + let body = transformFunctionBody(node); + write(createArrowFunction2(parameters, body, /*location*/ node)); + } + + function transformFunctionBody(node: FunctionLikeDeclaration): Block | Expression { + return isAsyncFunctionLike(node) + ? transformAsyncFunctionBody(node) + : visitNode(node.body, transformNode, PipelineFlags.LexicalEnvironment); + } + + function transformAsyncFunctionBody(node: FunctionLikeDeclaration): Block | Expression { + let promiseConstructor = getEntityNameFromTypeNode(node.type); + let hasLexicalArguments = (resolver.getNodeCheckFlags(node) & NodeCheckFlags.CaptureArguments) !== 0; + let args: string; + + // An async function is emit as an outer function that calls an inner + // generator function. To preserve lexical bindings, we pass the current + // `this` and `arguments` objects to `__awaiter`. The generator function + // passed to `__awaiter` is executed inside of the callback to the + // promise constructor. + // + // The emit for an async arrow without a lexical `arguments` binding might be: + // + // // input + // let a = async (b) => { await b; } + // + // // output + // let a = (b) => __awaiter(this, void 0, void 0, function* () { + // yield b; + // }); + // + // The emit for an async arrow with a lexical `arguments` binding might be: + // + // // input + // let a = async (b) => { await arguments[0]; } + // + // // output + // let a = (b) => __awaiter(this, arguments, void 0, function* (arguments) { + // yield arguments[0]; + // }); + // + // The emit for an async function expression without a lexical `arguments` binding + // might be: + // + // // input + // let a = async function (b) { + // await b; + // } + // + // // output + // let a = function (b) { + // return __awaiter(this, void 0, void 0, function* () { + // yield b; + // }); + // } + // + // The emit for an async function expression with a lexical `arguments` binding + // might be: + // + // // input + // let a = async function (b) { + // await arguments[0]; + // } + // + // // output + // let a = function (b) { + // return __awaiter(this, arguments, void 0, function* (_arguments) { + // yield _arguments[0]; + // }); + // } + // + // The emit for an async function expression with a lexical `arguments` binding + // and a return type annotation might be: + // + // // input + // let a = async function (b): MyPromise { + // await arguments[0]; + // } + // + // // output + // let a = function (b) { + // return __awaiter(this, arguments, MyPromise, function* (_arguments) { + // yield _arguments[0]; + // }); + // } + // + + let body = node.body; + let generatorBody: Block; + if (!isBlock(body)) { + let bodyExpr = visitNode(body, transformNode); + let returnStmt = createReturnStatement(bodyExpr); + generatorBody = createBlock([returnStmt]); + } + else { + generatorBody = visitNode(body, transformNode); + } + + let callExpr = createAwaiterHelperCall(hasLexicalArguments, promiseConstructor, generatorBody); + if (isArrowFunction(node)) { + return callExpr; + } + else { + return createBlock([createReturnStatement(callExpr)], /*location*/ body); } } - // Emit the call to __decorate. Given the following: - // - // class C { - // @dec method(@dec2 x) {} - // @dec get accessor() {} - // @dec prop; - // } - // - // The emit for a method is: - // - // Object.defineProperty(C.prototype, "method", - // __decorate([ - // dec, - // __param(0, dec2), - // __metadata("design:type", Function), - // __metadata("design:paramtypes", [Object]), - // __metadata("design:returntype", void 0) - // ], C.prototype, "method", Object.getOwnPropertyDescriptor(C.prototype, "method"))); - // - // The emit for an accessor is: - // - // Object.defineProperty(C.prototype, "accessor", - // __decorate([ - // dec - // ], C.prototype, "accessor", Object.getOwnPropertyDescriptor(C.prototype, "accessor"))); - // - // The emit for a property is: - // - // __decorate([ - // dec - // ], C.prototype, "prop"); - // - - let decoratorExpressions: Expression[] = []; - emitNodes(decorators, transformDecoratorToExpression, decoratorExpressions); - emitNodes(parameters, emitDecoratorsOfParameter, decoratorExpressions); - if (compilerOptions.emitDecoratorMetadata) { - emitNode(node, emitSerializedTypeMetadata, decoratorExpressions); + function transformParameter(node: ParameterDeclaration, write: (node: ParameterDeclaration) => void) { + let name = visitNode(node.name, transformNode); + let initializer = visitNode(node.initializer, transformNode); + let flags = node.flags & ~NodeFlags.AccessibilityModifier; + write(createParameter2(name, initializer, /*location*/ node, flags)); } - let prefix = getClassMemberPrefix(node, member); - let memberName = getExpressionForPropertyName(member); - if (isPropertyDeclaration(member)) { - let decorateExpr = createDecorateHelperCall(decoratorExpressions, prefix, memberName); - let statement = createExpressionStatement(decorateExpr); + function transformVariableStatement(node: VariableStatement, write: (node: Statement) => void) { + pipeNode(node.declarationList, transformVariableDeclarationListToExpressionStatement, write); + } + + function transformVariableDeclarationListToExpressionStatement(node: VariableDeclarationList, write: (node: Statement) => void) { + let expressions = visitNodes(node.declarations, transformVariableDeclarationToExpression); + if (expressions.length) { + write(createExpressionStatement(inlineExpressions(expressions))); + } + } + + function transformVariableDeclarationToExpression(node: VariableDeclaration, write: (node: Expression) => void) { + if (!node.initializer) { + return; + } + + transformBindingElementToExpressionWithParenthesisIfNeeded(node, write, /*parenthesizeObjectLiteralAssignment*/ true); + + let name = node.name; + if (isBindingPattern(name)) { + let expr = visitNode(name, transformBindingPatternToExpression); + let initializer = visitNode(node.initializer, transformNode); + let assignExpr = createAssignmentExpression(expr, initializer); + let parenExpr = createParenthesizedExpression(assignExpr); + write(parenExpr); + } + else { + let name = getModuleMemberName(node); + let initializer = visitNode(node.initializer, transformNode); + let assignExpr = createAssignmentExpression(name, initializer); + write(assignExpr); + } + } + + function transformBindingPatternToExpression(node: BindingPattern, write: (node: Expression) => void) { + switch (node.kind) { + case SyntaxKind.ObjectBindingPattern: + return transformObjectBindingPatternToExpression(node, write); + + case SyntaxKind.ArrayBindingPattern: + return transformArrayBindingPatternToExpression(node, write); + } + } + + function transformObjectBindingPatternToExpression(node: ObjectBindingPattern, write: (node: Expression) => void) { + let properties = visitNodes(node.elements, transformBindingElementToObjectLiteralElement); + write(createObjectLiteralExpression2(properties)); + } + + function transformArrayBindingPatternToExpression(node: ArrayBindingPattern, write: (node: Expression) => void) { + let elements = visitNodes(node.elements, transformBindingElementToExpression); + write(createArrayLiteralExpression(elements)); + } + + function transformBindingElementToObjectLiteralElement(node: BindingElement, write: (node: ObjectLiteralElement) => void) { + let propertyName = node.propertyName || node.name; + let expr = visitNode(node, transformBindingElementToExpression); + write(createPropertyAssignment(propertyName, expr)); + } + + function transformBindingElementToExpression(node: BindingElement, write: (node: Expression) => void) { + transformBindingElementToExpressionWithParenthesisIfNeeded(node, write, /*parenthesizeObjectLiteralAssignment*/ false); + } + + function transformBindingElementToExpressionWithParenthesisIfNeeded(node: BindingElement, write: (node: Expression) => void, parenthesizeObjectLiteralAssignment?: boolean) { + let name = node.name; + let expr = isBindingPattern(name) + ? visitNode(name, transformBindingPatternToExpression) + : getModuleMemberName(node); + + let initializer = visitNode(node.initializer, transformNode); + if (initializer) { + expr = createAssignmentExpression(expr, initializer); + } + + if (parenthesizeObjectLiteralAssignment && isObjectBindingPattern(name)) { + expr = createParenthesizedExpression(expr); + } + else if (node.dotDotDotToken) { + expr = createSpreadElementExpression(expr); + } + + write(expr); + } + + function transformModuleDeclaration(node: ModuleDeclaration, write: (node: Statement) => void) { + if (!shouldEmitModuleDeclaration(node)) { + return; + } + + let savedCurrentModuleDeclaration = currentModuleDeclaration; + currentModuleDeclaration = node; + + let location = node; + if (!isModuleMergedWithClass(node)) { + write(createVariableStatement3(node.name, undefined, location, isTopLevelExport(node) ? NodeFlags.Export : undefined)); + location = undefined; + } + + let localName = getGeneratedNameForNode(node); + let localParam = createParameter2(localName); + + let body = node.body; + let moduleBody: Block; + if (isModuleBlock(body)) { + moduleBody = createBlock(visitNodes(body.statements, transformModuleElement, PipelineFlags.LexicalEnvironment)); + } + else { + let inner = visitNode(body, transformNode, PipelineFlags.StatementOrBlock); + moduleBody = isBlock(inner) ? inner : createBlock([inner]); + } + + let funcExpr = createFunctionExpression2(/*name*/ undefined, [localParam], moduleBody); + let parenExpr = createParenthesizedExpression(funcExpr); + let moduleMemberName = getModuleMemberName(node); + let moduleStorageObjExpr = createObjectLiteralExpression2(); + let moduleStorageInitExpr = createAssignmentExpression(moduleMemberName, moduleStorageObjExpr); + let moduleStorageExpr = createLogicalOrExpression(moduleMemberName, moduleStorageInitExpr); + let moduleParam: Expression = moduleStorageExpr; + if (isNamespaceLevelExport(node)) { + moduleParam = createAssignmentExpression(cloneNode(node.name), moduleStorageExpr); + } + + let callExpr = createCallExpression2(parenExpr, [moduleParam]); + let callStmt = createExpressionStatement(callExpr, location, NodeFlags.GeneratedNamespace); + write(setOriginalNode(callStmt, node)); + + currentModuleDeclaration = savedCurrentModuleDeclaration; + } + + function shouldEmitModuleDeclaration(node: ModuleDeclaration) { + return isInstantiatedModule(node, compilerOptions.preserveConstEnums || compilerOptions.isolatedModules); + } + + function isModuleMergedWithClass(node: ModuleDeclaration) { + return !!(resolver.getNodeCheckFlags(node) & NodeCheckFlags.LexicalModuleMergesWithClass); + } + + function getExpressionForPropertyName(container: Declaration): Expression { + let name = container.name; + if (isIdentifier(name)) { + return createStringLiteral(name.text); + } + else if (isComputedPropertyName(name)) { + return getGeneratedNameForNode(name); + } + else { + return cloneNode(name); + } + } + + function transformPropertyName(container: ClassElement): PropertyName { + let name = container.name; + if (isComputedPropertyName(name)) { + let expression = visitNode(name.expression, transformNode); + if (nodeCanBeDecorated(container) && nodeIsDecorated(container)) { + let generatedName = getGeneratedNameForNode(name); + hoistVariableDeclaration(generatedName); + expression = createAssignmentExpression(generatedName, expression); + } + + return updateComputedPropertyName(name, expression); + } + else { + return cloneNode(name); + } + } + + function transformEnumDeclaration(node: EnumDeclaration, write: (node: Statement) => void) { + if (!shouldEmitEnumDeclaration(node)) { + // Const enum declarations may be elided. + return; + } + + let savedCurrentEnumLocalName = currentEnumLocalName; + currentEnumLocalName = getGeneratedNameForNode(node); + + let location: TextRange = node; + if (!isNamespaceLevelExport(node)) { + write(createVariableStatement3(node.name, /*initializer*/ undefined, location, isTopLevelExport(node) ? NodeFlags.Export : undefined)); + location = undefined; + } + + let enumStatements: Statement[] = []; + emitNodes(node.members, emitEnumMember, enumStatements, undefined, isStatementNode); + + let enumBody = createBlock(enumStatements); + let localNameParam = createParameter2(currentEnumLocalName); + let enumDecl = createFunctionExpression2(/*name*/ undefined, [localNameParam], enumBody); + let parenExpr = createParenthesizedExpression(enumDecl); + let moduleMemberName = getModuleMemberName(node); + let enumStorageObjectExpr = createObjectLiteralExpression2(); + let enumStorageInitExpr = createAssignmentExpression(moduleMemberName, enumStorageObjectExpr); + let enumStorageExpr = createLogicalOrExpression(moduleMemberName, enumStorageInitExpr); + let callExpr = createCallExpression2(parenExpr, [enumStorageExpr]); + write(createExpressionStatement(callExpr, location)); + + if (isNamespaceLevelExport(node)) { + write(createVariableStatement3(node.name, moduleMemberName)); + } + + currentEnumLocalName = savedCurrentEnumLocalName; + } + + function emitEnumMember(node: EnumMember, write: (node: Statement) => void) { + let enumNameExpr = getExpressionForPropertyName(node); + let enumValueExpr = getEnumMemberDeclarationValue(node); + let enumNameElemExpr = createElementAccessExpression2(currentEnumLocalName, enumNameExpr); + let enumValueAssignExpr = createAssignmentExpression(enumNameElemExpr, enumValueExpr); + let enumValueElemExpr = createElementAccessExpression2(currentEnumLocalName, enumValueAssignExpr); + let enumNameAssignExpr = createAssignmentExpression(enumValueElemExpr, enumNameExpr); + write(createExpressionStatement(enumNameAssignExpr, /*location*/ node)); + } + + function getEnumMemberDeclarationValue(member: EnumMember): Expression { + let value = resolver.getConstantValue(member); + if (value !== undefined) { + return createNumericLiteral2(value); + } + else if (member.initializer) { + return visitNode(member.initializer, transformNode); + } + else { + return createVoidZeroExpression(); + } + } + + function shouldEmitEnumDeclaration(node: EnumDeclaration) { + return isConst(node) || compilerOptions.preserveConstEnums || compilerOptions.isolatedModules; + } + + function transformAwaitExpression(node: AwaitExpression, write: (node: Expression) => void) { + let expression = visitNode(node.expression, transformNode); + let yieldExpr = createYieldExpression(/*asteriskToken*/ undefined, expression, /*location*/ node); + if (needsParenthesisForAwaitExpressionAsYield(node)) { + write(createParenthesizedExpression(yieldExpr)); + } + else { + write(yieldExpr); + } + } + + function needsParenthesisForAwaitExpressionAsYield(node: AwaitExpression) { + let parentNode = getParentNode(); + if (isBinaryExpression(parentNode) && !isAssignmentOperator(parentNode.operatorToken.kind)) { + return true; + } + else if (isConditionalExpression(parentNode) && parentNode.condition === node) { + return true; + } + + return false; + } + + function transformDecoratorsOfMembers(node: ClassLikeDeclaration, isStatic: boolean, statements: (node: Statement) => void) { + for (let member of node.members) { + // only emit members in the correct group + if (isStatic !== ((member.flags & NodeFlags.Static) !== 0)) { + continue; + } + + // skip members that cannot be decorated (such as the constructor) + // skip a member if it or any of its parameters are not decorated + if (!nodeCanBeDecorated(member) || !nodeOrChildIsDecorated(member)) { + continue; + } + + transformDecoratorsOfMember(node, member, statements); + } + } + + function transformDecoratorsOfConstructor(node: ClassLikeDeclaration, write: (node: Statement) => void) { + let decorators = node.decorators; + let constructor = getFirstConstructorWithBody(node); + let hasDecoratedParameters = constructor && forEach(constructor.parameters, nodeIsDecorated); + + // skip decoration of the constructor if neither it nor its parameters are decorated + if (!decorators && !hasDecoratedParameters) { + return; + } + + // Emit the call to __decorate. Given the class: + // + // @dec + // class C { + // } + // + // The emit for the class is: + // + // C = __decorate([dec], C); + // + + let decoratorExpressions: Expression[] = []; + emitNodes(decorators, transformDecoratorToExpression, decoratorExpressions); + emitNode(constructor, emitDecoratorsOfParameters, decoratorExpressions); + + if (compilerOptions.emitDecoratorMetadata) { + emitNode(node, emitSerializedTypeMetadata, decoratorExpressions); + } + + let name = getDeclarationName(node); + let callExpr = createDecorateHelperCall(decoratorExpressions, name); + let statement = createExpressionStatement(callExpr); write(statement); } - else { - let descriptorExpr = createGetOwnPropertyDescriptorCall(prefix, memberName); - let decorateExpr = createDecorateHelperCall(decoratorExpressions, prefix, memberName, descriptorExpr); - let definePropertyExpr = createDefinePropertyCall(prefix, memberName, decorateExpr); - let statement = createExpressionStatement(definePropertyExpr); - write(statement); - } - } - - function emitDecoratorsOfParameters(node: FunctionLikeDeclaration, write: (node: Expression) => void) { - pipeNodes(node.parameters, emitDecoratorsOfParameter, write); - } - - function emitDecoratorsOfParameter(node: ParameterDeclaration, write: (node: Expression) => void, offset?: number) { - let savedCurrentParameterIndex = currentParameterIndex; - currentParameterIndex = offset; - pipeNodes(node.decorators, emitDecoratorOfParameter, write); - currentParameterIndex = savedCurrentParameterIndex; - } - - function emitDecoratorOfParameter(node: Decorator, write: (node: Expression) => void) { - let decoratorExpr = visitNode(node.expression, transformNode); - write(createParamHelperCall(currentParameterIndex, decoratorExpr)); - } - - function emitSerializedTypeMetadata(node: Declaration, write: (node: Expression) => void) { - if (shouldAppendTypeMetadata(node)) { - let typeExpr = serializeTypeOfNode(node); - let metadataExpr = createMetadataHelperCall("design:type", createArrowFunction2([], typeExpr)); - write(metadataExpr); - } - if (shouldAppendParamTypesMetadata(node)) { - let paramTypesExpr = serializeParameterTypesOfNode(node); - let metadataExpr = createMetadataHelperCall("design:paramtypes", createArrowFunction2([], paramTypesExpr)); - write(metadataExpr); - } - if (shouldAppendReturnTypeMetadata(node)) { - let returnTypeExpr = serializeReturnTypeOfNode(node); - let metadataExpr = createMetadataHelperCall("design:returntype", createArrowFunction2([], returnTypeExpr)); - write(metadataExpr); - } - } - - function shouldAppendTypeMetadata(node: Declaration): boolean { - // This method determines whether to emit the "design:type" metadata based on the node's kind. - // The caller should have already tested whether the node has decorators and whether the emitDecoratorMetadata - // compiler option is set. - switch (node.kind) { - case SyntaxKind.MethodDeclaration: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - case SyntaxKind.PropertyDeclaration: - return true; - } - - return false; - } - - function shouldAppendReturnTypeMetadata(node: Declaration): boolean { - // This method determines whether to emit the "design:returntype" metadata based on the node's kind. - // The caller should have already tested whether the node has decorators and whether the emitDecoratorMetadata - // compiler option is set. - switch (node.kind) { - case SyntaxKind.MethodDeclaration: - return true; - } - return false; - } - - function shouldAppendParamTypesMetadata(node: Declaration): boolean { - // This method determines whether to emit the "design:paramtypes" metadata based on the node's kind. - // The caller should have already tested whether the node has decorators and whether the emitDecoratorMetadata - // compiler option is set. - switch (node.kind) { - case SyntaxKind.ClassDeclaration: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.SetAccessor: - return true; - } - return false; - } - - function serializeTypeOfNode(node: Node): Expression { - if (isPropertyDeclaration(node) || isParameter(node) || isGetAccessor(node)) { - return serializeTypeNode(node.type); - } - else if (isSetAccessor(node)) { - return serializeTypeNode(getSetAccessorTypeAnnotationNode(node)); - } - else if (isClassLike(node) || isFunctionLike(node)) { - return createIdentifier("Function"); - } - else { - return createVoidZeroExpression(); - } - } - - function serializeParameterTypesOfNode(node: Node): Expression { - let valueDeclaration = - isClassLike(node) ? getFirstConstructorWithBody(node) : - isFunctionLike(node) && nodeIsPresent(node.body) ? node : - undefined; - let parameterTypeExpressions: Expression[] = []; - if (valueDeclaration) { - for (let parameter of valueDeclaration.parameters) { - if (parameter.dotDotDotToken) { - let parameterType = parameter.type; - let elementType = - isArrayType(parameterType) ? parameterType.elementType : - isTypeReference(parameterType) && parameterType.typeArguments && singleOrUndefined(parameterType.typeArguments); - parameterTypeExpressions.push(serializeTypeNode(elementType)); + function transformDecoratorToExpression(node: Decorator, write: (node: Expression) => void) { + return visitNode(node.expression, transformNode); + } + + function transformDecoratorsOfMember(node: ClassLikeDeclaration, member: ClassElement, write: (node: Statement) => void) { + let decorators: Decorator[]; + let parameters: ParameterDeclaration[]; + + // skip an accessor declaration if it is not the first accessor + if (isAccessor(member) && member.body) { + let accessors = getAllAccessorDeclarations(node.members, member); + if (member !== accessors.firstAccessor) { + return; } - else { - parameterTypeExpressions.push(serializeTypeOfNode(parameter)); + + // get the decorators from the first accessor with decorators + decorators = accessors.firstAccessor.decorators; + if (!decorators && accessors.secondAccessor) { + decorators = accessors.secondAccessor.decorators; } + + // we only decorate parameters of the set accessor + parameters = accessors.setAccessor + ? accessors.setAccessor.parameters + : undefined; + } + else { + decorators = member.decorators; + + // we only decorate the parameters here if this is a method + if (isMethodDeclaration(member) && member.body) { + parameters = member.parameters; + } + } + + // Emit the call to __decorate. Given the following: + // + // class C { + // @dec method(@dec2 x) {} + // @dec get accessor() {} + // @dec prop; + // } + // + // The emit for a method is: + // + // Object.defineProperty(C.prototype, "method", + // __decorate([ + // dec, + // __param(0, dec2), + // __metadata("design:type", Function), + // __metadata("design:paramtypes", [Object]), + // __metadata("design:returntype", void 0) + // ], C.prototype, "method", Object.getOwnPropertyDescriptor(C.prototype, "method"))); + // + // The emit for an accessor is: + // + // Object.defineProperty(C.prototype, "accessor", + // __decorate([ + // dec + // ], C.prototype, "accessor", Object.getOwnPropertyDescriptor(C.prototype, "accessor"))); + // + // The emit for a property is: + // + // __decorate([ + // dec + // ], C.prototype, "prop"); + // + + let decoratorExpressions: Expression[] = []; + emitNodes(decorators, transformDecoratorToExpression, decoratorExpressions); + emitNodes(parameters, emitDecoratorsOfParameter, decoratorExpressions); + if (compilerOptions.emitDecoratorMetadata) { + emitNode(node, emitSerializedTypeMetadata, decoratorExpressions); + } + + let prefix = getClassMemberPrefix(node, member); + let memberName = getExpressionForPropertyName(member); + if (isPropertyDeclaration(member)) { + let decorateExpr = createDecorateHelperCall(decoratorExpressions, prefix, memberName); + let statement = createExpressionStatement(decorateExpr); + write(statement); + } + else { + let descriptorExpr = createGetOwnPropertyDescriptorCall(prefix, memberName); + let decorateExpr = createDecorateHelperCall(decoratorExpressions, prefix, memberName, descriptorExpr); + let definePropertyExpr = createDefinePropertyCall(prefix, memberName, decorateExpr); + let statement = createExpressionStatement(definePropertyExpr); + write(statement); } } - return createArrayLiteralExpression(parameterTypeExpressions); - } - - function serializeReturnTypeOfNode(node: Node): Expression { - if (isFunctionLike(node)) { - return serializeTypeNode(node.type); + function emitDecoratorsOfParameters(node: FunctionLikeDeclaration, write: (node: Expression) => void) { + pipeNodes(node.parameters, emitDecoratorsOfParameter, write); } - return undefined; - } + function emitDecoratorsOfParameter(node: ParameterDeclaration, write: (node: Expression) => void, offset?: number) { + let savedCurrentParameterIndex = currentParameterIndex; + currentParameterIndex = offset; + pipeNodes(node.decorators, emitDecoratorOfParameter, write); + currentParameterIndex = savedCurrentParameterIndex; + } + + function emitDecoratorOfParameter(node: Decorator, write: (node: Expression) => void) { + let decoratorExpr = visitNode(node.expression, transformNode); + write(createParamHelperCall(currentParameterIndex, decoratorExpr)); + } + + function emitSerializedTypeMetadata(node: Declaration, write: (node: Expression) => void) { + if (shouldAppendTypeMetadata(node)) { + let typeExpr = serializeTypeOfNode(node); + let metadataExpr = createMetadataHelperCall("design:type", createArrowFunction2([], typeExpr)); + write(metadataExpr); + } + if (shouldAppendParamTypesMetadata(node)) { + let paramTypesExpr = serializeParameterTypesOfNode(node); + let metadataExpr = createMetadataHelperCall("design:paramtypes", createArrowFunction2([], paramTypesExpr)); + write(metadataExpr); + } + if (shouldAppendReturnTypeMetadata(node)) { + let returnTypeExpr = serializeReturnTypeOfNode(node); + let metadataExpr = createMetadataHelperCall("design:returntype", createArrowFunction2([], returnTypeExpr)); + write(metadataExpr); + } + } + + function shouldAppendTypeMetadata(node: Declaration): boolean { + // This method determines whether to emit the "design:type" metadata based on the node's kind. + // The caller should have already tested whether the node has decorators and whether the emitDecoratorMetadata + // compiler option is set. + switch (node.kind) { + case SyntaxKind.MethodDeclaration: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + case SyntaxKind.PropertyDeclaration: + return true; + } - function serializeTypeNode(node: TypeNode): Expression { - if (node === undefined) { + return false; + } + + function shouldAppendReturnTypeMetadata(node: Declaration): boolean { + // This method determines whether to emit the "design:returntype" metadata based on the node's kind. + // The caller should have already tested whether the node has decorators and whether the emitDecoratorMetadata + // compiler option is set. + switch (node.kind) { + case SyntaxKind.MethodDeclaration: + return true; + } + return false; + } + + function shouldAppendParamTypesMetadata(node: Declaration): boolean { + // This method determines whether to emit the "design:paramtypes" metadata based on the node's kind. + // The caller should have already tested whether the node has decorators and whether the emitDecoratorMetadata + // compiler option is set. + switch (node.kind) { + case SyntaxKind.ClassDeclaration: + case SyntaxKind.MethodDeclaration: + case SyntaxKind.SetAccessor: + return true; + } + return false; + } + + function serializeTypeOfNode(node: Node): Expression { + if (isPropertyDeclaration(node) || isParameter(node) || isGetAccessor(node)) { + return serializeTypeNode(node.type); + } + else if (isSetAccessor(node)) { + return serializeTypeNode(getSetAccessorTypeAnnotationNode(node)); + } + else if (isClassLike(node) || isFunctionLike(node)) { + return createIdentifier("Function"); + } + else { + return createVoidZeroExpression(); + } + } + + function serializeParameterTypesOfNode(node: Node): Expression { + let valueDeclaration = + isClassLike(node) ? getFirstConstructorWithBody(node) : + isFunctionLike(node) && nodeIsPresent(node.body) ? node : + undefined; + + let parameterTypeExpressions: Expression[] = []; + if (valueDeclaration) { + for (let parameter of valueDeclaration.parameters) { + if (parameter.dotDotDotToken) { + let parameterType = parameter.type; + let elementType = + isArrayType(parameterType) ? parameterType.elementType : + isTypeReference(parameterType) && parameterType.typeArguments && singleOrUndefined(parameterType.typeArguments); + parameterTypeExpressions.push(serializeTypeNode(elementType)); + } + else { + parameterTypeExpressions.push(serializeTypeOfNode(parameter)); + } + } + } + + return createArrayLiteralExpression(parameterTypeExpressions); + } + + function serializeReturnTypeOfNode(node: Node): Expression { + if (isFunctionLike(node)) { + return serializeTypeNode(node.type); + } + + return undefined; + } + + function serializeTypeNode(node: TypeNode): Expression { + if (node === undefined) { + return createIdentifier("Object"); + } + + switch (node.kind) { + case SyntaxKind.VoidKeyword: + return createVoidZeroExpression(); + + case SyntaxKind.ParenthesizedType: + return serializeTypeNode((node).type); + + case SyntaxKind.FunctionType: + case SyntaxKind.ConstructorType: + return createIdentifier("Function"); + + case SyntaxKind.ArrayType: + case SyntaxKind.TupleType: + return createIdentifier("Array"); + + case SyntaxKind.TypePredicate: + case SyntaxKind.BooleanKeyword: + return createIdentifier("Boolean") + + case SyntaxKind.StringKeyword: + case SyntaxKind.StringLiteral: + return createIdentifier("String"); + + case SyntaxKind.NumberKeyword: + return createIdentifier("Number"); + + case SyntaxKind.SymbolKeyword: + return languageVersion < ScriptTarget.ES6 + ? getGlobalSymbolNameWithFallback() + : createIdentifier("Symbol"); + + case SyntaxKind.TypeReference: + return serializeTypeReferenceNode(node); + + case SyntaxKind.TypeQuery: + case SyntaxKind.TypeLiteral: + case SyntaxKind.UnionType: + case SyntaxKind.IntersectionType: + case SyntaxKind.AnyKeyword: + break; + + default: + Debug.fail("Cannot serialize unexpected type node."); + break; + } + return createIdentifier("Object"); } - - switch (node.kind) { - case SyntaxKind.VoidKeyword: - return createVoidZeroExpression(); - - case SyntaxKind.ParenthesizedType: - return serializeTypeNode((node).type); - - case SyntaxKind.FunctionType: - case SyntaxKind.ConstructorType: - return createIdentifier("Function"); - - case SyntaxKind.ArrayType: - case SyntaxKind.TupleType: - return createIdentifier("Array"); - - case SyntaxKind.TypePredicate: - case SyntaxKind.BooleanKeyword: - return createIdentifier("Boolean") - - case SyntaxKind.StringKeyword: - case SyntaxKind.StringLiteral: - return createIdentifier("String"); - - case SyntaxKind.NumberKeyword: - return createIdentifier("Number"); - - case SyntaxKind.SymbolKeyword: - return languageVersion < ScriptTarget.ES6 - ? getGlobalSymbolNameWithFallback() - : createIdentifier("Symbol"); - - case SyntaxKind.TypeReference: - return serializeTypeReferenceNode(node); - - case SyntaxKind.TypeQuery: - case SyntaxKind.TypeLiteral: - case SyntaxKind.UnionType: - case SyntaxKind.IntersectionType: - case SyntaxKind.AnyKeyword: - break; - - default: - Debug.fail("Cannot serialize unexpected type node."); - break; + + /** Serializes a TypeReferenceNode to an appropriate JS constructor value. Used by the __metadata decorator. */ + function serializeTypeReferenceNode(node: TypeReferenceNode) { + Debug.fail("parent pointer"); + let location: Node = node.parent; + while (isDeclaration(location) || isTypeNode(location)) { + location = location.parent; + } + + // Clone the type name and parent it to a location outside of the current declaration. + let typeName = cloneEntityName(node.typeName); + typeName.parent = location; + + let result = resolver.getTypeReferenceSerializationKind(typeName); + switch (result) { + case TypeReferenceSerializationKind.Unknown: + let tempVar = declareLocal(); + let globalObjectName = createIdentifier("Object"); + let typeExpr = serializeEntityNameAsExpression(typeName, /*useFallback*/ true); + let cacheExpr = createAssignmentExpression(tempVar, typeExpr); + let typeOfExpr = createTypeOfExpression(createParenthesizedExpression(cacheExpr)); + let functionLiteral = createStringLiteral("function"); + let equalityExpr = createStrictEqualityExpression(typeOfExpr, functionLiteral); + let logicalAndExpr = createLogicalAndExpression(equalityExpr, tempVar); + let logicalOrExpr = createLogicalOrExpression(logicalAndExpr, globalObjectName); + return logicalOrExpr; + + case TypeReferenceSerializationKind.TypeWithConstructSignatureAndValue: + return serializeEntityNameAsExpression(typeName, /*useFallback*/ false); + + case TypeReferenceSerializationKind.VoidType: + return createVoidZeroExpression(); + + case TypeReferenceSerializationKind.BooleanType: + return createIdentifier("Boolean"); + + case TypeReferenceSerializationKind.NumberLikeType: + return createIdentifier("Number"); + + case TypeReferenceSerializationKind.StringLikeType: + return createIdentifier("String"); + + case TypeReferenceSerializationKind.ArrayLikeType: + return createIdentifier("Array"); + + case TypeReferenceSerializationKind.ESSymbolType: + return languageVersion < ScriptTarget.ES6 + ? getGlobalSymbolNameWithFallback() + : createIdentifier("Symbol"); + + case TypeReferenceSerializationKind.TypeWithCallSignature: + return createIdentifier("Function"); + + case TypeReferenceSerializationKind.ObjectType: + default: + break; + } + + return createIdentifier("Object"); } - - return createIdentifier("Object"); - } - - /** Serializes a TypeReferenceNode to an appropriate JS constructor value. Used by the __metadata decorator. */ - function serializeTypeReferenceNode(node: TypeReferenceNode) { - Debug.fail("parent pointer"); - let location: Node = node.parent; - while (isDeclaration(location) || isTypeNode(location)) { - location = location.parent; + + function serializeEntityNameAsExpression(node: EntityName, useFallback: boolean): Expression { + switch (node.kind) { + case SyntaxKind.Identifier: + let name = cloneNode(node); + if (useFallback) { + let undefinedLiteral = createStringLiteral("undefined"); + let typeOfExpr = createTypeOfExpression(name); + let equalityExpr = createStrictInequalityExpression(typeOfExpr, undefinedLiteral); + let logicalAndExpr = createLogicalAndExpression(equalityExpr, name); + return logicalAndExpr; + } + + return name; + + case SyntaxKind.QualifiedName: + return serializeQualifiedNameAsExpression(node, useFallback); + } } - - // Clone the type name and parent it to a location outside of the current declaration. - let typeName = cloneEntityName(node.typeName); - typeName.parent = location; - - let result = resolver.getTypeReferenceSerializationKind(typeName); - switch (result) { - case TypeReferenceSerializationKind.Unknown: + + function serializeQualifiedNameAsExpression(node: QualifiedName, useFallback: boolean): Expression { + let left: Expression + if (node.left.kind === SyntaxKind.Identifier) { + left = serializeEntityNameAsExpression(node.left, useFallback); + } + else if (useFallback) { let tempVar = declareLocal(); - let globalObjectName = createIdentifier("Object"); - let typeExpr = serializeEntityNameAsExpression(typeName, /*useFallback*/ true); - let cacheExpr = createAssignmentExpression(tempVar, typeExpr); - let typeOfExpr = createTypeOfExpression(createParenthesizedExpression(cacheExpr)); - let functionLiteral = createStringLiteral("function"); - let equalityExpr = createStrictEqualityExpression(typeOfExpr, functionLiteral); - let logicalAndExpr = createLogicalAndExpression(equalityExpr, tempVar); - let logicalOrExpr = createLogicalOrExpression(logicalAndExpr, globalObjectName); - return logicalOrExpr; - - case TypeReferenceSerializationKind.TypeWithConstructSignatureAndValue: - return serializeEntityNameAsExpression(typeName, /*useFallback*/ false); - - case TypeReferenceSerializationKind.VoidType: - return createVoidZeroExpression(); - - case TypeReferenceSerializationKind.BooleanType: - return createIdentifier("Boolean"); - - case TypeReferenceSerializationKind.NumberLikeType: - return createIdentifier("Number"); - - case TypeReferenceSerializationKind.StringLikeType: - return createIdentifier("String"); - - case TypeReferenceSerializationKind.ArrayLikeType: - return createIdentifier("Array"); - - case TypeReferenceSerializationKind.ESSymbolType: - return languageVersion < ScriptTarget.ES6 - ? getGlobalSymbolNameWithFallback() - : createIdentifier("Symbol"); - - case TypeReferenceSerializationKind.TypeWithCallSignature: - return createIdentifier("Function"); - - case TypeReferenceSerializationKind.ObjectType: - default: - break; - } - - return createIdentifier("Object"); - } - - function serializeEntityNameAsExpression(node: EntityName, useFallback: boolean): Expression { - switch (node.kind) { - case SyntaxKind.Identifier: - let name = cloneNode(node); - if (useFallback) { - let undefinedLiteral = createStringLiteral("undefined"); - let typeOfExpr = createTypeOfExpression(name); - let equalityExpr = createStrictInequalityExpression(typeOfExpr, undefinedLiteral); - let logicalAndExpr = createLogicalAndExpression(equalityExpr, name); - return logicalAndExpr; - } - - return name; - - case SyntaxKind.QualifiedName: - return serializeQualifiedNameAsExpression(node, useFallback); - } - } - - function serializeQualifiedNameAsExpression(node: QualifiedName, useFallback: boolean): Expression { - let left: Expression - if (node.left.kind === SyntaxKind.Identifier) { - left = serializeEntityNameAsExpression(node.left, useFallback); - } - else if (useFallback) { - let tempVar = declareLocal(); - let pathExpr = serializeEntityNameAsExpression(node.left, /*useFallback*/ true); - let cacheExpr = createAssignmentExpression(tempVar, pathExpr); - left = createLogicalAndExpression(cacheExpr, tempVar); - } - else { - left = serializeEntityNameAsExpression(node.left, /*useFallback*/ false); - } - - // we clone the node here to create a copy of the node with no position information - let right = cloneNode(node.right); - let propExpr = createPropertyAccessExpression2(left, right); - return propExpr; - } - - function getGlobalSymbolNameWithFallback(): Expression { - let globalSymbolName = createIdentifier("Symbol"); - let globalObjectName = createIdentifier("Object"); - let typeOfExpr = createTypeOfExpression(globalSymbolName); - let functionLiteral = createStringLiteral("function"); - let equalityExpr = createStrictEqualityExpression(typeOfExpr, functionLiteral); - let conditionalExpr = createConditionalExpression2(equalityExpr, globalSymbolName, globalObjectName); - return conditionalExpr; - } - - function isTopLevelExport(node: Node) { - return !!(node.flags & NodeFlags.Export) && isSourceFile(getParentNode()); - } - - function isTopLevelDefaultExport(node: Node) { - return isTopLevelExport(node) && !!(node.flags & NodeFlags.Default); - } - - function isTopLevelNonDefaultExport(node: Node) { - return isTopLevelExport(node) && !(node.flags & NodeFlags.Default); - } - - function isNamespaceLevelExport(node: Node) { - return !!(node.flags & NodeFlags.Export) && !isSourceFile(getParentNode()); - } - - function getContainingModule(): ModuleDeclaration { - return findAncestorNode(isModuleDeclaration); - } - - function getContainingModuleName(): Identifier { - let container = findAncestorNode(isModuleDeclaration); - return container ? getGeneratedNameForNode(container) : createIdentifier("exports"); - } - - function getModuleMemberName(node: Declaration): Expression { - let name = getDeclarationName(node); - Debug.assert(isIdentifier(name)); - - if (getCombinedNodeFlags(transform) & NodeFlags.Export) { - let container = getContainingModuleName(); - let propExpr = createPropertyAccessExpression2(container, name); + let pathExpr = serializeEntityNameAsExpression(node.left, /*useFallback*/ true); + let cacheExpr = createAssignmentExpression(tempVar, pathExpr); + left = createLogicalAndExpression(cacheExpr, tempVar); + } + else { + left = serializeEntityNameAsExpression(node.left, /*useFallback*/ false); + } + + // we clone the node here to create a copy of the node with no position information + let right = cloneNode(node.right); + let propExpr = createPropertyAccessExpression2(left, right); return propExpr; } - return name; + + function getGlobalSymbolNameWithFallback(): Expression { + let globalSymbolName = createIdentifier("Symbol"); + let globalObjectName = createIdentifier("Object"); + let typeOfExpr = createTypeOfExpression(globalSymbolName); + let functionLiteral = createStringLiteral("function"); + let equalityExpr = createStrictEqualityExpression(typeOfExpr, functionLiteral); + let conditionalExpr = createConditionalExpression2(equalityExpr, globalSymbolName, globalObjectName); + return conditionalExpr; + } + + function isTopLevelExport(node: Node) { + return !!(node.flags & NodeFlags.Export) && isSourceFile(getParentNode()); + } + + function isTopLevelDefaultExport(node: Node) { + return isTopLevelExport(node) && !!(node.flags & NodeFlags.Default); + } + + function isTopLevelNonDefaultExport(node: Node) { + return isTopLevelExport(node) && !(node.flags & NodeFlags.Default); + } + + function isNamespaceLevelExport(node: Node) { + return !!(node.flags & NodeFlags.Export) && !isSourceFile(getParentNode()); + } + + function getContainingModule(): ModuleDeclaration { + return findAncestorNode(isModuleDeclaration); + } + + function getContainingModuleName(): Identifier { + let container = findAncestorNode(isModuleDeclaration); + return container ? getGeneratedNameForNode(container) : createIdentifier("exports"); + } + + function getModuleMemberName(node: Declaration): Expression { + let name = getDeclarationName(node); + Debug.assert(isIdentifier(name)); + + if (getCombinedNodeFlags(transformer) & NodeFlags.Export) { + let container = getContainingModuleName(); + let propExpr = createPropertyAccessExpression2(container, name); + return propExpr; + } + return name; + } } } \ No newline at end of file diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 9e9f367f8a4..1029ec1c148 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1411,16 +1411,11 @@ namespace ts { // @kind(SyntaxKind.IfStatement) export interface IfStatement extends Statement { expression: Expression; - - // @visitor("visitStatement") thenStatement: Statement; - - // @visitor("visitStatement") elseStatement?: Statement; } export interface IterationStatement extends Statement { - // @visitor("visitStatement") statement: Statement; } @@ -1472,8 +1467,6 @@ namespace ts { // @kind(SyntaxKind.WithStatement) export interface WithStatement extends Statement { expression: Expression; - - // @visitor("visitStatement") statement: Statement; } @@ -1504,8 +1497,6 @@ namespace ts { // @kind(SyntaxKind.LabeledStatement) export interface LabeledStatement extends Statement { label: Identifier; - - // @visitor("visitStatement") statement: Statement; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index a269e66863b..09676eaeacf 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1707,6 +1707,17 @@ namespace ts { return pos === -1; } + export function childNodeStartPositionIsOnSameLine(sourceFile: SourceFile, parent: Node, child: Node) { + if (nodeIsSynthesized(child)) { + return !(child).startsOnNewLine; + } + if (nodeIsSynthesized(parent)) { + return false; + } + return getLineOfLocalPosition(sourceFile, skipTrivia(sourceFile.text, parent.pos)) === + getLineOfLocalPosition(sourceFile, skipTrivia(sourceFile.text, child.pos)); + } + export function createDiagnosticCollection(): DiagnosticCollection { let nonFileDiagnostics: Diagnostic[] = []; let fileDiagnostics: Map = {};