From f15d20b9f22fa3ad1e0ee746bedd8a11d4723004 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Sat, 12 Sep 2015 15:32:05 -0700 Subject: [PATCH] Cleanup and organization --- scripts/processTypes.ts | 40 ++- src/compiler/checker.ts | 6 +- src/compiler/core.ts | 74 +++--- src/compiler/emitter.ts | 20 +- src/compiler/factory.generated.ts | 41 ++- src/compiler/factory.ts | 409 ++++++------------------------ src/compiler/scanner.ts | 4 + src/compiler/transform.ts | 205 ++++++++------- src/compiler/transforms/es5.ts | 16 +- src/compiler/transforms/es6.ts | 144 ++++------- src/compiler/tsc.ts | 14 +- src/compiler/types.ts | 10 +- src/compiler/utilities.ts | 8 +- src/services/breakpoints.ts | 2 +- src/services/formatting/rules.ts | 2 +- 15 files changed, 413 insertions(+), 582 deletions(-) diff --git a/scripts/processTypes.ts b/scripts/processTypes.ts index f8928d58ae0..9436d372579 100644 --- a/scripts/processTypes.ts +++ b/scripts/processTypes.ts @@ -532,7 +532,7 @@ function generateFactory(outputFile: string) { return; } - // Skip the is function for this node if it is already defined + // Skip the is function for this node if it is already defined if (resolveQualifiedName(factorySourceFile, `ts.is${syntaxNode.kindName}`, SymbolFlags.Function)) { return; } @@ -552,8 +552,24 @@ function generateFactory(outputFile: string) { return; } + let typeNames = splitTypeName(typeName); + let nodeTestFunction: string; + if (typeNames.length === 1) { + let typeSymbol = resolveExportedQualifiedName(tsModuleSymbol, typeName, SymbolFlags.Type); + if (typeSymbol) { + let nodeTestAnnotation = findFirstAnnotation(typeSymbol, NodeTestAnnotation.match); + if (nodeTestAnnotation) { + nodeTestFunction = nodeTestAnnotation.functionName; + } + } + } + + if (!nodeTestFunction) { + nodeTestFunction = `is${typeNameToMethodNameSuffix(typeName)}`; + } + // Skip the is function for this type if it is already defined in factory.ts - if (resolveQualifiedName(factorySourceFile, `ts.is${typeNameToMethodNameSuffix(typeName)}`, SymbolFlags.Function)) { + if (resolveQualifiedName(factorySourceFile, `ts.${nodeTestFunction}`, SymbolFlags.Function)) { return; } @@ -562,7 +578,7 @@ function generateFactory(outputFile: string) { return; } - writer.write(`export function is${typeNameToMethodNameSuffix(typeName)}(node: Node): node is ${typeName} {`); + writer.write(`export function ${nodeTestFunction}(node: Node): node is ${typeName} {`); writer.writeLine(); writer.increaseIndent(); @@ -1307,6 +1323,8 @@ function createLineWrappingTextWriter(newLine: string, maxWidth: number): EmitTe // Annotations // +function findFirstAnnotation(symbol: Symbol, match: (annotation: Annotation) => annotation is TAnnotation): TAnnotation; +function findFirstAnnotation(symbol: Symbol, match: (annotation: Annotation) => boolean): TAnnotation; function findFirstAnnotation(symbol: Symbol, match: (annotation: Annotation) => boolean): TAnnotation { for (let annotation of getAnnotations(symbol)) { if (match(annotation)) { @@ -1317,6 +1335,8 @@ function findFirstAnnotation(symbol: Symbol, mat return undefined; } +function findAllAnnotations(symbol: Symbol, match: (annotation: Annotation) => annotation is TAnnotation): TAnnotation[]; +function findAllAnnotations(symbol: Symbol, match: (annotation: Annotation) => boolean): TAnnotation[]; function findAllAnnotations(symbol: Symbol, match: (annotation: Annotation) => boolean): TAnnotation[] { let annotations: TAnnotation[]; for (let annotation of getAnnotations(symbol)) { @@ -1570,6 +1590,20 @@ class NewLexicalEnvironmentAnnotation extends Annotation { } } +@annotation("nodetest") +class NodeTestAnnotation extends Annotation { + public name = "nodetest"; + public functionName: string; + constructor([functionName, ..._arguments]: [string, any]) { + super(_arguments); + this.functionName = functionName + } + + public static match(annotation: Annotation): annotation is NodeTestAnnotation { + return annotation instanceof NodeTestAnnotation; + } +} + function createAnnotation(name: string, _arguments: any[]): Annotation { let ctor = getProperty(annotationConstructors, name); if (ctor) { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b2614a49b83..693187a897f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1400,7 +1400,7 @@ namespace ts { } function isEntityNameOrExpression(node: Node): node is EntityName | Expression { - return isEntityName(node) || isExpression(node); + return isEntityName(node) || isExpressionNode(node); } function isEntityNameVisible(entityName: EntityName | Expression, enclosingDeclaration: Node): SymbolVisibilityResult { @@ -13951,7 +13951,7 @@ namespace ts { (entityName.parent.kind === SyntaxKind.JsxClosingElement)) { return getJsxElementTagSymbol(entityName.parent); } - else if (isPartOfExpression(entityName)) { + else if (isExpression(entityName)) { if (nodeIsMissing(entityName)) { // Missing entity name. return undefined; @@ -14089,7 +14089,7 @@ namespace ts { return getTypeFromTypeNode(node); } - if (isPartOfExpression(node)) { + if (isExpression(node)) { return getTypeOfExpression(node); } diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 73d050bf2c0..af3106d12e1 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -189,6 +189,34 @@ namespace ts { } } } + + export function trimArray(array: T[]): T[] { + let result: T[]; + if (array) { + result = []; + for (let v of array) { + if (v !== undefined) { + result.push(v); + } + } + } + return result; + } + + export function append(to: T[], ...values: T[]): T[] { + let result: T[]; + if (to) { + for (let v of values) { + if (v !== undefined) { + if (!result) { + result = to.slice(0); + } + result.push(v); + } + } + } + return result || to; + } export function rangeEquals(array1: T[], array2: T[], pos: number, end: number) { while (pos < end) { @@ -835,16 +863,6 @@ namespace ts { * @param currentNode The current node for the navigator. */ export function createParentNavigator(currentNode: Node): ParentNavigator { - /** Traverses the parent pointers for the current node to find the root node. */ - function getRoot() { - let rootNode = currentNode; - while (rootNode && rootNode.parent) { - rootNode = rootNode.parent; - } - - return rootNode; - } - /** Gets the grandparent of the current node, without moving the navigator. */ function getGrandparent() { let parent = getParent(); @@ -877,30 +895,17 @@ namespace ts { return false; } - /** Navigates to the root node for the current node. */ - function moveToRoot(): boolean { - let rootNode = getRoot(); - if (rootNode) { - currentNode = rootNode; - return true; - } - - return false; - } - /** Creates a new ParentNavigator from the current node. */ function createParentNavigator() { return ts.createParentNavigator(currentNode); } return { - getRoot, getGrandparent, getParent, getNode, getKind, moveToParent, - moveToRoot, createParentNavigator, }; } @@ -915,11 +920,6 @@ namespace ts { let parentNode: Node; let currentNode: Node; - /** Gets the node at the bottom of the stack. */ - function getRoot() { - return rootNode; - } - /** Gets the node two steps back from the top of the stack. */ function getGrandparent() { return peekNode(2); @@ -953,6 +953,16 @@ namespace ts { currentNode = node; } + /** Pushes a node onto the stack if it is not already at the top of the stack. */ + function tryPushNode(node: Node): boolean { + if (currentNode !== node) { + return false; + } + + pushNode(node); + return true; + } + /** Pops the top node from the stack. */ function popNode(): void { currentNode = parentNode; @@ -985,7 +995,7 @@ namespace ts { if (parentNode && match(parentNode)) { return parentNode; } - for (let i = stack.length; i >= 0; i--) { + for (let i = stack.length - 1; i >= 0; i--) { let node = stack[i]; if (match(node)) { return node; @@ -1048,26 +1058,24 @@ namespace ts { } return { - getRoot, getGrandparent, getParent, getNode, getKind, - moveToRoot, moveToParent, createParentNavigator, }; } return { - getRoot, getGrandparent, getParent, getNode, getKind, pushNode, - popNode, + tryPushNode, setNode, + popNode, findAncestorNode, createParentNavigator, }; diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 7c1000b7a84..682bc603bdd 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -504,11 +504,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function writeTextWithSpanRecord(tokenKind: SyntaxKind, startPos: number, emitFn?: () => void) { - let tokenStartPos = ts.skipTrivia(currentSourceFile.text, startPos); - recordSourceMapSpan(tokenStartPos); - let tokenEndPos = emitTokenText(tokenKind, tokenStartPos, emitFn); - recordSourceMapSpan(tokenEndPos); - return tokenEndPos; + if (!positionIsSynthesized(startPos)) { + let tokenStartPos = skipTrivia(currentSourceFile.text, startPos); + recordSourceMapSpan(tokenStartPos); + let tokenEndPos = emitTokenText(tokenKind, tokenStartPos, emitFn); + recordSourceMapSpan(tokenEndPos); + return tokenEndPos; + } + else { + emitTokenText(tokenKind, startPos, emitFn); + } } function recordNewSourceFileStart(node: SourceFile) { @@ -1112,7 +1117,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi let nav = nodeStack.createParentNavigator(); nav.moveToParent(); - let emitOuterParens = isPartOfExpression(nav) && templateNeedsParens(node); + let emitOuterParens = isExpression(nav) && templateNeedsParens(node); if (emitOuterParens) { write("("); } @@ -4389,7 +4394,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi pushNode(body); let preambleEmitted = writer.getTextPos() !== initialTextPos; - if (!preambleEmitted && nodeEndIsOnSameLineAsNodeStart(body, body) + if (!preambleEmitted && !nodeIsSynthesized(body) + && nodeEndIsOnSameLineAsNodeStart(body, body) && !isBlockWithSynthesizedStatementsOnNewLine(body) && (!nodeIsSynthesized(body) || isSingleLineSynthesizedBlock(body))) { for (let statement of body.statements) { diff --git a/src/compiler/factory.generated.ts b/src/compiler/factory.generated.ts index 56073b6de1c..c86d8f94050 100644 --- a/src/compiler/factory.generated.ts +++ b/src/compiler/factory.generated.ts @@ -2967,7 +2967,7 @@ namespace ts { } return false; } - export function isExpression(node: Node): node is Expression { + export function isExpressionNode(node: Node): node is Expression { if (node) { switch (node.kind) { case SyntaxKind.OmittedExpression: @@ -3386,6 +3386,45 @@ namespace ts { } return false; } + export function isStatementNode(node: Node): node is Statement { + if (node) { + switch (node.kind) { + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.Block: + case SyntaxKind.EmptyStatement: + case SyntaxKind.DebuggerStatement: + case SyntaxKind.MissingDeclaration: + case SyntaxKind.VariableStatement: + case SyntaxKind.ExpressionStatement: + case SyntaxKind.IfStatement: + case SyntaxKind.DoStatement: + case SyntaxKind.WhileStatement: + case SyntaxKind.ForStatement: + case SyntaxKind.ForInStatement: + case SyntaxKind.ForOfStatement: + case SyntaxKind.BreakStatement: + case SyntaxKind.ContinueStatement: + case SyntaxKind.ReturnStatement: + case SyntaxKind.WithStatement: + case SyntaxKind.SwitchStatement: + case SyntaxKind.LabeledStatement: + case SyntaxKind.ThrowStatement: + case SyntaxKind.TryStatement: + case SyntaxKind.ClassDeclaration: + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.TypeAliasDeclaration: + case SyntaxKind.EnumDeclaration: + case SyntaxKind.ModuleDeclaration: + case SyntaxKind.ModuleBlock: + case SyntaxKind.ImportEqualsDeclaration: + case SyntaxKind.ImportDeclaration: + case SyntaxKind.ExportDeclaration: + case SyntaxKind.ExportAssignment: + return true; + } + } + return false; + } export function isExpressionOrVariableDeclarationList(node: Node): node is Expression | VariableDeclarationList { if (node) { switch (node.kind) { diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 1276fea83b3..930be0126da 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -140,23 +140,20 @@ namespace ts { } export function createNumericLiteral2(value: number, location?: TextRange, flags?: NodeFlags): LiteralExpression { - let node = factory.createNumericLiteral(String(value), location, flags); + let node = createNumericLiteral(String(value), location, flags); return node; } export function createPropertyAccessExpression2(expression: Expression, name: Identifier, location?: TextRange, flags?: NodeFlags) { - return factory.createPropertyAccessExpression( - factory.parenthesizeForAccess(expression), - factory.createNode(SyntaxKind.DotToken), - name, - location, - flags - ); + 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 const enum BinaryOperand { - Left, - Right + export function makeSynthesized(node: TNode): TNode { + return nodeIsSynthesized(node) ? node : cloneNode(node); } export function parenthesizeForBinary(expr: Expression, operator: SyntaxKind) { @@ -175,7 +172,7 @@ namespace ts { let operatorPrecedence = getBinaryOperatorPrecedence(operator); if (exprPrecedence < operatorPrecedence) { // lower precedence, the expression needs parenthesis - return factory.createParenthesizedExpression(expr); + return createParenthesizedExpression(expr); } else { // higher precedence. @@ -205,25 +202,15 @@ namespace ts { return expr; } - return factory.createParenthesizedExpression(expr); + return createParenthesizedExpression(expr); } export function createCallExpression2(expression: Expression, _arguments?: Expression[], location?: TextRange, flags?: NodeFlags) { - return factory.createCallExpression( - parenthesizeForAccess(expression), - /*typeArguments*/ undefined, - _arguments, - location, - flags - ); + return createCallExpression(parenthesizeForAccess(expression), undefined, _arguments, location, flags); } export function createObjectLiteralExpression2(properties?: ObjectLiteralElement[]) { - return createObjectLiteralExpression( - /*decorators*/ undefined, - /*modifiers*/ undefined, - createNodeArray(properties) - ); + return createObjectLiteralExpression(undefined, undefined, createNodeArray(properties)); } export function createAssignmentExpression(left: Expression, right: Expression) { @@ -251,353 +238,150 @@ namespace ts { } export function createBinaryExpression2(left: Expression, operator: SyntaxKind, right: Expression) { - return factory.createBinaryExpression( - parenthesizeForBinary(left, operator), - factory.createNode(operator), - parenthesizeForBinary(right, operator) - ); + return createBinaryExpression(parenthesizeForBinary(left, operator), createNode(operator), parenthesizeForBinary(right, operator)); } export function createConditionalExpression2(condition: Expression, whenTrue: Expression, whenFalse: Expression, location?: TextRange, flags?: NodeFlags) { - return factory.createConditionalExpression( - condition, - factory.createNode(SyntaxKind.QuestionToken), - whenTrue, - factory.createNode(SyntaxKind.ColonToken), - whenFalse, - location, - flags - ); + 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 factory.createParameter( - /*decorators*/ undefined, - /*modifiers*/ undefined, - /*dotDotDotToken*/ undefined, - name, - /*questionToken*/ undefined, - /*type*/ undefined, - initializer, - location, - flags - ); + return createParameter(undefined, undefined, undefined, name, undefined, undefined, initializer, location, flags); } export function createRestParameter(name: Identifier, location?: TextRange, flags?: NodeFlags) { - return factory.createParameter( - /*decorators*/ undefined, - /*modifiers*/ undefined, - factory.createNode(SyntaxKind.DotDotDotToken), - name, - /*questionToken*/ undefined, - /*type*/ undefined, - /*initializer*/ undefined, - location, - flags - ); - } - - export function createVariableStatement2(declarationList: VariableDeclarationList, location?: TextRange, flags?: NodeFlags) { - return factory.createVariableStatement( - /*decorators*/ undefined, - /*modifiers*/ undefined, - declarationList, - location, - flags - ); + 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 factory.createVariableDeclaration( - name, - /*type*/ undefined, - initializer, - location, - flags - ); + return createVariableDeclaration(name, undefined, initializer, location, flags); } - export function createClassDeclaration2(name: Identifier, heritageClause: HeritageClause, members: ClassElement[], location?: TextRange, flags?: NodeFlags): ClassDeclaration { - return factory.createClassDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - name, - /*typeParameters*/ undefined, - heritageClause ? [heritageClause] : undefined, - members, - 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 createClassExpression2(name: Identifier, heritageClause: HeritageClause, members: ClassElement[]): ClassExpression { - return factory.createClassExpression( - /*decorators*/ undefined, - /*modifiers*/ undefined, - name, - /*typeParameters*/ undefined, - heritageClause ? [heritageClause] : undefined, - members - ); + 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 factory.createConstructor( - /*decorators*/ undefined, - /*modifiers*/ undefined, - parameters, - /*type*/ undefined, - body, - location, - flags - ); + return createConstructor(undefined, undefined, parameters, undefined, body, location, flags); } - + export function createMethodDeclaration2(name: PropertyName, parameters: Array, body: Block, location?: TextRange, flags?: NodeFlags): MethodDeclaration { - return factory.createMethodDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - /*asteriskToken*/ undefined, - name, - /*typeParameters*/ undefined, - parameters, - /*type*/ undefined, - body, - location, - flags - ); + 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 factory.createGetAccessor( - /*decorators*/ undefined, - /*modifiers*/ undefined, - name, - parameters, - /*type*/ undefined, - body, - location, - flags - ); + 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 factory.createSetAccessor( - /*decorators*/ undefined, - /*modifiers*/ undefined, - name, - parameters, - /*type*/ undefined, - body, - location, - flags - ); + return createSetAccessor(undefined, undefined, name, parameters, undefined, body, location, flags); } export function createFunctionDeclaration2(name: Identifier, parameters: ParameterDeclaration[], body: Block, location?: TextRange, flags?: NodeFlags) { - return factory.createFunctionDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - /*asteriskToken*/ undefined, - name, - /*typeParameters*/ undefined, - parameters, - /*type*/ undefined, - body, - location, - flags - ); + 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 factory.createFunctionDeclaration( - /*decorators*/ undefined, - /*modifiers*/ undefined, - asteriskToken, - name, - /*typeParameters*/ undefined, - parameters, - /*type*/ undefined, - body, - location, - flags - ); - } + 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 factory.createFunctionExpression( - /*decorators*/ undefined, - /*modifiers*/ undefined, - /*asteriskToken*/ undefined, - name, - /*typeParameters*/ undefined, - parameters, - /*type*/ undefined, - body, - location, - flags - ); + 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 factory.createFunctionExpression( - /*decorators*/ undefined, - /*modifiers*/ undefined, - asteriskToken, - name, - /*typeParameters*/ undefined, - parameters, - /*type*/ undefined, - body, - location, - flags - ); + return createFunctionExpression(undefined, undefined, asteriskToken, name, undefined, parameters, undefined, body, location, flags); } - export function createGeneratorFunctionExpression(name: Identifier, parameters: ParameterDeclaration[], body: Block, location?: TextRange, flags?: NodeFlags) { - return factory.createFunctionExpression( - /*decorators*/ undefined, - /*modifiers*/ undefined, - createNode(SyntaxKind.AsteriskToken), - name, - /*typeParameters*/ undefined, - parameters, - /*type*/ undefined, - 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 factory.createVoidExpression( - factory.createNumericLiteral2(0), - location, - flags); + return createVoidExpression(createNumericLiteral2(0), location, flags); } - + export function createPropertyOrElementAccessExpression(expression: Expression, propName: Identifier | LiteralExpression, location?: TextRange, flags?: NodeFlags): LeftHandSideExpression { - if (!nodeIsSynthesized(propName)) { - propName = cloneNode(propName); - } - - if (propName.kind !== SyntaxKind.Identifier) { - return createElementAccessExpression2(expression, propName, location, flags); - } - - return createPropertyAccessExpression2(expression, propName, location, flags); + 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 factory.createElementAccessExpression( - factory.parenthesizeForAccess(expression), - argumentExpression, - location, - flags - ); + return createElementAccessExpression(parenthesizeForAccess(expression), argumentExpression, location, flags); } export function createElementAccessExpression3(expression: Expression, index: number, location?: TextRange, flags?: NodeFlags): ElementAccessExpression { - return factory.createElementAccessExpression2( - expression, - factory.createNumericLiteral2(index), - location, - flags - ); + return createElementAccessExpression2(expression, createNumericLiteral2(index), location, flags); } export function createSliceCall(value: Expression, sliceIndex: number, location?: TextRange, flags?: NodeFlags): CallExpression { - return factory.createCallExpression2( - factory.createPropertyAccessExpression2( - factory.parenthesizeForAccess(value), - factory.createIdentifier("slice") - ), - [factory.createNumericLiteral2(sliceIndex)], - location, - flags - ); + return createCallExpression2(createPropertyAccessExpression3(value, "slice"), [createNumericLiteral2(sliceIndex)], location, flags); } export function createApplyCall(target: Expression, thisArg: Expression, _arguments: Expression, location?: TextRange, flags?: NodeFlags) { - let applyName = createIdentifier("apply"); - let propExpr = createPropertyAccessExpression2(target, applyName); - return factory.createCallExpression2(propExpr, [thisArg, _arguments], location, flags); + return factory.createCallExpression2(createPropertyAccessExpression3(target, "apply"), [thisArg, _arguments], location, flags); } export function createExtendsHelperCall(name: Identifier) { - let extendsName = factory.createIdentifier("__extends"); - let superName = factory.createIdentifier("_super"); - let callExpr = factory.createCallExpression2(extendsName, [name, superName]); - return callExpr; + return factory.createCallExpression2(createIdentifier("__extends"), [name, createIdentifier("_super")]); } export function createAwaiterHelperCall(hasLexicalArguments: boolean, promiseConstructor: EntityName | Expression, body: Block) { - let thisExpr = factory.createThisKeyword(); - let awaiterName = factory.createIdentifier("__awaiter"); - let argumentsExpr = hasLexicalArguments ? factory.createIdentifier("arguments") : factory.createVoidZeroExpression(); - let promiseExpr = promiseConstructor ? convertEntityNameToExpression(promiseConstructor) : factory.createIdentifier("Promise"); - let generatorFunc = factory.createGeneratorFunctionExpression(/*name*/ undefined, [], body); - let callExpr = factory.createCallExpression2(awaiterName, [thisExpr, argumentsExpr, promiseExpr, generatorFunc]); - return callExpr; + 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 { - if (isQualifiedName(node)) { - let left = convertEntityNameToExpression(node.left); - let right = factory.cloneNode(node.right); - return factory.createPropertyAccessExpression2(left, right); - } - else { - return factory.cloneNode(node); - } + return isQualifiedName(node) ? createPropertyAccessExpression2(convertEntityNameToExpression(node.left), cloneNode(node.right)) : cloneNode(node); } export function createDecorateHelperCall(decoratorExpressions: Expression[], target: Expression, memberName?: Expression, descriptor?: Expression) { - let _arguments: Expression[] = [ - factory.createArrayLiteralExpression(decoratorExpressions), - target - ]; - if (memberName) { - _arguments.push(memberName); - if (descriptor) { - _arguments.push(descriptor); - } - } - let decorateHelperName = factory.createIdentifier("__decorate"); - let decoratorsExpr = factory.createArrayLiteralExpression(decoratorExpressions); - let callExpr = factory.createCallExpression2(decorateHelperName, _arguments); - return callExpr; + return createCallExpression2(createIdentifier("__decorate"), append([createArrayLiteralExpression(decoratorExpressions), target], memberName, descriptor)); } export function createParamHelperCall(parameterIndex: number, decoratorExpression: Expression) { - let paramHelperName = factory.createIdentifier("__param"); - let parameterIndexExpr = factory.createNumericLiteral(String(parameterIndex)); - let callExpr = factory.createCallExpression2(paramHelperName, [parameterIndexExpr, decoratorExpression]); - return callExpr; + return createCallExpression2(createIdentifier("__param"), [createNumericLiteral2(parameterIndex), decoratorExpression]); } export function createMetadataHelperCall(metadataKey: string, metadataValue: Expression) { - let metadataHelperName = factory.createIdentifier("__metadata"); - let metadataKeyExpr = factory.createStringLiteral(metadataKey); - let callExpr = factory.createCallExpression2(metadataHelperName, [metadataKeyExpr, metadataValue]); - return callExpr; + return createCallExpression2(createIdentifier("__metadata"), [createStringLiteral(metadataKey), metadataValue]); } export function createDefinePropertyCall(target: Expression, memberName: Expression, descriptor: Expression) { - let globalObjectName = factory.createIdentifier("Object"); - let definePropertyName = factory.createIdentifier("defineProperty"); - let propExpr = factory.createPropertyAccessExpression(globalObjectName, factory.createNode(SyntaxKind.DotToken), definePropertyName); - let callExpr = factory.createCallExpression2(propExpr, [target, memberName, descriptor]); - return callExpr; + return createCallExpression2(createPropertyAccessExpression3(createIdentifier("Object"), "defineProperty"), [target, memberName, descriptor]); } export function createGetOwnPropertyDescriptorCall(target: Expression, memberName: Expression) { - let globalObjectName = factory.createIdentifier("Object"); - let getOwnPropertyDescriptorName = factory.createIdentifier("getOwnPropertyDescriptor"); - let propExpr = factory.createPropertyAccessExpression(globalObjectName, factory.createNode(SyntaxKind.DotToken), getOwnPropertyDescriptorName); - let callExpr = factory.createCallExpression2(propExpr, [target, memberName]); - return callExpr; + return createCallExpression2(createPropertyAccessExpression3(createIdentifier("Object"), "getOwnPropertyDescriptor"), [target, memberName]); } export function createDefaultValueCheck(value: Expression, defaultValue: Expression, ensureIdentifier: (value: Expression) => Expression, location?: TextRange, flags?: NodeFlags): Expression { @@ -609,45 +393,16 @@ namespace ts { return factory.createConditionalExpression2(factory.createStrictEqualityExpression(value, factory.createVoidZeroExpression()), defaultValue, value, location, flags); } - export function createMemberAccessForPropertyName(target: Expression, memberName: DeclarationName, location?: TextRange, flags?: NodeFlags): MemberExpression { - if (isStringLiteral(memberName) || isNumericLiteral(memberName)) { - return factory.createElementAccessExpression2( - target, - cloneNode(memberName), - location, - flags - ); - } - else if (isComputedPropertyName(memberName)) { - // TODO(rbuckton): Decorators? Will be handled higher in the stack I think. - return factory.createElementAccessExpression2( - target, - cloneNode(memberName.expression), - location, - flags - ); - } - else { - return factory.createPropertyAccessExpression2( - target, - cloneNode(memberName), - 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[]) { - let expression = expressions[0]; - for (let i = 1; i < expressions.length; i++) { - expression = createBinaryExpression2( - expression, - SyntaxKind.CommaToken, - expressions[i] - ); - } - - return expression; + return reduceLeft(expressions, createCommaExpression); } } diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index 199e7e9b638..57f3ffbb0a9 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -418,6 +418,10 @@ namespace ts { /* @internal */ export function skipTrivia(text: string, pos: number, stopAfterLineBreak?: boolean): number { + if (pos < 0) { + return pos; + } + // Keep in sync with couldStartTrivia while (true) { let ch = text.charCodeAt(pos); diff --git a/src/compiler/transform.ts b/src/compiler/transform.ts index 999bcc1a76a..053b972de1f 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 { @@ -255,10 +255,13 @@ namespace ts { } export type Visitor = (input: Node, output: (node: Node) => void) => void; + export type PipelineOutput = (node: TOut) => void; + export type Pipeline = (input: TIn, output: PipelineOutput) => void; + export type NodeTest = (node: Node) => node is T; + export const enum VisitorFlags { NewLexicalEnvironment = 1 << 1, - PreserveStack = 1 << 2, ReturnUndefinedIfEmpty = 1 << 3, } @@ -591,6 +594,24 @@ namespace ts { transformFlags |= aggregateTransformFlagsForThisNodeAndSubtree(child); } + function verifyNode(node: Node, nodeTestCallback: (node: Node) => node is TOut): node is TOut { + if (!nodeTestCallback(node)) { + Debug.fail("Incorrect node kind after visit"); + return false; + } + return true; + } + + function writeNodeToPipeline(node: Node, output: (node: TOut) => void, nodeTest: (node: Node) => node is TOut): void { + if (!node) { + return; + } + + if (verifyNode(node, nodeTest)) { + output(node); + } + } + /** * A function passed to a visitor callback that can be used to write a single node. * @param node The node to write @@ -689,43 +710,20 @@ namespace ts { } } - function pipeOne(input: Node, output: (node: Node) => void, visitor: Visitor, flags: VisitorFlags): void { - if (!(flags & VisitorFlags.PreserveStack)) { - nodeStack.pushNode(input); - visitor(input, output); - nodeStack.popNode(); - } - else { - visitor(input, output); - } + function addNodeTestToPipeline(output: (node: TOut) => void, nodeTest: (node: Node) => node is TOut): (node: TOut) => void { + return (node: Node) => writeNodeToPipeline(node, output, nodeTest); } - function pipeMany(input: Node[], output: (node: Node) => void, visitor: Visitor, flags: VisitorFlags): void { - if (!(flags & VisitorFlags.PreserveStack)) { - // For perf reasons, we push `undefined` as the current node and set it to the correct - // value for each iteration of the loop below. This avoids excessive push and pop - // operations on `ancestorStack`. - nodeStack.pushNode(/*node*/ undefined); - - // Visit each input node - for (let i = 0, l = input.length; i < l; ++i) { - let currentNode = input[i]; - nodeStack.setNode(currentNode); - visitor(currentNode, output); - } - - // For the perf reasons mentioned above, we pop the current node at the end of the loop. - nodeStack.popNode(); - } - else { - for (let node of input) { - visitor(node, output); - } - } - } - - function pipeOneOrMany(input: T, output: (node: Node) => void, visitor: Visitor, flags: VisitorFlags, pipe: (input: T, output: (node: Node) => void, visitor: Visitor, flags: VisitorFlags) => void): void { - if (!input) { + /** + * Pipes an input node (or nodes) into an output callback by passing it through a visitor callback. + * @remarks + * The primary responsibility of `pipeOneOrMany` is to execute the `visitor` callback for each + * input node, passing the input node and the output callback. + * 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, nodeTest: (node: Node) => node is TOut): void { + if (!inputNode && !inputNodes) { return; } @@ -746,21 +744,44 @@ namespace ts { hoistedFunctionDeclarations = undefined; } - pipe(input, output, visitor, flags & ~VisitorFlags.NewLexicalEnvironment); + // If a node test was supplied, we need to wrap the output with an assertion + if (nodeTest) { + output = addNodeTestToPipeline(output, nodeTest); + } + + if (inputNode) { + let nodeWasPushed = nodeStack.tryPushNode(inputNode); + pipeline(inputNode, output); + if (nodeWasPushed) { + nodeStack.popNode(); + } + } + else { + // For perf reasons, we push `undefined` as the current node and set it to the correct + // value for each iteration of the loop below. This avoids excessive push and pop + // operations on `nodeStack`. + nodeStack.pushNode(/*node*/ undefined); + + // Visit each input node + for (let node of inputNodes) { + nodeStack.setNode(node); + pipeline(node, output); + } + + // For the perf reasons mentioned above, we pop the current node at the end of the loop. + nodeStack.popNode(); + } // 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.NewLexicalEnvironment) { if (hoistedVariableDeclarations) { - output(factory.createVariableStatement2( - factory.createVariableDeclarationList( - hoistedVariableDeclarations - ) - )); + var stmt = factory.createVariableStatement2(factory.createVariableDeclarationList(hoistedVariableDeclarations)); + output(stmt); } else if (hoistedFunctionDeclarations) { for (let decl of hoistedFunctionDeclarations) { - output(decl); + output(decl); } } @@ -770,33 +791,11 @@ namespace ts { hoistedFunctionDeclarations = savedHoistedFunctionDeclarations; } } - - /** - * Writes the result from visiting a single input node to an output node array. - * @param input The source node to visit. - * @param output The destination node array to which to write the results from visiting each node. - * @param visitor The callback to execute as we visit each node in the source. - * @param newLexicalEnvironment A value that indicates whether this pipeline starts a new lexical environment. - */ - export function pipeNode(input: Node, output: (node: Node) => void, visitor: Visitor, flags?: VisitorFlags): void { - pipeOneOrMany(input, output, visitor, flags, pipeOne); - } - - /** - * Pipelines the results of visiting each node from an input source to an output callback function. - * @param input The source nodes to visit. - * @param output The callback passed to `visitor` (below) to write each visited node. - * @param visitor The callback to execute as we visit each node in the source. - * @param newLexicalEnvironment A value that indicates whether this pipeline starts a new lexical environment. - */ - export function pipeNodes(input: Node[], output: (node: Node) => void, visitor: Visitor, flags?: VisitorFlags): void { - pipeOneOrMany(input, output, visitor, flags, pipeMany); - } - - function emitOneOrMany(input: T, output: Node[], visitor: Visitor, flags: VisitorFlags, pipe: (input: T, output: (node: Node) => void, visitor: Visitor, flags: VisitorFlags) => void): void { + + function emitOneOrMany(inputNode: TIn, inputNodes: TIn[], pipeline: Pipeline, output: TOut[], flags: VisitorFlags, nodeTest: (node: Node) => node is TOut): TOut[] { // Exit early if we have nothing to do - if (!input) { - return; + if (!inputNode && !inputNodes) { + return undefined; } // Preserve the current environment on the call stack as we descend into the tree @@ -811,40 +810,64 @@ namespace ts { offsetWritten = 0; writeNodeToNodeArrayFastOrSlow = writeNodeToNodeArrayFast; - pipe(input, writeNodeToNodeArray, visitor, flags | VisitorFlags.PreserveStack); + pipeOneOrMany(inputNode, inputNodes, pipeline, writeNodeToNodeArray, flags, nodeTest); // Restore previous environment originalNodes = savedOriginalNodes; updatedNodes = savedUpdatedNodes; offsetWritten = savedOffsetWritten; - writeNodeToNodeArrayFastOrSlow = savedWriteNodeToNodeArrayFastOrSlow; + writeNodeToNodeArrayFastOrSlow = savedWriteNodeToNodeArrayFastOrSlow; + + return output; + } + + /** + * Pipelines the results of visiting a single input node to an output callback function. + * @param input The source node to visit. + * @param pipeline The callback to execute as we visit each node in the source. + * @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, nodeTest?: NodeTest): void { + pipeOneOrMany(input, undefined, pipeline, output, flags, nodeTest); + } + + /** + * Pipelines the results of visiting each node from an input source to an output callback function. + * @param input The source nodes to visit. + * @param pipeline The callback to execute as we visit each node in the source. + * @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, nodeTest?: NodeTest): void { + pipeOneOrMany(undefined, input, pipeline, output, flags, nodeTest); } /** * Writes the result from visiting a single input node to an output node array. * @param input The source node to visit. + * @param pipeline The callback to execute as we visit each node in the source. * @param output The destination node array to which to write the results from visiting each node. - * @param visitor The callback to execute as we visit each node in the source. - * @param newLexicalEnvironment A value that indicates whether this pipeline starts a new lexical environment. + * @param flags Flags that affect the pipeline. */ - export function emitNode(input: Node, output: Node[], visitor: (input: Node, write: (output: Node) => void) => void, flags?: VisitorFlags): void { - emitOneOrMany(input, output, visitor, flags, pipeOne); + export function emitNode(input: TIn, pipeline: Pipeline, output: TOut[], flags?: VisitorFlags, nodeTest?: NodeTest): void { + emitOneOrMany(input, undefined, pipeline, output, 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. + * @param pipeline The callback to execute as we visit each node in the source. * @param output The destination node array to which to write the results from visiting each node. - * @param visitor The callback to execute as we visit each node in the source. - * @param newLexicalEnvironment A value that indicates whether this pipeline starts a new lexical environment. + * @param flags Flags that affect the pipeline. */ - export function emitNodes(input: Node[], output: Node[], visitor: (input: Node, write: (output: Node) => void) => void, flags?: VisitorFlags): void { - emitOneOrMany(input, output, visitor, flags, pipeMany); + export function emitNodes(input: TIn[], pipeline: Pipeline, output: TOut[], flags?: VisitorFlags, nodeTest?: NodeTest): void { + emitOneOrMany(undefined, input, pipeline, output, flags, nodeTest); } - export function visitNode(node: T, visitor: (input: Node, write: (node: Node) => void) => void, flags?: VisitorFlags): T; - export function visitNode(node: TIn, visitor: (input: TIn, write: (node: TOut) => void) => void, flags?: VisitorFlags): TOut; - export function visitNode(node: TIn, visitor: (input: TIn, write: (node: TOut) => void) => void, flags?: VisitorFlags): TOut { + export function visitNode(node: T, visitor: Visitor, flags?: VisitorFlags): T; + export function visitNode(node: TIn, visitor: Visitor, flags?: VisitorFlags, nodeTest?: (node: Node) => node is TOut): TOut; + export function visitNode(node: TIn, visitor: Visitor, flags?: VisitorFlags, nodeTest?: (node: Node) => node is TOut): TOut { if (!node) { return undefined; } @@ -854,7 +877,7 @@ namespace ts { updatedNode = undefined; writeNodeFastOrSlow = writeNodeSlow; - pipeNode(node, writeNode, visitor, flags); + pipeNode(node, visitor, writeNode, flags, nodeTest); let visited = readNode(); updatedNode = savedUpdatedNode; @@ -863,7 +886,7 @@ namespace ts { return visited; } - export function visitStatement(node: Statement, visitor: (input: Node, write: (node: Node) => void) => void, flags?: VisitorFlags) { + export function visitStatement(node: Statement, visitor: Visitor, flags?: VisitorFlags) { if (!node) { return node; } @@ -876,7 +899,7 @@ namespace ts { updatedBlock = undefined; writeStatementFastOrSlow = writeStatementSlow; - pipeNode(node, writeStatement, visitor, flags); + pipeNode(node, visitor, writeStatement, flags, isStatementNode); let visited = readStatement(); updatedStatement = savedUpdatedStatement; @@ -886,9 +909,9 @@ namespace ts { return visited; } - export function visitNodes(nodes: T[], visitor: (input: Node, output: (node: Node) => void) => void, flags?: VisitorFlags): NodeArray; - export function visitNodes(nodes: TIn[], visitor: (input: TIn, output: (node: TOut) => void) => void, flags?: VisitorFlags): NodeArray; - export function visitNodes(nodes: TIn[], visitor: (input: TIn, output: (node: TOut) => void) => void, flags?: VisitorFlags): NodeArray { + export function visitNodes(nodes: T[], visitor: Visitor, flags?: VisitorFlags): NodeArray; + export function visitNodes(nodes: TIn[], visitor: Visitor, flags?: VisitorFlags): NodeArray; + export function visitNodes(nodes: TIn[], visitor: Visitor, flags?: VisitorFlags, nodeTest?: (node: Node) => node is TOut): NodeArray { // Exit early if we have nothing to do if (!nodes) { return undefined; @@ -907,7 +930,7 @@ namespace ts { writeNodeToNodeArrayFastOrSlow = writeNodeToNodeArraySlow; // Pipe each node from input into output - pipeNodes(originalNodes, writeNodeToNodeArray, visitor, flags); + pipeNodes(nodes, visitor, writeNodeToNodeArray, flags, nodeTest); let visited = readNodeArray(flags); @@ -969,7 +992,7 @@ namespace ts { ); default: - if (isExpression(node)) { + if (isExpressionNode(node)) { return visitExpressionFunctionBodyInNewLexicalEnvironment(node, visitor); } else { diff --git a/src/compiler/transforms/es5.ts b/src/compiler/transforms/es5.ts index b26b05ec300..8cdf74e88e0 100644 --- a/src/compiler/transforms/es5.ts +++ b/src/compiler/transforms/es5.ts @@ -115,7 +115,7 @@ namespace ts.transform { function transformClassLikeDeclaration(node: ClassLikeDeclaration, name: Identifier): LeftHandSideExpression { let baseTypeNode = getClassExtendsHeritageClauseElement(node); let classBody = factory.createBlock([]); - emitNode(node, classBody.statements, transformClassBody); + emitNode(node, transformClassBody, classBody.statements); let superExpr = baseTypeNode ? visitNode(baseTypeNode.expression, transformNode) : undefined; let superName = baseTypeNode ? factory.createIdentifier("_super") : undefined; @@ -153,7 +153,7 @@ namespace ts.transform { let body = factory.createBlock([]); if (constructor) { - emitNode(constructor, body.statements, transformConstructor, VisitorFlags.NewLexicalEnvironment); + emitNode(constructor, transformConstructor, body.statements, VisitorFlags.NewLexicalEnvironment); } else if (baseTypeNode) { let superCall = createDefaultSuperCall(); @@ -168,7 +168,7 @@ namespace ts.transform { emitCaptureThisForNode(constructor, write); emitDefaultValueAssignments(constructor, write); emitRestParameter(constructor, write); - pipeNodes(constructor.body.statements, write, transformNode); + pipeNodes(constructor.body.statements, transformNode, write); } function transformParameter(node: ParameterDeclaration, write: (node: ParameterDeclaration) => void) { @@ -194,7 +194,7 @@ namespace ts.transform { function emitDefaultValueAssignments(node: FunctionLikeDeclaration, write: (node: Statement) => void) { if (!(node.transformFlags & (TransformFlags.SubtreeContainsParameterInitializer | TransformFlags.SubtreeContainsParameterBindingPattern))) { - return + return; } for (let parameter of node.parameters) { @@ -453,14 +453,14 @@ namespace ts.transform { function rewriteFunctionExpression(node: FunctionLikeDeclaration, name: Identifier, location: TextRange): FunctionExpression { let parameters = visitNodes(node.parameters, transformNode); let body = factory.createBlock([], node.body); - emitNode(node, body.statements, transformFunctionBody, VisitorFlags.NewLexicalEnvironment); + emitNode(node, transformFunctionBody, body.statements, VisitorFlags.NewLexicalEnvironment); return factory.createFunctionExpression2(name, parameters, body, location); } function transformFunctionDeclaration(node: FunctionDeclaration, write: (node: Statement) => void): void { let parameters = visitNodes(node.parameters, transformNode); let body = factory.createBlock([], node.body); - emitNode(node, body.statements, transformFunctionBody, VisitorFlags.NewLexicalEnvironment); + emitNode(node, transformFunctionBody, body.statements, VisitorFlags.NewLexicalEnvironment); write(factory.createFunctionDeclaration2(node.name, parameters, body, /*location*/ node)); } @@ -469,7 +469,7 @@ namespace ts.transform { let parameters = visitNodes(node.parameters, transformNode); let newBody = factory.createBlock([], /*location*/ isBlock(node.body) ? node.body : undefined); - emitNode(node, newBody.statements, transformFunctionBody, VisitorFlags.NewLexicalEnvironment); + emitNode(node, transformFunctionBody, newBody.statements, VisitorFlags.NewLexicalEnvironment); let func = createfn(name, parameters, newBody, location); return func; @@ -482,7 +482,7 @@ namespace ts.transform { let body = node.body; if (isBlock(body)) { - pipeNodes(body.statements, write, transformNode); + pipeNodes(body.statements, transformNode, write); } else { let expr = visitNode(body, transformNode); diff --git a/src/compiler/transforms/es6.ts b/src/compiler/transforms/es6.ts index c04cd7d607a..96b44bc3daf 100644 --- a/src/compiler/transforms/es6.ts +++ b/src/compiler/transforms/es6.ts @@ -67,7 +67,7 @@ namespace ts.transform { * @param context Context information for the transform. * @param node The node to transform. */ - function transformNodeWorker(node: Node, write?: (node: Node) => void): void { + function transformNodeWorker(node: Node, write: (node: Node) => void): void { // TypeScript ambient declarations are elided. if (node.flags & NodeFlags.Ambient) { return; @@ -120,10 +120,6 @@ namespace ts.transform { // TypeScript property declarations are elided. return; - case SyntaxKind.IndexSignature: - // TypeScript index signatures 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`. @@ -240,36 +236,33 @@ namespace ts.transform { 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 thisNodeIsNamespaceExport = isNamespaceLevelExport(node); - let name = getDeclarationName(node); - let heritageClauses = visitNodes(node.heritageClauses, transformNode); - let extendsClause = heritageClauses && firstOrUndefined(heritageClauses); - let baseTypeNode = extendsClause && firstOrUndefined(extendsClause.types); + let baseTypeNode = visitAndGetClassExtendsHeritageClauseElement(node); + let classMembers: ClassElement[] = []; let constructor = transformConstructor(node, baseTypeNode); - let members = visitNodes(node.members, transformNode); - let classMembers: ClassElement[] = constructor ? [constructor, ...members] : members; - + if (constructor) { + classMembers.push(constructor); + } + + emitNodes(node.members, transformNode, classMembers); + if (nodeIsDecorated(node)) { - let classExpr = factory.createClassExpression2(/*name*/ undefined, extendsClause, classMembers); - let varDecl = factory.createVariableDeclaration2(name, classExpr); - let varDeclList = factory.createVariableDeclarationList([varDecl], /*location*/ undefined, NodeFlags.Let); - let exportFlags = isTopLevelNonDefaultExport(node) ? NodeFlags.Export : undefined; - let varStmt = factory.createVariableStatement2(varDeclList, /*location*/ node, exportFlags); + // 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 = factory.createClassExpression3(baseTypeNode, classMembers); + let varStmt = factory.createSimpleLetStatement(getDeclarationName(node), classExpr, /*location*/ node, isTopLevelNonDefaultExport(node)); varStmt.original = node; write(varStmt); } else { - let exportFlags = thisNodeIsNamespaceExport - ? undefined - : node.flags & (NodeFlags.Export | NodeFlags.Default); - let classDecl = factory.createClassDeclaration2(name, extendsClause, classMembers, /*location*/ node, exportFlags); + let exportFlags = isNamespaceLevelExport(node) ? undefined : node.flags & (NodeFlags.Export | NodeFlags.Default); + let classDecl = factory.createClassDeclaration2(getDeclarationName(node), baseTypeNode, classMembers, /*location*/ node, exportFlags); classDecl.original = node; write(classDecl); } @@ -279,50 +272,49 @@ namespace ts.transform { transformDecoratorsOfMembers(node, /*isStatic*/ true, write); transformDecoratorsOfConstructor(node, write); - if (thisNodeIsNamespaceExport) { - let namespaceExportExpr = factory.createAssignmentExpression(getModuleMemberName(node), name); - let exprStmt = factory.createExpressionStatement(namespaceExportExpr); - write(exprStmt); + if (isNamespaceLevelExport(node)) { + write(factory.createExpressionStatement(factory.createAssignmentExpression(getModuleMemberName(node), getDeclarationName(node)))); } else if (isTopLevelDefaultExport(node) && nodeIsDecorated(node)) { - let exportDefaultStmt = factory.createExportAssignment(/*decorators*/ undefined, /*modifiers*/ undefined, name); - write(exportDefaultStmt); + write(factory.createExportDefaultStatement(getDeclarationName(node))); } } function transformClassExpression(node: ClassExpression, write: (node: LeftHandSideExpression) => void) { - let name = getDeclarationName(node); - let heritageClauses = visitNodes(node.heritageClauses, transformNode); - let extendsClause = heritageClauses ? firstOrUndefined(heritageClauses) : undefined; - let baseTypeNode = extendsClause ? firstOrUndefined(extendsClause.types) : undefined; + let baseTypeNode = visitAndGetClassExtendsHeritageClauseElement(node); + let classMembers: ClassElement[] = []; let constructor = transformConstructor(node, baseTypeNode); - let members = visitNodes(node.members, transformNodeWorker); - let classMembers = constructor ? [constructor, ...members] : members; - - let newNode = factory.createClassExpression2( - name, - extendsClause, - classMembers - ); + if (constructor) { + classMembers.push(constructor); + } + emitNodes(node.members, transformNode, classMembers); + + let classExpr = factory.createClassExpression2(getDeclarationName(node), baseTypeNode, classMembers); let staticPropertyAssignments = getInitializedProperties(node, /*isStatic*/ true); if (staticPropertyAssignments) { let expressions: Expression[] = []; let tempVar = declareLocal(); - let cacheExpr = factory.createAssignmentExpression(tempVar, newNode); + let cacheExpr = factory.createAssignmentExpression(tempVar, classExpr); expressions.push(cacheExpr); transformPropertyDeclarationsToExpressions(node, staticPropertyAssignments, expressions); expressions.push(tempVar); write(factory.createParenthesizedExpression(factory.inlineExpressions(expressions))); } else { - write(newNode); + write(classExpr); } } + function visitAndGetClassExtendsHeritageClauseElement(node: ClassLikeDeclaration) { + let heritageClauses = visitNodes(node.heritageClauses, transformNode); + let extendsClause = heritageClauses && firstOrUndefined(heritageClauses); + let baseTypeNode = extendsClause && firstOrUndefined(extendsClause.types); + return baseTypeNode; + } + function isTopLevelExport(node: Node) { - return !!(node.flags & NodeFlags.Export) && - getParentNode().kind === SyntaxKind.SourceFile; + return !!(node.flags & NodeFlags.Export) && isSourceFile(getParentNode()); } function isTopLevelDefaultExport(node: Node) { @@ -334,8 +326,7 @@ namespace ts.transform { } function isNamespaceLevelExport(node: Node) { - return !!(node.flags & NodeFlags.Export) && - getParentNode().kind !== SyntaxKind.SourceFile; + return !!(node.flags & NodeFlags.Export) && !isSourceFile(getParentNode()); } function getContainingModule(): ModuleDeclaration { @@ -366,53 +357,29 @@ namespace ts.transform { let constructor = getFirstConstructorWithBody(node); let parameterPropertyAssignments = constructor ? getParametersWithPropertyAssignments(constructor) : undefined; let instancePropertyAssignments = getInitializedProperties(node, /*isStatic*/ false); - // let leadingCommentRanges: CommentRange[]; - // let trailingCommentRanges: CommentRange[]; - // for (let member of node.members) { - // if (isConstructor(member) && !member.body) { - // let leadingCommentRangesOfMember = context.getLeadingCommentRanges(member, /*onlyPinnedOrTripleSlashComments*/ true); - // leadingCommentRanges = concatenate(leadingCommentRanges, leadingCommentRangesOfMember); - // } - // } // For target ES6 and above, if there is no property assignment // do not emit constructor in class declaration. if (!parameterPropertyAssignments && !instancePropertyAssignments) { - // if (leadingCommentRanges) { - // leadingCommentRanges = concatenate(leadingCommentRanges, context.getLeadingCommentRanges(ctor)); - // ctor = factory.cloneNode(ctor); - // factory.attachCommentRanges(ctor, leadingCommentRanges, trailingCommentRanges); - // } - return constructor; } - - let parameters = - constructor ? visitNodes(constructor.parameters, transformNode) : - baseTypeNode ? [factory.createRestParameter(factory.createIdentifier("args"), /*location*/ undefined, NodeFlags.GeneratedRest)] : - []; - - // let parameters = - // constructor ? transformSignatureParameters(constructor.parameters) : - // hasBaseType ? [factory.createRestParameter(tag_id`args`, /*location*/ undefined, NodeFlags.GeneratedRest)] : - // []; - + let parameters = constructor + ? visitNodes(constructor.parameters, transformNode) + : baseTypeNode + ? [factory.createRestParameter(factory.createIdentifier("args"), /*location*/ undefined, NodeFlags.GeneratedRest)] + : []; + let statements: Statement[] = []; let superCall: ExpressionStatement; if (constructor) { - // leadingCommentRanges = concatenate( - // leadingCommentRanges, - // context.getLeadingCommentRanges(ctor) - // ); - // trailingCommentRanges = context.getTrailingCommentRanges(ctor); - if (baseTypeNode) { superCall = findInitialSuperCall(constructor); if (superCall) { statements.push(superCall); } } + if (parameterPropertyAssignments) { for (let parameter of parameterPropertyAssignments) { let name = factory.cloneNode(parameter.name); @@ -438,19 +405,11 @@ namespace ts.transform { transformPropertyDeclarationsToStatements(node, instancePropertyAssignments, n => { statements.push(n); }); if (constructor) { - let bodyStatements = superCall - ? constructor.body.statements.slice(1) - : constructor.body.statements; - statements.push(...visitNodes(bodyStatements, transformNode)); + let bodyStatements = constructor.body.statements; + emitNodes(superCall ? bodyStatements.slice(1) : bodyStatements, transformNode, statements, VisitorFlags.NewLexicalEnvironment); } - let body = constructor - ? factory.updateBlock(constructor.body, statements) - : factory.createBlock(statements); - - let newConstructor = factory.createConstructor2(parameters, body, /*location*/ constructor); - // factory.attachCommentRanges(newConstructor, leadingCommentRanges, trailingCommentRanges); - return newConstructor; + return factory.createConstructor2(parameters, factory.createBlock(statements), /*location*/ constructor); } function transformHeritageClause(node: HeritageClause, write: (node: HeritageClause) => void) { @@ -772,13 +731,14 @@ 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."); - pipeNode(node.declarationList, write, transformVariableDeclarationList); + // Debug.assert(isNamespaceLevelExport(node), "Should only reach here for exported variables." + node.declarationList.declarations[0].name); + // pipeNode(node.declarationList, write, transformVariableDeclarationList); + return accept(node, transformNode, write); } function transformVariableDeclarationList(node: VariableDeclarationList, write: (node: Statement) => void) { let expressions: Expression[] = []; - emitNodes(node.declarations, expressions, transformVariableDeclaration); + emitNodes(node.declarations, transformVariableDeclaration, expressions); if (expressions.length) { let exprStmt = factory.createExpressionStatement(factory.inlineExpressions(expressions)); diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index d5bfb62a0c8..1474900aee2 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -561,13 +561,13 @@ namespace ts { } } - declare var global: any, require: any; - if (typeof global !== "undefined" && Object.prototype.toString.call(global.process) === '[object process]') { - try { - require("source-map-support").install(); - } - catch (e) { } - } + // declare var global: any, require: any; + // if (typeof global !== "undefined" && Object.prototype.toString.call(global.process) === '[object process]') { + // try { + // require("source-map-support").install(); + // } + // catch (e) { } + // } } ts.executeCommandLine(ts.sys.args); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 513b1d80be1..6a1e76348b4 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -638,8 +638,6 @@ namespace ts { // @internal export interface ParentNavigator extends ParentNavigable { - /** Gets the root node for the current node. */ - getRoot(): Node; /** Gets the grandparent node for the current node. */ getGrandparent(): Node; /** Gets the parent node for the current node. */ @@ -650,14 +648,10 @@ namespace ts { getKind(): SyntaxKind; /** Moves the navigator to the parent node of the current node. */ moveToParent(): boolean; - /** Moves the navigator to the root node of the current node. */ - moveToRoot(): boolean; } // @internal export interface NodeStack extends ParentNavigable { - /** Gets the node at the bottom of the stack. */ - getRoot(): Node; /** Gets the node two steps down from the top of the stack. */ getGrandparent(): Node; /** Gets the node one step down from the top of the stack. */ @@ -672,6 +666,8 @@ namespace ts { findAncestorNode(match: (node: Node) => boolean): Node; /** Pushes a node onto the stack. */ pushNode(node: Node): void; + /** Pushes a node onto the stack if it is not already at the top of the stack. */ + tryPushNode(node: Node): boolean; /** Replaces the node at the top of the stack. */ setNode(node: Node): void; /** Pops the top node from the stack. */ @@ -1102,6 +1098,7 @@ namespace ts { // checker actually thinks you have something of the right type. Note: the brands are // never actually given values. At runtime they have zero cost. // @kind(SyntaxKind.OmittedExpression) + // @nodetest("isExpressionNode") export interface Expression extends Node { _expressionBrand: any; contextualType?: Type; // Used to temporarily assign a contextual type during overload resolution @@ -1372,6 +1369,7 @@ namespace ts { export type JsxChild = JsxText | JsxExpression | JsxElement | JsxSelfClosingElement; + // @nodetest("isStatementNode") export interface Statement extends Node { _statementBrand: any; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index a5f52303230..a269e66863b 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -918,7 +918,7 @@ namespace ts { } /** Returns whether the node is part of an expression. */ - export function isPartOfExpression(navigable: ParentNavigable): boolean { + export function isExpression(navigable: ParentNavigable): boolean { let nav = navigable.createParentNavigator(); while (true) { let node = nav.getNode(); @@ -1702,6 +1702,10 @@ namespace ts { export function nodeIsSynthesized(node: Node): boolean { return node && node.pos === -1; } + + export function positionIsSynthesized(pos: number): boolean { + return pos === -1; + } export function createDiagnosticCollection(): DiagnosticCollection { let nonFileDiagnostics: Diagnostic[] = []; @@ -1957,7 +1961,7 @@ namespace ts { } export function getLineOfLocalPosition(currentSourceFile: SourceFile, pos: number) { - return getLineAndCharacterOfPosition(currentSourceFile, pos).line; + return !positionIsSynthesized(pos) ? getLineAndCharacterOfPosition(currentSourceFile, pos).line : NaN; } export function getFirstConstructorWithBody(node: ClassLikeDeclaration): ConstructorDeclaration { diff --git a/src/services/breakpoints.ts b/src/services/breakpoints.ts index 8e9e816176d..227ba6a7970 100644 --- a/src/services/breakpoints.ts +++ b/src/services/breakpoints.ts @@ -59,7 +59,7 @@ namespace ts.BreakpointResolver { function spanInNode(node: Node): TextSpan { if (node) { - if (isPartOfExpression(node)) { + if (isExpression(node)) { if (node.parent.kind === SyntaxKind.DoStatement) { // Set span as if on while keyword return spanInPreviousNode(node); diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index 44b9797938f..f609edb0501 100644 --- a/src/services/formatting/rules.ts +++ b/src/services/formatting/rules.ts @@ -744,7 +744,7 @@ namespace ts.formatting { } static NodeIsInDecoratorContext(node: Node): boolean { - while (isPartOfExpression(node)) { + while (isExpression(node)) { node = node.parent; } return node.kind === SyntaxKind.Decorator;