From 36fad618f6a7832233285078f059d16d8492d7f9 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 14 Sep 2015 13:34:42 -0700 Subject: [PATCH] Fixed issue with transform flag computation in binder, and emitTokenText when the position is synthesized. --- src/compiler/binder.ts | 8 +- src/compiler/core.ts | 15 + src/compiler/emitter.ts | 10 +- src/compiler/factory.ts | 744 ++++++++++++++++----------------- src/compiler/transform.ts | 90 ++-- src/compiler/transforms/es5.ts | 37 +- src/compiler/transforms/es6.ts | 311 +++++++------- src/compiler/types.ts | 4 + 8 files changed, 636 insertions(+), 583 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 1ee3332b824..d3e67eab210 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -101,7 +101,7 @@ namespace ts { let symbolCount = 0; let Symbol = objectAllocator.getSymbolConstructor(); let classifiableNames: Map = {}; - let transformFlags: TransformFlags; + let subtreeTransformFlags: TransformFlags; let skipTransformFlagAggregation: boolean; if (!file.locals) { @@ -817,10 +817,10 @@ namespace ts { function aggregateTransformFlagsAnd(cbNode: (node: Node) => T, node: Node): T { if (!skipTransformFlagAggregation) { - let saveTransformFlags = transformFlags; - transformFlags = 0; + let savedSubtreeTransformFlags = subtreeTransformFlags; + subtreeTransformFlags = 0; let result = cbNode(node); - transformFlags = saveTransformFlags | computeTransformFlagsForNode(node, transformFlags); + subtreeTransformFlags = savedSubtreeTransformFlags | (computeTransformFlagsForNode(node, subtreeTransformFlags) & ~TransformFlags.NodeExcludes); return result; } diff --git a/src/compiler/core.ts b/src/compiler/core.ts index af3106d12e1..c58253390f4 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -203,6 +203,21 @@ namespace ts { return result; } + export function skip(array: T[], count: number) { + if (array) { + return count === 0 ? array : array.slice(count); + } + return undefined; + } + + export function take(array: T[], count: number) { + if (array) { + return count === array.length ? array : array.slice(0, count); + } + + return undefined; + } + export function append(to: T[], ...values: T[]): T[] { let result: T[]; if (to) { diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 3a7aa59d215..949cc7b6445 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -512,7 +512,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi return tokenEndPos; } else { - emitTokenText(tokenKind, startPos, emitFn); + return emitTokenText(tokenKind, startPos, emitFn); } } @@ -799,7 +799,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi else { write(tokenString); } - return startPos + tokenString.length; + return positionIsSynthesized(startPos) ? -1 : startPos + tokenString.length; } function emitOptional(prefix: string, node: Node) { @@ -5038,7 +5038,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitClassLikeDeclarationBelowES6(node: ClassLikeDeclaration) { - Debug.assert(!compilerOptions.experimentalTransforms, "This function should not be called when using '--experimentalTransforms'."); + // Debug.assert(!compilerOptions.experimentalTransforms, "This function should not be called when using '--experimentalTransforms'."); verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); if (node.kind === SyntaxKind.ClassDeclaration) { @@ -5723,7 +5723,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitEnumDeclaration(node: EnumDeclaration) { - Debug.assert(!compilerOptions.experimentalTransforms, "This function should not be called when using '--experimentalTransforms'."); + // Debug.assert(!compilerOptions.experimentalTransforms, "This function should not be called when using '--experimentalTransforms'."); verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); // const enums are completely erased during compilation. @@ -5845,7 +5845,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitModuleDeclaration(node: ModuleDeclaration) { - Debug.assert(!compilerOptions.experimentalTransforms, "This function should not be called when using '--experimentalTransforms'."); + // Debug.assert(!compilerOptions.experimentalTransforms, "This function should not be called when using '--experimentalTransforms'."); verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index f87d7874247..cdcb9be5fe1 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -7,409 +7,407 @@ namespace ts { return nodeConstructors[kind] || (nodeConstructors[kind] = objectAllocator.getNodeConstructor(kind)); } - // export function createNode(kind: SyntaxKind): T { - // return createNode(kind); - // } + export function setNodeFlags(node: T, flags: NodeFlags): T { + if (!node || flags === undefined) { + return node; + } + + node.flags = flags; + return node; + } + + export function setTextRange(node: T, range: TextRange): T { + if (!node || !range) { + return node; + } + + node.pos = range.pos; + node.end = range.end; + return node; + } + + export function setModifiers(node: T, modifiers: Node[]): T { + if (modifiers) { + node.modifiers = createModifiersArray(modifiers); + node.flags |= node.modifiers.flags; + } + + return node; + } - // // @internal - // export namespace factory { - export function setNodeFlags(node: T, flags: NodeFlags): T { - if (!node || flags === undefined) { - return node; - } + export function setOriginalNode(node: T, original: Node): T { + node.original = node; + return node; + } + + export function attachCommentRanges(node: T, leadingCommentRanges: CommentRange[], trailingCommentRanges?: CommentRange[]): T { + (node).leadingCommentRanges = leadingCommentRanges; + (node).trailingCommentRanges = trailingCommentRanges; + return node; + } + + export function startOnNewLine(node: T): T { + (node).startsOnNewLine = true; + return node; + } + + export function updateFrom(oldNode: T, newNode: T): T { + let flags = oldNode.flags; + if (oldNode.modifiers) { + flags &= ~oldNode.modifiers.flags; + } + + if (newNode.modifiers) { + flags |= newNode.modifiers.flags; + } + + newNode.flags = flags; + newNode.original = oldNode; + newNode.pos = oldNode.pos; + newNode.end = oldNode.end; + //mergeCommentRanges(oldNode, newNode); + return newNode; + } + + function mergeCommentRanges(oldNode: Node, newNode: Node) { + if ((oldNode).leadingCommentRanges && !(newNode).leadingCommentRanges) { + (newNode).leadingCommentRanges = (oldNode).leadingCommentRanges; + } + if ((oldNode).trailingCommentRanges && !(newNode).trailingCommentRanges) { + (newNode).trailingCommentRanges = (oldNode).trailingCommentRanges; + } + } + + export function cloneNodeArray(array: NodeArray): NodeArray { + return array ? createNodeArray(array.slice(0), /*location*/ array) : undefined; + } + + export function createNode(kind: SyntaxKind, location?: TextRange, flags?: NodeFlags): T { + let node = new (getNodeConstructor(kind))(); + if (location) { + node.pos = location.pos; + node.end = location.end; + } + if (flags) { node.flags = flags; - return node; } - - export function setTextRange(node: T, range: TextRange): T { - if (!node || !range) { - return node; - } - - node.pos = range.pos; - node.end = range.end; - return node; + return node; + } + + export function createNodeArray(elements?: T[], location?: TextRange) { + let nodes = >(elements || []); + if (location) { + nodes.pos = location.pos; + nodes.end = location.end; + } + else if (nodes.pos === undefined) { + nodes.pos = -1; + nodes.end = -1; } - export function setModifiers(node: T, modifiers: Node[]): T { - if (modifiers) { - node.modifiers = createModifiersArray(modifiers); - node.flags |= node.modifiers.flags; - } - - return node; - } - - export function attachCommentRanges(node: T, leadingCommentRanges: CommentRange[], trailingCommentRanges?: CommentRange[]): T { - (node).leadingCommentRanges = leadingCommentRanges; - (node).trailingCommentRanges = trailingCommentRanges; - return node; - } + return nodes; + } - export function startOnNewLine(node: T): T { - (node).startsOnNewLine = true; - return node; - } - - export function updateFrom(oldNode: T, newNode: T): T { - let flags = oldNode.flags; - if (oldNode.modifiers) { - flags &= ~oldNode.modifiers.flags; - } - - if (newNode.modifiers) { - flags |= newNode.modifiers.flags; - } - - newNode.flags = flags; - newNode.original = oldNode; - newNode.pos = oldNode.pos; - newNode.end = oldNode.end; - - //mergeCommentRanges(oldNode, newNode); - return newNode; - } - - function mergeCommentRanges(oldNode: Node, newNode: Node) { - if ((oldNode).leadingCommentRanges && !(newNode).leadingCommentRanges) { - (newNode).leadingCommentRanges = (oldNode).leadingCommentRanges; - } - if ((oldNode).trailingCommentRanges && !(newNode).trailingCommentRanges) { - (newNode).trailingCommentRanges = (oldNode).trailingCommentRanges; - } - } - - export function cloneNodeArray(array: NodeArray): NodeArray { - return array ? createNodeArray(array.slice(0), /*location*/ array) : undefined; - } - - export function createNode(kind: SyntaxKind, location?: TextRange, flags?: NodeFlags): T { - let node = new (getNodeConstructor(kind))(); - if (location) { - node.pos = location.pos; - node.end = location.end; - } - if (flags) { - node.flags = flags; - } - return node; - } - - export function createNodeArray(elements?: T[], location?: TextRange) { - let nodes = >(elements || []); - if (location) { - nodes.pos = location.pos; - nodes.end = location.end; - } - else if (nodes.pos === undefined) { - nodes.pos = -1; - nodes.end = -1; - } - - return nodes; - } - - export function createModifiersArray(elements?: Node[], location?: TextRange) { - let modifiers = createNodeArray(elements || [], location); - if (modifiers.flags === undefined) { - let flags = 0; - for (let modifier of modifiers) { - flags |= modifierToFlag(modifier.kind); - } - - modifiers.flags = flags; - } - - return modifiers; - } - - // export function createSourceFileNode(): SourceFile { - // let node = createNode(SyntaxKind.SourceFile); - // return node; - // } - - // export function updateSourceFileNode(node: SourceFile, statements: NodeArray, endOfFileToken: Node): SourceFile { - // if (statements !== node.statements || endOfFileToken !== node.endOfFileToken) { - // let newNode = createNode(SyntaxKind.SourceFile); - // newNode.statements = statements; - // newNode.endOfFileToken = endOfFileToken; - // return updateFrom(node, newNode); - // } - // return node; - // } - - export function createNumericLiteral2(value: number, location?: TextRange, flags?: NodeFlags): LiteralExpression { - let node = createNumericLiteral(String(value), location, flags); - return node; - } - - export function createPropertyAccessExpression2(expression: Expression, name: Identifier, location?: TextRange, flags?: NodeFlags) { - return createPropertyAccessExpression(parenthesizeForAccess(expression), createNode(SyntaxKind.DotToken), name, location, flags); - } - - export function createPropertyAccessExpression3(expression: Expression, name: string, location?: TextRange, flags?: NodeFlags) { - return createPropertyAccessExpression(parenthesizeForAccess(expression), createNode(SyntaxKind.DotToken), createIdentifier(name), location, flags); - } - - export function makeSynthesized(node: TNode): TNode { - return nodeIsSynthesized(node) ? node : cloneNode(node); - } - - export function parenthesizeForBinary(expr: Expression, operator: SyntaxKind) { - // When diagnosing whether the expression needs parentheses, the decision should be based - // on the innermost expression in a chain of nested type assertions. - while (expr.kind === SyntaxKind.TypeAssertionExpression || expr.kind === SyntaxKind.AsExpression) { - expr = (expr).expression; - } - - // If the resulting expression is already parenthesized, we do not need to do any further processing. - if (isParenthesizedExpression(expr)) { - return expr; - } - - let exprPrecedence = getExpressionPrecedence(expr); - let operatorPrecedence = getBinaryOperatorPrecedence(operator); - if (exprPrecedence < operatorPrecedence) { - // lower precedence, the expression needs parenthesis - return createParenthesizedExpression(expr); - } - else { - // higher precedence. - return expr; - } - } - - export function parenthesizeForAccess(expr: Expression): LeftHandSideExpression { - // When diagnosing whether the expression needs parentheses, the decision should be based - // on the innermost expression in a chain of nested type assertions. - while (expr.kind === SyntaxKind.TypeAssertionExpression || expr.kind === SyntaxKind.AsExpression) { - expr = (expr).expression; + export function createModifiersArray(elements?: Node[], location?: TextRange) { + let modifiers = createNodeArray(elements || [], location); + if (modifiers.flags === undefined) { + let flags = 0; + for (let modifier of modifiers) { + flags |= modifierToFlag(modifier.kind); } - // isLeftHandSideExpression is almost the correct criterion for when it is not necessary - // to parenthesize the expression before a dot. The known exceptions are: - // - // NewExpression: - // new C.x -> not the same as (new C).x - // NumberLiteral - // 1.x -> not the same as (1).x - // - if (isLeftHandSideExpression(expr) && - expr.kind !== SyntaxKind.NewExpression && - expr.kind !== SyntaxKind.NumericLiteral) { + modifiers.flags = flags; + } - return expr; - } + return modifiers; + } + + // export function createSourceFileNode(): SourceFile { + // let node = createNode(SyntaxKind.SourceFile); + // return node; + // } + + // export function updateSourceFileNode(node: SourceFile, statements: NodeArray, endOfFileToken: Node): SourceFile { + // if (statements !== node.statements || endOfFileToken !== node.endOfFileToken) { + // let newNode = createNode(SyntaxKind.SourceFile); + // newNode.statements = statements; + // newNode.endOfFileToken = endOfFileToken; + // return updateFrom(node, newNode); + // } + // return node; + // } + + export function createNumericLiteral2(value: number, location?: TextRange, flags?: NodeFlags): LiteralExpression { + let node = createNumericLiteral(String(value), location, flags); + return node; + } + + export function createPropertyAccessExpression2(expression: Expression, name: Identifier, location?: TextRange, flags?: NodeFlags) { + return createPropertyAccessExpression(parenthesizeForAccess(expression), createNode(SyntaxKind.DotToken), name, location, flags); + } + + export function createPropertyAccessExpression3(expression: Expression, name: string, location?: TextRange, flags?: NodeFlags) { + return createPropertyAccessExpression(parenthesizeForAccess(expression), createNode(SyntaxKind.DotToken), createIdentifier(name), location, flags); + } + + export function makeSynthesized(node: TNode): TNode { + return nodeIsSynthesized(node) ? node : cloneNode(node); + } + + export function parenthesizeForBinary(expr: Expression, operator: SyntaxKind) { + // When diagnosing whether the expression needs parentheses, the decision should be based + // on the innermost expression in a chain of nested type assertions. + while (expr.kind === SyntaxKind.TypeAssertionExpression || expr.kind === SyntaxKind.AsExpression) { + expr = (expr).expression; + } + // If the resulting expression is already parenthesized, we do not need to do any further processing. + if (isParenthesizedExpression(expr)) { + return expr; + } + + let exprPrecedence = getExpressionPrecedence(expr); + let operatorPrecedence = getBinaryOperatorPrecedence(operator); + if (exprPrecedence < operatorPrecedence) { + // lower precedence, the expression needs parenthesis return createParenthesizedExpression(expr); } + else { + // higher precedence. + return expr; + } + } - export function createCallExpression2(expression: Expression, _arguments?: Expression[], location?: TextRange, flags?: NodeFlags) { - return createCallExpression(parenthesizeForAccess(expression), undefined, _arguments, location, flags); - } - - export function createObjectLiteralExpression2(properties?: ObjectLiteralElement[]) { - return createObjectLiteralExpression(undefined, undefined, createNodeArray(properties)); - } - - export function createAssignmentExpression(left: Expression, right: Expression) { - return createBinaryExpression2(left, SyntaxKind.EqualsToken, right); - } - - export function createStrictEqualityExpression(left: Expression, right: Expression) { - return createBinaryExpression2(left, SyntaxKind.EqualsEqualsEqualsToken, right); - } - - export function createStrictInequalityExpression(left: Expression, right: Expression) { - return createBinaryExpression2(left, SyntaxKind.ExclamationEqualsEqualsToken, right); - } - - export function createLogicalAndExpression(left: Expression, right: Expression) { - return createBinaryExpression2(left, SyntaxKind.AmpersandAmpersandToken, right); - } - - export function createLogicalOrExpression(left: Expression, right: Expression) { - return createBinaryExpression2(left, SyntaxKind.BarBarToken, right); - } - - export function createCommaExpression(left: Expression, right: Expression) { - return createBinaryExpression2(left, SyntaxKind.CommaToken, right); + export function parenthesizeForAccess(expr: Expression): LeftHandSideExpression { + // When diagnosing whether the expression needs parentheses, the decision should be based + // on the innermost expression in a chain of nested type assertions. + while (expr.kind === SyntaxKind.TypeAssertionExpression || expr.kind === SyntaxKind.AsExpression) { + expr = (expr).expression; } - export function createBinaryExpression2(left: Expression, operator: SyntaxKind, right: Expression) { - return createBinaryExpression(parenthesizeForBinary(left, operator), createNode(operator), parenthesizeForBinary(right, operator)); - } - - export function createConditionalExpression2(condition: Expression, whenTrue: Expression, whenFalse: Expression, location?: TextRange, flags?: NodeFlags) { - return createConditionalExpression(condition, createNode(SyntaxKind.QuestionToken), whenTrue, createNode(SyntaxKind.ColonToken), whenFalse, location, flags); - } - - export function createParameter2(name: BindingPattern | Identifier, initializer?: Expression, location?: TextRange, flags?: NodeFlags) { - return createParameter(undefined, undefined, undefined, name, undefined, undefined, initializer, location, flags); - } - - export function createRestParameter(name: Identifier, location?: TextRange, flags?: NodeFlags) { - return createParameter(undefined, undefined, createNode(SyntaxKind.DotDotDotToken), name, undefined, undefined, undefined, location, flags); - } - - export function createVariableDeclaration2(name: Identifier | BindingPattern, initializer?: Expression, location?: TextRange, flags?: NodeFlags) { - return createVariableDeclaration(name, undefined, initializer, location, flags); - } - - export function createVariableStatement2(declarationList: VariableDeclarationList, location?: TextRange, flags?: NodeFlags) { - return createVariableStatement(undefined, undefined, declarationList, location, flags); - } - - export function createSimpleLetStatement(name: Identifier, initializer: Expression, location?: TextRange, exported?: boolean) { - let varDecl = createVariableDeclaration2(name, initializer); - let varDeclList = createVariableDeclarationList([varDecl], undefined, NodeFlags.Let); - let varStmt = createVariableStatement2(varDeclList, location, exported ? NodeFlags.Export : 0); - return varStmt; - } - - export function createExportDefaultStatement(expression: Expression): ExportAssignment { - return createExportAssignment(undefined, undefined, expression); - } - - function createClassHeritageClauses(baseTypeNode: ExpressionWithTypeArguments) { - return baseTypeNode ? [createHeritageClause(SyntaxKind.ExtendsKeyword, [baseTypeNode])] : undefined; + // isLeftHandSideExpression is almost the correct criterion for when it is not necessary + // to parenthesize the expression before a dot. The known exceptions are: + // + // NewExpression: + // new C.x -> not the same as (new C).x + // NumberLiteral + // 1.x -> not the same as (1).x + // + if (isLeftHandSideExpression(expr) && + expr.kind !== SyntaxKind.NewExpression && + expr.kind !== SyntaxKind.NumericLiteral) { + + return expr; } - export function createClassDeclaration2(name: Identifier, baseTypeNode: ExpressionWithTypeArguments, members: ClassElement[], location?: TextRange, flags?: NodeFlags): ClassDeclaration { - return createClassDeclaration(undefined, undefined, name, undefined, createClassHeritageClauses(baseTypeNode), members, location, flags); - } + return createParenthesizedExpression(expr); + } - export function createClassExpression2(name: Identifier, baseTypeNode: ExpressionWithTypeArguments, members: ClassElement[]): ClassExpression { - return createClassExpression(undefined, undefined, name, undefined, createClassHeritageClauses(baseTypeNode), members); - } + export function createCallExpression2(expression: Expression, _arguments?: Expression[], location?: TextRange, flags?: NodeFlags) { + return createCallExpression(parenthesizeForAccess(expression), undefined, _arguments, location, flags); + } - export function createClassExpression3(baseTypeNode: ExpressionWithTypeArguments, members: ClassElement[]): ClassExpression { - return createClassExpression2(undefined, baseTypeNode, members); - } - - export function createConstructor2(parameters: Array, body: Block, location?: TextRange, flags?: NodeFlags): ConstructorDeclaration { - return createConstructor(undefined, undefined, parameters, undefined, body, location, flags); - } + export function createObjectLiteralExpression2(properties?: ObjectLiteralElement[]) { + return createObjectLiteralExpression(undefined, undefined, createNodeArray(properties)); + } - export function createMethodDeclaration2(name: PropertyName, parameters: Array, body: Block, location?: TextRange, flags?: NodeFlags): MethodDeclaration { - return createMethodDeclaration(undefined, undefined, undefined, name, undefined, parameters, undefined, body, location, flags); - } + export function createAssignmentExpression(left: Expression, right: Expression) { + return createBinaryExpression2(left, SyntaxKind.EqualsToken, right); + } - export function createGetAccessor2(name: PropertyName, parameters: Array, body: Block, location?: TextRange, flags?: NodeFlags): GetAccessorDeclaration { - return createGetAccessor(undefined, undefined, name, parameters, undefined, body, location, flags); - } + export function createStrictEqualityExpression(left: Expression, right: Expression) { + return createBinaryExpression2(left, SyntaxKind.EqualsEqualsEqualsToken, right); + } - export function createSetAccessor2(name: PropertyName, parameters: Array, body: Block, location?: TextRange, flags?: NodeFlags): SetAccessorDeclaration { - return createSetAccessor(undefined, undefined, name, parameters, undefined, body, location, flags); - } - - export function createFunctionDeclaration2(name: Identifier, parameters: ParameterDeclaration[], body: Block, location?: TextRange, flags?: NodeFlags) { - return createFunctionDeclaration(undefined, undefined, undefined, name, undefined, parameters, undefined, body, location, flags); - } - - export function createFunctionDeclaration3(asteriskToken: Node, name: Identifier, parameters: ParameterDeclaration[], body: Block, location?: TextRange, flags?: NodeFlags) { - return createFunctionDeclaration(undefined, undefined, asteriskToken, name, undefined, parameters, undefined, body, location, flags); - } - - export function createFunctionExpression2(name: Identifier, parameters: ParameterDeclaration[], body: Block, location?: TextRange, flags?: NodeFlags) { - return createFunctionExpression(undefined, undefined, undefined, name, undefined, parameters, undefined, body, location, flags); - } - - export function createFunctionExpression3(asteriskToken: Node, name: Identifier, parameters: ParameterDeclaration[], body: Block, location?: TextRange, flags?: NodeFlags) { - return createFunctionExpression(undefined, undefined, asteriskToken, name, undefined, parameters, undefined, body, location, flags); - } - - export function createArrowFunction2(parameters: ParameterDeclaration[], body: Block | Expression, location?: TextRange, flags?: NodeFlags) { - return createArrowFunction(undefined, undefined, undefined, parameters, undefined, createNode(SyntaxKind.EqualsGreaterThanToken), body, location, flags); - } + export function createStrictInequalityExpression(left: Expression, right: Expression) { + return createBinaryExpression2(left, SyntaxKind.ExclamationEqualsEqualsToken, right); + } - export function createGeneratorFunctionExpression(parameters: ParameterDeclaration[], body: Block, location?: TextRange, flags?: NodeFlags) { - return createFunctionExpression(undefined, undefined, createNode(SyntaxKind.AsteriskToken), undefined, undefined, parameters, undefined, body, location, flags); - } - - export function createVoidZeroExpression(location?: TextRange, flags?: NodeFlags): VoidExpression { - return createVoidExpression(createNumericLiteral2(0), location, flags); - } - - export function createPropertyOrElementAccessExpression(expression: Expression, propName: Identifier | LiteralExpression, location?: TextRange, flags?: NodeFlags): LeftHandSideExpression { - return isIdentifier(propName) - ? createPropertyAccessExpression2(expression, makeSynthesized(propName), location, flags) - : createElementAccessExpression2(expression, makeSynthesized(propName), location, flags); - } - - export function createElementAccessExpression2(expression: Expression, argumentExpression: Expression, location?: TextRange, flags?: NodeFlags): ElementAccessExpression { - return createElementAccessExpression(parenthesizeForAccess(expression), argumentExpression, location, flags); - } + export function createLogicalAndExpression(left: Expression, right: Expression) { + return createBinaryExpression2(left, SyntaxKind.AmpersandAmpersandToken, right); + } - export function createElementAccessExpression3(expression: Expression, index: number, location?: TextRange, flags?: NodeFlags): ElementAccessExpression { - return createElementAccessExpression2(expression, createNumericLiteral2(index), location, flags); - } + export function createLogicalOrExpression(left: Expression, right: Expression) { + return createBinaryExpression2(left, SyntaxKind.BarBarToken, right); + } - export function createSliceCall(value: Expression, sliceIndex: number, location?: TextRange, flags?: NodeFlags): CallExpression { - return createCallExpression2(createPropertyAccessExpression3(value, "slice"), [createNumericLiteral2(sliceIndex)], location, flags); - } - - export function createApplyCall(target: Expression, thisArg: Expression, _arguments: Expression, location?: TextRange, flags?: NodeFlags) { - return createCallExpression2(createPropertyAccessExpression3(target, "apply"), [thisArg, _arguments], location, flags); - } + export function createCommaExpression(left: Expression, right: Expression) { + return createBinaryExpression2(left, SyntaxKind.CommaToken, right); + } - export function createExtendsHelperCall(name: Identifier) { - return createCallExpression2(createIdentifier("__extends"), [name, createIdentifier("_super")]); - } - - export function createAwaiterHelperCall(hasLexicalArguments: boolean, promiseConstructor: EntityName | Expression, body: Block) { - let argumentsExpr = hasLexicalArguments ? createIdentifier("arguments") : createVoidZeroExpression(); - let promiseExpr = promiseConstructor ? convertEntityNameToExpression(promiseConstructor) : createIdentifier("Promise"); - return createCallExpression2(createIdentifier("__awaiter"), [createThisKeyword(), argumentsExpr, promiseExpr, createGeneratorFunctionExpression([], body)]); - } - - function convertEntityNameToExpression(node: EntityName | Expression): Expression { - return isQualifiedName(node) ? createPropertyAccessExpression2(convertEntityNameToExpression(node.left), cloneNode(node.right)) : cloneNode(node); - } - - export function createDecorateHelperCall(decoratorExpressions: Expression[], target: Expression, memberName?: Expression, descriptor?: Expression) { - return createCallExpression2(createIdentifier("__decorate"), append([createArrayLiteralExpression(decoratorExpressions), target], memberName, descriptor)); - } - - export function createParamHelperCall(parameterIndex: number, decoratorExpression: Expression) { - return createCallExpression2(createIdentifier("__param"), [createNumericLiteral2(parameterIndex), decoratorExpression]); - } - - export function createMetadataHelperCall(metadataKey: string, metadataValue: Expression) { - return createCallExpression2(createIdentifier("__metadata"), [createStringLiteral(metadataKey), metadataValue]); - } + export function createBinaryExpression2(left: Expression, operator: SyntaxKind, right: Expression) { + return createBinaryExpression(parenthesizeForBinary(left, operator), createNode(operator), parenthesizeForBinary(right, operator)); + } - export function createDefinePropertyCall(target: Expression, memberName: Expression, descriptor: Expression) { - return createCallExpression2(createPropertyAccessExpression3(createIdentifier("Object"), "defineProperty"), [target, memberName, descriptor]); - } - - export function createGetOwnPropertyDescriptorCall(target: Expression, memberName: Expression) { - return createCallExpression2(createPropertyAccessExpression3(createIdentifier("Object"), "getOwnPropertyDescriptor"), [target, memberName]); - } - - export function createDefaultValueCheck(value: Expression, defaultValue: Expression, ensureIdentifier: (value: Expression) => Expression, location?: TextRange, flags?: NodeFlags): Expression { - // The value expression will be evaluated twice, so for anything but a simple identifier - // we need to generate a temporary variable - value = ensureIdentifier(value); + export function createConditionalExpression2(condition: Expression, whenTrue: Expression, whenFalse: Expression, location?: TextRange, flags?: NodeFlags) { + return createConditionalExpression(condition, createNode(SyntaxKind.QuestionToken), whenTrue, createNode(SyntaxKind.ColonToken), whenFalse, location, flags); + } + + export function createParameter2(name: BindingPattern | Identifier, initializer?: Expression, location?: TextRange, flags?: NodeFlags) { + return createParameter(undefined, undefined, undefined, name, undefined, undefined, initializer, location, flags); + } + + export function createRestParameter(name: Identifier, location?: TextRange, flags?: NodeFlags) { + return createParameter(undefined, undefined, createNode(SyntaxKind.DotDotDotToken), name, undefined, undefined, undefined, location, flags); + } + + export function createVariableDeclaration2(name: Identifier | BindingPattern, initializer?: Expression, location?: TextRange, flags?: NodeFlags) { + return createVariableDeclaration(name, undefined, initializer, location, flags); + } + + export function createVariableStatement2(declarationList: VariableDeclarationList, location?: TextRange, flags?: NodeFlags) { + return createVariableStatement(undefined, undefined, declarationList, location, flags); + } + + export function createSimpleLetStatement(name: Identifier, initializer: Expression, location?: TextRange, exported?: boolean) { + let varDecl = createVariableDeclaration2(name, initializer); + let varDeclList = createVariableDeclarationList([varDecl], undefined, NodeFlags.Let); + let varStmt = createVariableStatement2(varDeclList, location, exported ? NodeFlags.Export : 0); + return varStmt; + } + + export function createExportDefaultStatement(expression: Expression): ExportAssignment { + return createExportAssignment(undefined, undefined, expression); + } + + function createClassHeritageClauses(baseTypeNode: ExpressionWithTypeArguments) { + return baseTypeNode ? [createHeritageClause(SyntaxKind.ExtendsKeyword, [baseTypeNode])] : undefined; + } + + export function createClassDeclaration2(name: Identifier, baseTypeNode: ExpressionWithTypeArguments, members: ClassElement[], location?: TextRange, flags?: NodeFlags): ClassDeclaration { + return createClassDeclaration(undefined, undefined, name, undefined, createClassHeritageClauses(baseTypeNode), members, location, flags); + } + + export function createClassExpression2(name: Identifier, baseTypeNode: ExpressionWithTypeArguments, members: ClassElement[]): ClassExpression { + return createClassExpression(undefined, undefined, name, undefined, createClassHeritageClauses(baseTypeNode), members); + } + + export function createClassExpression3(baseTypeNode: ExpressionWithTypeArguments, members: ClassElement[]): ClassExpression { + return createClassExpression2(undefined, baseTypeNode, members); + } + + export function createConstructor2(parameters: Array, body: Block, location?: TextRange, flags?: NodeFlags): ConstructorDeclaration { + return createConstructor(undefined, undefined, parameters, undefined, body, location, flags); + } + + export function createMethodDeclaration2(name: PropertyName, parameters: Array, body: Block, location?: TextRange, flags?: NodeFlags): MethodDeclaration { + return createMethodDeclaration(undefined, undefined, undefined, name, undefined, parameters, undefined, body, location, flags); + } + + export function createGetAccessor2(name: PropertyName, parameters: Array, body: Block, location?: TextRange, flags?: NodeFlags): GetAccessorDeclaration { + return createGetAccessor(undefined, undefined, name, parameters, undefined, body, location, flags); + } + + export function createSetAccessor2(name: PropertyName, parameters: Array, body: Block, location?: TextRange, flags?: NodeFlags): SetAccessorDeclaration { + return createSetAccessor(undefined, undefined, name, parameters, undefined, body, location, flags); + } + + export function createFunctionDeclaration2(name: Identifier, parameters: ParameterDeclaration[], body: Block, location?: TextRange, flags?: NodeFlags) { + return createFunctionDeclaration(undefined, undefined, undefined, name, undefined, parameters, undefined, body, location, flags); + } + + export function createFunctionDeclaration3(asteriskToken: Node, name: Identifier, parameters: ParameterDeclaration[], body: Block, location?: TextRange, flags?: NodeFlags) { + return createFunctionDeclaration(undefined, undefined, asteriskToken, name, undefined, parameters, undefined, body, location, flags); + } + + export function createFunctionExpression2(name: Identifier, parameters: ParameterDeclaration[], body: Block, location?: TextRange, flags?: NodeFlags) { + return createFunctionExpression(undefined, undefined, undefined, name, undefined, parameters, undefined, body, location, flags); + } + + export function createFunctionExpression3(asteriskToken: Node, name: Identifier, parameters: ParameterDeclaration[], body: Block, location?: TextRange, flags?: NodeFlags) { + return createFunctionExpression(undefined, undefined, asteriskToken, name, undefined, parameters, undefined, body, location, flags); + } + + export function createArrowFunction2(parameters: ParameterDeclaration[], body: Block | Expression, location?: TextRange, flags?: NodeFlags) { + return createArrowFunction(undefined, undefined, undefined, parameters, undefined, createNode(SyntaxKind.EqualsGreaterThanToken), body, location, flags); + } + + export function createGeneratorFunctionExpression(parameters: ParameterDeclaration[], body: Block, location?: TextRange, flags?: NodeFlags) { + return createFunctionExpression(undefined, undefined, createNode(SyntaxKind.AsteriskToken), undefined, undefined, parameters, undefined, body, location, flags); + } + + export function createVoidZeroExpression(location?: TextRange, flags?: NodeFlags): VoidExpression { + return createVoidExpression(createNumericLiteral2(0), location, flags); + } + + export function createPropertyOrElementAccessExpression(expression: Expression, propName: Identifier | LiteralExpression, location?: TextRange, flags?: NodeFlags): LeftHandSideExpression { + return isIdentifier(propName) + ? createPropertyAccessExpression2(expression, makeSynthesized(propName), location, flags) + : createElementAccessExpression2(expression, makeSynthesized(propName), location, flags); + } + + export function createElementAccessExpression2(expression: Expression, argumentExpression: Expression, location?: TextRange, flags?: NodeFlags): ElementAccessExpression { + return createElementAccessExpression(parenthesizeForAccess(expression), argumentExpression, location, flags); + } + + export function createElementAccessExpression3(expression: Expression, index: number, location?: TextRange, flags?: NodeFlags): ElementAccessExpression { + return createElementAccessExpression2(expression, createNumericLiteral2(index), location, flags); + } + + export function createSliceCall(value: Expression, sliceIndex: number, location?: TextRange, flags?: NodeFlags): CallExpression { + return createCallExpression2(createPropertyAccessExpression3(value, "slice"), [createNumericLiteral2(sliceIndex)], location, flags); + } + + export function createApplyCall(target: Expression, thisArg: Expression, _arguments: Expression, location?: TextRange, flags?: NodeFlags) { + return createCallExpression2(createPropertyAccessExpression3(target, "apply"), [thisArg, _arguments], location, flags); + } + + export function createExtendsHelperCall(name: Identifier) { + return createCallExpression2(createIdentifier("__extends"), [name, createIdentifier("_super")]); + } + + export function createAwaiterHelperCall(hasLexicalArguments: boolean, promiseConstructor: EntityName | Expression, body: Block) { + let argumentsExpr = hasLexicalArguments ? createIdentifier("arguments") : createVoidZeroExpression(); + let promiseExpr = promiseConstructor ? convertEntityNameToExpression(promiseConstructor) : createIdentifier("Promise"); + return createCallExpression2(createIdentifier("__awaiter"), [createThisKeyword(), argumentsExpr, promiseExpr, createGeneratorFunctionExpression([], body)]); + } + + function convertEntityNameToExpression(node: EntityName | Expression): Expression { + return isQualifiedName(node) ? createPropertyAccessExpression2(convertEntityNameToExpression(node.left), cloneNode(node.right)) : cloneNode(node); + } + + export function createDecorateHelperCall(decoratorExpressions: Expression[], target: Expression, memberName?: Expression, descriptor?: Expression) { + return createCallExpression2(createIdentifier("__decorate"), append([createArrayLiteralExpression(decoratorExpressions), target], memberName, descriptor)); + } + + export function createParamHelperCall(parameterIndex: number, decoratorExpression: Expression) { + return createCallExpression2(createIdentifier("__param"), [createNumericLiteral2(parameterIndex), decoratorExpression]); + } + + export function createMetadataHelperCall(metadataKey: string, metadataValue: Expression) { + return createCallExpression2(createIdentifier("__metadata"), [createStringLiteral(metadataKey), metadataValue]); + } + + export function createDefinePropertyCall(target: Expression, memberName: Expression, descriptor: Expression) { + return createCallExpression2(createPropertyAccessExpression3(createIdentifier("Object"), "defineProperty"), [target, memberName, descriptor]); + } + + export function createGetOwnPropertyDescriptorCall(target: Expression, memberName: Expression) { + return createCallExpression2(createPropertyAccessExpression3(createIdentifier("Object"), "getOwnPropertyDescriptor"), [target, memberName]); + } + + export function createDefaultValueCheck(value: Expression, defaultValue: Expression, ensureIdentifier: (value: Expression) => Expression, location?: TextRange, flags?: NodeFlags): Expression { + // The value expression will be evaluated twice, so for anything but a simple identifier + // we need to generate a temporary variable + value = ensureIdentifier(value); - // === void 0 ? : - return createConditionalExpression2(createStrictEqualityExpression(value, createVoidZeroExpression()), defaultValue, value, location, flags); - } + // === void 0 ? : + return createConditionalExpression2(createStrictEqualityExpression(value, createVoidZeroExpression()), defaultValue, value, location, flags); + } - export function createMemberAccessForPropertyName(target: Expression, memberName: PropertyName, location?: TextRange, flags?: NodeFlags): MemberExpression { - return isIdentifier(memberName) - ? createPropertyAccessExpression2(target, cloneNode(memberName), location, flags) - : isComputedPropertyName(memberName) - ? createElementAccessExpression2(target, cloneNode(memberName.expression), location, flags) - : createElementAccessExpression2(target, cloneNode(memberName), location, flags); - } - - export function inlineExpressions(expressions: Expression[]) { - return reduceLeft(expressions, createCommaExpression); - } - // } + export function createMemberAccessForPropertyName(target: Expression, memberName: PropertyName, location?: TextRange, flags?: NodeFlags): MemberExpression { + return isIdentifier(memberName) + ? createPropertyAccessExpression2(target, cloneNode(memberName), location, flags) + : isComputedPropertyName(memberName) + ? createElementAccessExpression2(target, cloneNode(memberName.expression), location, flags) + : createElementAccessExpression2(target, cloneNode(memberName), location, flags); + } + export function inlineExpressions(expressions: Expression[]) { + return reduceLeft(expressions, createCommaExpression); + } + export function isDeclarationStatement(node: Node): node is DeclarationStatement { if (node) { switch (node.kind) { @@ -426,6 +424,6 @@ namespace ts { return true; } } - return false; + return false; } } \ No newline at end of file diff --git a/src/compiler/transform.ts b/src/compiler/transform.ts index e3cf7c2d26c..3097a324c60 100644 --- a/src/compiler/transform.ts +++ b/src/compiler/transform.ts @@ -1,6 +1,6 @@ /// /// -const FORCE_TRANSFORMS = false; +const FORCE_TRANSFORMS = true; /* @internal */ namespace ts { @@ -16,6 +16,7 @@ namespace ts { * @param subtreeFlags Transform flags computed for this node's subtree */ export function computeTransformFlagsForNode(node: Node, subtreeFlags: TransformFlags) { + Debug.assert((subtreeFlags & TransformFlags.NodeExcludes) == 0, "Subtree includes a `ThisNode...` flag."); let transformFlags: TransformFlags; // Mark transformations needed for each node @@ -260,12 +261,45 @@ namespace ts { return transform.runTransformationChain(statements, chain, _compilerOptions, _currentSourceFile, _resolver, _generatedNameSet, _nodeToGeneratedName); } - export const enum VisitorFlags { + export const enum PipelineFlags { LexicalEnvironment = 1 << 1, StatementOrBlock = 1 << 2, //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 namespace transform { // Flags enum to track count of temp variables and a few dedicated names const enum TempFlags { @@ -467,6 +501,10 @@ namespace ts { return nodeStack.createParentNavigator(); } + export function getRootNode(): SourceFile { + return currentSourceFile; + } + export function getCurrentNode(): Node { return nodeStack.getNode(); } @@ -666,7 +704,7 @@ namespace ts { return updatedNode; } - function readNodeArray(flags: VisitorFlags): NodeArray { + function readNodeArray(): NodeArray { if (updatedNodes) { return createNodeArray(updatedNodes, /*location*/ >originalNodes); } @@ -686,7 +724,7 @@ namespace ts { * This function also manages when new lexical environments are introduced, and tracks temporary * variables and hoisted variable and function declarations. */ - function pipeOneOrMany(inputNode: TIn, inputNodes: TIn[], pipeline: Visitor, output: (node: TOut) => void, flags: VisitorFlags): void { + function pipeOneOrMany(inputNode: TIn, inputNodes: TIn[], pipeline: Visitor, output: (node: TOut) => void, flags: PipelineFlags): void { if (!inputNode && !inputNodes) { return; } @@ -698,7 +736,7 @@ namespace ts { // If we are starting a new lexical environment, we need to reinitialize the lexical // environment state as well - if (flags & VisitorFlags.LexicalEnvironment) { + if (flags & PipelineFlags.LexicalEnvironment) { savedTempFlags = tempFlags; savedHoistedVariableDeclarations = hoistedVariableDeclarations; savedHoistedFunctionDeclarations = hoistedFunctionDeclarations; @@ -733,7 +771,7 @@ namespace ts { // If we established a new lexical environment, we need to write any hoisted variables or // function declarations to the end of the output. - if (flags & VisitorFlags.LexicalEnvironment) { + if (flags & PipelineFlags.LexicalEnvironment) { if (hoistedVariableDeclarations) { var stmt = createVariableStatement2(createVariableDeclarationList(hoistedVariableDeclarations)); output(stmt); @@ -751,7 +789,7 @@ namespace ts { } } - function emitOne(input: TIn, pipeline: Pipeline, flags: VisitorFlags, nodeTest: NodeTest): TOut { + function emitOne(input: TIn, pipeline: Pipeline, flags: PipelineFlags, nodeTest: NodeTest): TOut { if (!input) { return undefined; } @@ -766,7 +804,7 @@ namespace ts { updatedNode = undefined; nodeTestCallback = nodeTest; writeNodeWithOrWithoutNodeTest = nodeTest ? writeNodeWithNodeTest : writeNodeWithoutNodeTest; - writeNodeFastOrSlow = flags & VisitorFlags.StatementOrBlock ? writeNodeFastOrSlow = writeStatementOrBlockSlow : writeNodeFastOrSlow = writeNodeSlow; + writeNodeFastOrSlow = flags & PipelineFlags.StatementOrBlock ? writeStatementOrBlockSlow : writeNodeSlow; // Pipe the input node into the output pipeOneOrMany(input, undefined, pipeline, writeNode, flags); @@ -783,7 +821,7 @@ namespace ts { return result; } - function emitOneOrMany(inputNode: TIn, inputNodes: TIn[], pipeline: Pipeline, output: TOut[], flags: VisitorFlags, nodeTest: (node: Node) => node is TOut): NodeArray { + function emitOneOrMany(inputNode: TIn, inputNodes: TIn[], pipeline: Pipeline, output: TOut[], flags: PipelineFlags, nodeTest: (node: Node) => node is TOut): NodeArray { // Exit early if we have nothing to do if (!inputNode && !inputNodes) { return undefined; @@ -809,7 +847,7 @@ namespace ts { pipeOneOrMany(inputNode, inputNodes, pipeline, writeNode, flags); // Read the output array - output = >readNodeArray(flags); + output = >readNodeArray(); // Restore previous environment offsetWritten = savedOffsetWritten; @@ -863,7 +901,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?: VisitorFlags): void { + export function pipeNode(input: TIn, pipeline: Pipeline, output: PipelineOutput, flags?: PipelineFlags): void { pipeOneOrMany(input, undefined, pipeline, output, flags); } @@ -874,7 +912,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?: VisitorFlags): void { + export function pipeNodes(input: TIn[], pipeline: Pipeline, output: PipelineOutput, flags?: PipelineFlags): void { pipeOneOrMany(undefined, input, pipeline, output, flags); } @@ -885,10 +923,14 @@ 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?: VisitorFlags, nodeTest?: NodeTest): void { + export 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. @@ -896,32 +938,32 @@ 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?: VisitorFlags, nodeTest?: NodeTest): void { + export 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?: VisitorFlags): T; - export function visitNode(node: TIn, visitor: Pipeline, flags?: VisitorFlags, nodeTest?: NodeTest): TOut; - export function visitNode(node: TIn, visitor: Pipeline, flags?: VisitorFlags, nodeTest?: NodeTest): TOut { + 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?: VisitorFlags) { - return emitOne(node, visitor, flags | VisitorFlags.StatementOrBlock, undefined); + 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?: VisitorFlags): NodeArray; - export function visitNodes(nodes: TIn[], pipeline: Pipeline, flags?: VisitorFlags, nodeTest?: NodeTest): NodeArray; - export function visitNodes(nodes: TIn[], pipeline: Pipeline, flags?: VisitorFlags, nodeTest?: NodeTest): NodeArray { + 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, VisitorFlags.LexicalEnvironment)); + return updateBlock(node, visitNodes(node.statements, pipeline, PipelineFlags.LexicalEnvironment)); } else if (isModuleBlock(node)) { - return updateModuleBlock(node, visitNodes(node.statements, pipeline, VisitorFlags.LexicalEnvironment)); + return updateModuleBlock(node, visitNodes(node.statements, pipeline, PipelineFlags.LexicalEnvironment)); } else if (isExpressionNode(node)) { return visitExpressionFunctionBodyInNewLexicalEnvironment(node, pipeline); diff --git a/src/compiler/transforms/es5.ts b/src/compiler/transforms/es5.ts index c1d3640a0e1..37a080c5ba1 100644 --- a/src/compiler/transforms/es5.ts +++ b/src/compiler/transforms/es5.ts @@ -2,7 +2,7 @@ /*@internal*/ namespace ts.transform { export function toES5(statements: NodeArray) { - return visitNodes(statements, transformNode, VisitorFlags.LexicalEnvironment); + return visitNodes(statements, transformNode, PipelineFlags.LexicalEnvironment); } /** @@ -41,7 +41,7 @@ namespace ts.transform { accept(node, transformNode, write); } else { - return write(node); + write(node); } } @@ -153,7 +153,7 @@ namespace ts.transform { let body = createBlock([]); if (constructor) { - emitNode(constructor, transformConstructor, body.statements, VisitorFlags.LexicalEnvironment); + emitNode(constructor, transformConstructor, body.statements, PipelineFlags.LexicalEnvironment); } else if (baseTypeNode) { let superCall = createDefaultSuperCall(); @@ -402,7 +402,7 @@ namespace ts.transform { function transformMethodDeclaration(node: ClassLikeDeclaration, member: MethodDeclaration, write: (node: Statement) => void): void { let prefix = getClassMemberPrefix(node, member); let propExpr = getMemberAccessForPropertyName(node, member); - let funcExpr = rewriteFunctionExpression(member, /*name*/ undefined, /*location*/ undefined); + let funcExpr = transformFunctionLikeExpressionToFunctionExpression(member, /*name*/ undefined, /*location*/ undefined); let assignExpr = createAssignmentExpression(propExpr, funcExpr); let assignStmt = createExpressionStatement(assignExpr, /*location*/ member); startOnNewLine(assignStmt); @@ -415,7 +415,7 @@ namespace ts.transform { let name = getExpressionForPropertyName(firstAccessor); let descriptorExpr = createObjectLiteralExpression2(); if (accessors.getAccessor) { - let funcExpr = rewriteFunctionExpression(accessors.getAccessor, /*name*/ undefined, /*location*/ accessors.getAccessor); + let funcExpr = transformFunctionLikeExpressionToFunctionExpression(accessors.getAccessor, /*name*/ undefined, /*location*/ accessors.getAccessor); let getName = createIdentifier("get"); let getProp = createPropertyAssignment(getName, funcExpr); startOnNewLine(getProp); @@ -423,7 +423,7 @@ namespace ts.transform { } if (accessors.setAccessor) { - let funcExpr = rewriteFunctionExpression(accessors.setAccessor, /*name*/ undefined, /*location*/ accessors.setAccessor); + let funcExpr = transformFunctionLikeExpressionToFunctionExpression(accessors.setAccessor, /*name*/ undefined, /*location*/ accessors.setAccessor); let setName = createIdentifier("set"); let setProp = createPropertyAssignment(setName, funcExpr); startOnNewLine(setProp); @@ -447,32 +447,19 @@ namespace ts.transform { } function transformFunctionExpression(node: FunctionExpression, write: (node: Expression) => void): void { - write(rewriteFunctionExpression(node, node.name, /*location*/ node)); + write(transformFunctionLikeExpressionToFunctionExpression(node, node.name, /*location*/ node)); } - function rewriteFunctionExpression(node: FunctionLikeDeclaration, name: Identifier, location: TextRange): FunctionExpression { + function transformFunctionLikeExpressionToFunctionExpression(node: FunctionLikeDeclaration, name: Identifier, location: TextRange): FunctionExpression { let parameters = visitNodes(node.parameters, transformNode); - let body = createBlock([], node.body); - emitNode(node, transformFunctionBody, body.statements, VisitorFlags.LexicalEnvironment); - return createFunctionExpression2(name, parameters, body, location); + 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 body = createBlock([], node.body); - emitNode(node, transformFunctionBody, body.statements, VisitorFlags.LexicalEnvironment); - write(createFunctionDeclaration2(node.name, parameters, body, /*location*/ node)); - } - - function transformToFunctionLikeDeclaration(node: FunctionLikeDeclaration, name: Identifier, location: TextRange, - createfn: (name: DeclarationName, parameters: ParameterDeclaration[], body: Block, location: TextRange) => T): T { - - let parameters = visitNodes(node.parameters, transformNode); - let newBody = createBlock([], /*location*/ isBlock(node.body) ? node.body : undefined); - emitNode(node, transformFunctionBody, newBody.statements, VisitorFlags.LexicalEnvironment); - - let func = createfn(name, parameters, newBody, location); - return func; + let statements = flatMapNode(node, transformFunctionBody, PipelineFlags.LexicalEnvironment); + write(createFunctionDeclaration2(node.name, parameters, createBlock(statements), /*location*/ node)); } function transformFunctionBody(node: FunctionLikeDeclaration, write: (node: Statement) => void) { diff --git a/src/compiler/transforms/es6.ts b/src/compiler/transforms/es6.ts index 4dd2ea56ab1..a78f5037e62 100644 --- a/src/compiler/transforms/es6.ts +++ b/src/compiler/transforms/es6.ts @@ -4,6 +4,9 @@ namespace ts.transform { let resolver: EmitResolver; let compilerOptions: CompilerOptions; let languageVersion: ScriptTarget; + let currentLexicalEnvironment: SourceFile | FunctionLikeDeclaration | ModuleDeclaration + let currentModuleDeclaration: ModuleDeclaration; + let currentClassLikeDeclaration: ClassLikeDeclaration; let currentBaseTypeNode: ExpressionWithTypeArguments; let currentConstructor: ConstructorDeclaration; let currentParametersWithPropertyAssignments: ParameterDeclaration[]; @@ -13,7 +16,8 @@ namespace ts.transform { resolver = getEmitResolver(); compilerOptions = getCompilerOptions(); languageVersion = compilerOptions.target || ScriptTarget.ES3; - return visitNodes(statements, transformNode, VisitorFlags.LexicalEnvironment); + currentLexicalEnvironment = getRootNode(); + return visitNodes(statements, transformNode, PipelineFlags.LexicalEnvironment); } /** @@ -33,11 +37,7 @@ namespace ts.transform { * If no part of this node or its subtree requires transformation, the node * is returned, unchanged. */ - function transformNode(node: T, write: (node: T) => void): void { - if (!node) { - return; - } - + function transformNode(node: Node, write: (node: Node) => void): void { // Debug.assert( // !needsTransform(node, TransformFlags.ThisNodeNeedsTransformToES7), // "Cannot transform node with post-ES7 syntax."); @@ -54,10 +54,6 @@ namespace ts.transform { } function transformModuleElement(node: Node, write: (node: Node) => void): void { - if (!node) { - return; - } - if (node.flags & NodeFlags.Export) { transformNodeWorker(node, write); } @@ -165,11 +161,11 @@ namespace ts.transform { // // TypeScript heritage clause extensions include: // - `implements` clause - return transformHeritageClause(node, write); + return visitHeritageClause(node, write); case SyntaxKind.ExpressionWithTypeArguments: // TypeScript supports type arguments on an expression in an `extends` heritage clause. - return transformExpressionWithTypeArguments(node, write); + return visitExpressionWithTypeArguments(node, write); case SyntaxKind.MethodDeclaration: // TypeScript method declarations may be 'async', and may have decorators, modifiers @@ -247,31 +243,30 @@ namespace ts.transform { * @param node The node to transform. */ function transformClassDeclaration(node: ClassDeclaration, write: (node: Statement) => void) { - let baseTypeNode = visitAndGetClassExtendsHeritageClauseElement(node); - let classMembers: ClassElement[] = []; - let constructor = transformConstructor(node, baseTypeNode); - if (constructor) { - classMembers.push(constructor); - } + 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(baseTypeNode, classMembers); + let classExpr = createClassExpression3(currentBaseTypeNode, classMembers); let varStmt = createSimpleLetStatement(getDeclarationName(node), classExpr, /*location*/ node, isTopLevelNonDefaultExport(node)); - varStmt.original = node; - write(varStmt); + write(setOriginalNode(varStmt, node)); } else { let exportFlags = isNamespaceLevelExport(node) ? undefined : node.flags & (NodeFlags.Export | NodeFlags.Default); - let classDecl = createClassDeclaration2(getDeclarationName(node), baseTypeNode, classMembers, /*location*/ node, exportFlags); - classDecl.original = node; - write(classDecl); + let classDecl = createClassDeclaration2(getDeclarationName(node), currentBaseTypeNode, classMembers, /*location*/ node, exportFlags); + write(setOriginalNode(classDecl, node)); } - transformPropertyDeclarationsToStatements(node, getInitializedProperties(node, /*isStatic*/ true), write); + pipeNodes(getInitializedProperties(node, /*isStatic*/ true), transformPropertyDeclarationToStatement, write); + transformDecoratorsOfMembers(node, /*isStatic*/ false, write); transformDecoratorsOfMembers(node, /*isStatic*/ true, write); transformDecoratorsOfConstructor(node, write); @@ -282,79 +277,47 @@ namespace ts.transform { else if (isTopLevelDefaultExport(node) && nodeIsDecorated(node)) { write(createExportDefaultStatement(getDeclarationName(node))); } + + currentClassLikeDeclaration = savedCurrentClassLikeDeclaration; + currentBaseTypeNode = savedCurrentBaseTypeNode; } function transformClassExpression(node: ClassExpression, write: (node: LeftHandSideExpression) => void) { - let baseTypeNode = visitAndGetClassExtendsHeritageClauseElement(node); - let classMembers: ClassElement[] = []; - let constructor = transformConstructor(node, baseTypeNode); - if (constructor) { - classMembers.push(constructor); - } + 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), baseTypeNode, classMembers); + let classExpr = createClassExpression2(getDeclarationName(node), currentBaseTypeNode, classMembers); let staticPropertyAssignments = getInitializedProperties(node, /*isStatic*/ true); if (staticPropertyAssignments) { let expressions: Expression[] = []; let tempVar = declareLocal(); - let cacheExpr = createAssignmentExpression(tempVar, classExpr); - expressions.push(cacheExpr); - transformPropertyDeclarationsToExpressions(node, staticPropertyAssignments, expressions); + expressions.push(createAssignmentExpression(tempVar, classExpr)); + emitNodes(staticPropertyAssignments, transformPropertyDeclarationToExpression, expressions); expressions.push(tempVar); write(createParenthesizedExpression(inlineExpressions(expressions))); } else { write(classExpr); } + + currentClassLikeDeclaration = savedCurrentClassLikeDeclaration; + currentBaseTypeNode = savedCurrentBaseTypeNode; } - function visitAndGetClassExtendsHeritageClauseElement(node: ClassLikeDeclaration) { - let heritageClauses = visitNodes(node.heritageClauses, transformNode); + function getAndVisitClassExtendsHeritageClauseElement(node: ClassLikeDeclaration) { + let heritageClauses = visitNodes(node.heritageClauses, visitHeritageClause); let extendsClause = heritageClauses && firstOrUndefined(heritageClauses); let baseTypeNode = extendsClause && firstOrUndefined(extendsClause.types); return baseTypeNode; } - 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); - return propExpr; - } - return name; - } - - function transformConstructor(node: ClassLikeDeclaration, baseTypeNode: ExpressionWithTypeArguments) { + 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 @@ -365,117 +328,92 @@ namespace ts.transform { // For target ES6 and above, if there is no property assignment // do not emit constructor in class declaration. if (!parameterPropertyAssignments && !instancePropertyAssignments) { - return constructor; + write(constructor); + return; } let parameters: ParameterDeclaration[] = []; if (constructor) { emitNodes(constructor.parameters, transformNode, parameters); } - else if (baseTypeNode) { + else if (currentBaseTypeNode) { parameters.push(createRestParameter(createIdentifier("args"), /*location*/ undefined, NodeFlags.GeneratedRest)); } - let statements: Statement[] = []; - let savedCurrentBaseTypeNode = currentBaseTypeNode; let savedCurrentConstructor = currentConstructor; let savedCurrentParametersWithPropertyAssignments = currentParametersWithPropertyAssignments; let savedCurrentInstancePropertyAssignments = currentInstancePropertyAssignments; - emitNode(node, emitConstructorBody, statements, VisitorFlags.LexicalEnvironment, isStatementNode); + let statements = flatMapNode(node, emitConstructorBody, PipelineFlags.LexicalEnvironment) + write(createConstructor2(parameters, createBlock(statements), /*location*/ constructor)); - currentBaseTypeNode = savedCurrentBaseTypeNode; currentConstructor = savedCurrentConstructor; currentParametersWithPropertyAssignments = savedCurrentParametersWithPropertyAssignments; currentInstancePropertyAssignments = savedCurrentInstancePropertyAssignments; - - return createConstructor2(parameters, createBlock(statements), /*location*/ constructor); } function emitConstructorBody(node: ClassLikeDeclaration, write: (node: Statement) => void) { - let baseTypeNode = currentBaseTypeNode; - let constructor = currentConstructor; - let parameterPropertyAssignments = currentParametersWithPropertyAssignments; - let instancePropertyAssignments = currentInstancePropertyAssignments; let superCall: ExpressionStatement; - if (constructor) { - if (baseTypeNode) { - superCall = findInitialSuperCall(constructor); + if (currentConstructor) { + if (currentBaseTypeNode) { + superCall = findInitialSuperCall(currentConstructor); if (superCall) { write(superCall); } } - if (parameterPropertyAssignments) { - for (let parameter of parameterPropertyAssignments) { - let name = cloneNode(parameter.name); - let thisExpr = createThisKeyword(); - let propExpr = createPropertyAccessExpression2(thisExpr, name); - let assignExpr = createAssignmentExpression(propExpr, name); - let assignStmt = createExpressionStatement(assignExpr); - startOnNewLine(assignStmt); - write(assignStmt); - } - } + pipeNodes(currentParametersWithPropertyAssignments, emitParameterPropertyAssignment, write); } - else if (baseTypeNode) { - let superExpr = createSuperKeyword(); - let argsName = createIdentifier("args"); - let spreadExpr = createSpreadElementExpression(argsName); - let callExpr = createCallExpression2(superExpr, [spreadExpr]); - let callStmt = createExpressionStatement(callExpr, /*location*/ undefined, NodeFlags.GeneratedSuper); - startOnNewLine(callStmt); - write(callStmt); + else if (currentBaseTypeNode) { + let callExpr = createCallExpression2(createSuperKeyword(), [createSpreadElementExpression(createIdentifier("args"))]); + write(startOnNewLine(createExpressionStatement(callExpr, /*location*/ undefined, NodeFlags.GeneratedSuper))); } - transformPropertyDeclarationsToStatements(node, instancePropertyAssignments, write); + pipeNodes(currentInstancePropertyAssignments, transformPropertyDeclarationToStatement, write); - if (constructor) { - let bodyStatements = constructor.body.statements; - pipeNodes(superCall ? bodyStatements.slice(1) : bodyStatements, transformNode, write); + if (currentConstructor) { + pipeNodes(skip(currentConstructor.body.statements, superCall ? 1 : 0), transformNode, write); } } - function transformHeritageClause(node: HeritageClause, write: (node: HeritageClause) => void) { + 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, visitNodes(node.types, transformNode))); + write(updateHeritageClause(node, take(visitNodes(node.types, visitExpressionWithTypeArguments), 1))); } } - function transformExpressionWithTypeArguments(node: ExpressionWithTypeArguments, write: (node: ExpressionWithTypeArguments) => void) { + function visitExpressionWithTypeArguments(node: ExpressionWithTypeArguments, write: (node: ExpressionWithTypeArguments) => void) { write(updateExpressionWithTypeArguments(node, visitNode(node.expression, transformNode), /*typeArguments*/ undefined)); } - function transformPropertyDeclarationsToStatements(node: ClassLikeDeclaration, properties: PropertyDeclaration[], write: (node: Statement) => void) { - if (!properties) { - return; - } - - for (let property of properties) { - write(createExpressionStatement(transformPropertyDeclaration(node, property), /*location*/ property)); - } + function transformPropertyDeclarationToStatement(node: PropertyDeclaration, write: (node: Statement) => void): void { + transformPropertyDeclarationToExpressionOrStatement(node, undefined, write); } - - function transformPropertyDeclarationsToExpressions(node: ClassLikeDeclaration, properties: PropertyDeclaration[], expressions: Expression[]) { - if (!properties) { - return; - } - for (let property of properties) { - let propertyAssignment = transformPropertyDeclaration(node, property, /*location*/ property); - expressions.push(propertyAssignment); - } + function transformPropertyDeclarationToExpression(node: PropertyDeclaration, write: (node: Expression) => void): void { + transformPropertyDeclarationToExpressionOrStatement(node, write, undefined); } - function transformPropertyDeclaration(node: ClassLikeDeclaration, property: PropertyDeclaration, location?: TextRange): Expression { - let isStatic = (property.flags & NodeFlags.Static) !== 0; - let target = isStatic ? getDeclarationName(node) : createThisKeyword(); - let name = transformPropertyName(property); - let left = createMemberAccessForPropertyName(target, name, /*location*/ property.name); - let initializer = visitNode(property.initializer, transformNode); + 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, location); - return assignExpr; + setTextRange(assignExpr, node); + if (writeExpression) { + writeExpression(assignExpr); + } + else { + writeStatement(createExpressionStatement(assignExpr)); + } } // emitter.ts:4074 @@ -707,10 +645,7 @@ namespace ts.transform { } function transformVariableStatement(node: VariableStatement, write: (node: Statement) => void) { - // TODO(rbuckton): transform namespace exports for a variable declaration list - // Debug.assert(isNamespaceLevelExport(node), "Should only reach here for exported variables." + node.declarationList.declarations[0].name); - // pipeNode(node.declarationList, write, transformVariableDeclarationListToExpressionStatement); - return accept(node, transformNode, write); + pipeNode(node.declarationList, transformVariableDeclarationListToExpressionStatement, write); } function transformVariableDeclarationListToExpressionStatement(node: VariableDeclarationList, write: (node: Statement) => void) { @@ -752,22 +687,56 @@ namespace ts.transform { } function transformObjectBindingPatternToExpression(node: ObjectBindingPattern, write: (node: Expression) => void) { - Debug.fail("not implemented"); - let properties: ObjectLiteralElement[] = []; + let properties = visitNodes(node.elements, transformBindingElementToObjectLiteralElement); write(createObjectLiteralExpression2(properties)); } + + function transformBindingElementToObjectLiteralElement(node: BindingElement, write: (node: ObjectLiteralElement) => void) { + let propertyName = node.propertyName || node.name; + 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); + } + + write(createPropertyAssignment(propertyName, expr)); + } + function transformArrayBindingPatternToExpression(node: ArrayBindingPattern, write: (node: Expression) => void) { - Debug.fail("not implemented"); - let elements: Expression[] = []; + let elements = visitNodes(node.elements, transformBindingElementToExpression); write(createArrayLiteralExpression(elements)); } + + function transformBindingElementToExpression(node: BindingElement, write: (node: Expression) => void) { + 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 (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; @@ -784,7 +753,7 @@ namespace ts.transform { let body = node.body; let moduleBody: Block; if (isModuleBlock(body)) { - moduleBody = createBlock(visitNodes(body.statements, transformModuleElement)); + moduleBody = createBlock(visitNodes(body.statements, transformModuleElement, PipelineFlags.LexicalEnvironment)); } else { let inner = visitStatement(body, transformNode); @@ -804,8 +773,9 @@ namespace ts.transform { let callExpr = createCallExpression2(parenExpr, [moduleParam]); let callStmt = createExpressionStatement(callExpr, location, NodeFlags.GeneratedNamespace); - callStmt.original = node; - write(callStmt); + write(setOriginalNode(callStmt, node)); + + currentModuleDeclaration = savedCurrentModuleDeclaration; } function shouldEmitModuleDeclaration(node: ModuleDeclaration) { @@ -1382,4 +1352,41 @@ namespace ts.transform { 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); + return propExpr; + } + return name; + } } \ No newline at end of file diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f46518cca03..9e9f367f8a4 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -892,6 +892,10 @@ namespace ts { type?: TypeNode; initializer?: Expression; } + + export interface PropertyLikeDeclaration extends Declaration { + name: PropertyName; + } export interface BindingPattern extends Node { elements: NodeArray;