diff --git a/src/compiler/core.ts b/src/compiler/core.ts index a1f6565ed1ffb..ee5fc07cadd4b 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -780,6 +780,9 @@ namespace ts { end: -1, flags: 0, parent: undefined, + createParentNavigator(): ParentNavigator { + return createParentNavigator(this); + } }; return Node; }, @@ -788,6 +791,213 @@ namespace ts { getSignatureConstructor: () => Signature }; + /** + * Creates an object used to navigate the ancestors of a node by following parent pointers. + * @param currentNode The current node for the navigator. + */ + export function createParentNavigator(currentNode: Node): ParentNavigator { + /** Gets the grandparent of the current node, without moving the navigator. */ + function getGrandparent() { + let parent = getParent(); + return parent ? parent.parent : undefined; + } + + /** Gets the parent of the current node, without moving the navigator. */ + function getParent() { + return currentNode ? currentNode.parent : undefined; + } + + /** Gets the current node. */ + function getNode() { + return currentNode; + } + + /** Gets the SyntaxKind for the current node. */ + function getKind() { + return currentNode ? currentNode.kind : undefined; + } + + /** Navigates to the parent of the current node if it has one. */ + function moveToParent(): boolean { + let parent = getParent(); + if (parent) { + currentNode = parent; + return true; + } + + return false; + } + + /** Creates a new ParentNavigator from the current node. */ + function createParentNavigator() { + return ts.createParentNavigator(currentNode); + } + + return { + getGrandparent, + getParent, + getNode, + getKind, + moveToParent, + createParentNavigator, + }; + } + + /** + * Creates a node stack used to maintain parent relationships without parent pointers. + */ + export function createNodeStack(): NodeStack { + let stackSize: number = 0; + let stack: Node[] = []; + let rootNode: Node; + let parentNode: Node; + let currentNode: Node; + + /** Gets the node two steps back from the top of the stack. */ + function getGrandparent() { + return peekNode(2); + } + + /** Gets the node one step back from the top of the stack. */ + function getParent() { + return parentNode; + } + + /** Gets the node at the top of the stack. */ + function getNode() { + return currentNode; + } + + /** Gets the SyntaxKind for the node at the top of the stack. */ + function getKind() { + return currentNode ? currentNode.kind : undefined; + } + + /** Pushes a node onto the stack. */ + function pushNode(node: Node): void { + stackSize++; + if (stackSize > 2) { + stack.push(parentNode); + } + else if (stackSize === 1) { + rootNode = node; + } + parentNode = currentNode; + currentNode = node; + } + + /** Pops the top node from the stack. */ + function popNode(): void { + currentNode = parentNode; + parentNode = stackSize > 2 ? stack.pop() : undefined; + stackSize--; + if (stackSize === 0) { + rootNode = undefined; + } + } + + /** Replaces the node at the top of the stack. */ + function setNode(node: Node): void { + currentNode = node; + } + + /** Peeks at a node a specified number of steps back from the top of the stack. */ + function peekNode(stackOffset: number): Node { + switch (stackOffset) { + case 0: + return currentNode; + case 1: + return parentNode; + default: + return stackSize > 2 ? stack[stackSize - 1 - stackOffset] : undefined; + } + } + + /** Traverses the stack from top to bottom until it finds an ancestor of the current node + * that matches the supplied predicate. */ + function findAncestorNode(match: (node: Node) => node is T): T; + /** Traverses the stack from top to bottom until it finds an ancestor of the current node + * that matches the supplied predicate. */ + function findAncestorNode(match: (node: Node) => boolean): Node; + function findAncestorNode(match: (node: Node) => boolean) { + if (parentNode && match(parentNode)) { + return parentNode; + } + for (let i = stack.length - 1; i >= 0; i--) { + let node = stack[i]; + if (match(node)) { + return node; + } + } + return undefined; + } + + /** Creates a parent navigator from the top of the stack. */ + function createParentNavigator() { + return createParentNavigatorFromStackOffset(0); + } + + /** Creates a parent navigator a specified number of steps back from the top of the stack. */ + function createParentNavigatorFromStackOffset(stackOffset: number): ParentNavigator { + /** Gets the node two steps back from the current stack offset. */ + function getGrandparent() { + return peekNode(stackOffset + 2); + } + + /** Gets the node one step back from the current stack offset. */ + function getParent() { + return peekNode(stackOffset + 1); + } + + /** Gets the node at the current stack offset. */ + function getNode() { + return peekNode(stackOffset); + } + + /** Gets the SyntaxKind of the node at the current stack offset. */ + function getKind() { + let node = getNode(); + return node ? node.kind : undefined; + } + + /** Navigates to the node one step back from the current stack offset. */ + function moveToParent() { + if (getParent()) { + stackOffset++; + return true; + } + + return false; + } + + /** Creates a new ParentNavigator from the current stack offset. */ + function createParentNavigator() { + return createParentNavigatorFromStackOffset(stackOffset); + } + + return { + getGrandparent, + getParent, + getNode, + getKind, + moveToParent, + createParentNavigator, + }; + } + + return { + getGrandparent, + getParent, + getNode, + getKind, + pushNode, + popNode, + setNode, + findAncestorNode, + createParentNavigator, + }; + } + export const enum AssertionLevel { None = 0, Normal = 1, diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index d84b7b70ada36..a412ff86340c2 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -13,6 +13,12 @@ namespace ts { CountMask = 0x0FFFFFFF, // Temp variable counter _i = 0x10000000, // Use/preference flag for '_i' } + + const enum StackBehavior { + Unspecified, // No node stack behavior has been specified or should be necessary + NodeIsOnTopOfStack, // The specified node is on the top of the node stack + ParentIsOnTopOfStack, // The parent of the specified node is on the top of the node stack + } // targetSourceFile is when users only want one file in entire project to be emitted. This is used in compileOnSave feature export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFile: SourceFile): EmitResult { @@ -103,6 +109,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi }; function isNodeDescendentOf(node: Node, ancestor: Node): boolean { + // node stack behavior: parent pointers are acceptable here as they refer to the original + // source tree while (node) { if (node === ancestor) return true; node = node.parent; @@ -111,6 +119,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function isUniqueLocalName(name: string, container: Node): boolean { + // node stack behavior: parent pointers are acceptable here as they refer to the original + // source tree for (let node = container; isNodeDescendentOf(node, container); node = node.nextContainer) { if (node.locals && hasProperty(node.locals, name)) { // We conservatively include alias symbols to cover cases where they're emitted as locals @@ -138,6 +148,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi let generatedNameSet: Map = {}; let nodeToGeneratedName: string[] = []; let computedPropertyNamesToGeneratedNames: string[]; + + // `nodeStack` is a stack consisting of each node in a branch of the + // tree as we descend into the branch. It is used to derive the parent + // node of any node without needing parent pointers, to help with + // the emit of synthesized nodes that do not maintain parent pointers. + let nodeStack: NodeStack; + let pushNode: (node: Node) => void; + let popNode: () => void; let extendsEmitted = false; let decorateEmitted = false; @@ -201,7 +219,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } }); } - + writeLine(); writeEmittedFiles(writer.getText(), /*writeByteOrderMark*/ compilerOptions.emitBOM); return; @@ -209,9 +227,18 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi function emitSourceFile(sourceFile: SourceFile): void { currentSourceFile = sourceFile; exportFunctionForFile = undefined; + nodeStack = createNodeStack(); + pushNode = nodeStack.pushNode; + popNode = nodeStack.popNode; emit(sourceFile); } - + + function findAncestorNode(match: (node: Node) => node is T): T; + function findAncestorNode(match: (node: Node) => boolean): Node; + function findAncestorNode(match: (node: Node) => boolean) { + return nodeStack.findAncestorNode(match); + } + function isUniqueName(name: string): boolean { return !resolver.hasGlobalName(name) && !hasProperty(currentSourceFile.identifiers, name) && @@ -262,12 +289,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function generateNameForModuleOrEnum(node: ModuleDeclaration | EnumDeclaration) { + verifyStackBehavior(StackBehavior.Unspecified); + let name = node.name.text; // Use module/enum name itself if it is unique, otherwise make a unique variation return isUniqueLocalName(name, node) ? name : makeUniqueName(name); } function generateNameForImportOrExportDeclaration(node: ImportDeclaration | ExportDeclaration) { + verifyStackBehavior(StackBehavior.Unspecified); + let expr = getExternalModuleName(node); let baseName = expr.kind === SyntaxKind.StringLiteral ? escapeIdentifier(makeIdentifierFromModuleName((expr).text)) : "module"; @@ -275,14 +306,20 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function generateNameForExportDefault() { + verifyStackBehavior(StackBehavior.Unspecified); + return makeUniqueName("default"); } function generateNameForClassExpression() { + verifyStackBehavior(StackBehavior.Unspecified); + return makeUniqueName("class"); } function generateNameForNode(node: Node) { + verifyStackBehavior(StackBehavior.Unspecified); + switch (node.kind) { case SyntaxKind.Identifier: return makeUniqueName((node).text); @@ -302,6 +339,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function getGeneratedNameForNode(node: Node) { + verifyStackBehavior(StackBehavior.Unspecified); + let id = getNodeId(node); return nodeToGeneratedName[id] || (nodeToGeneratedName[id] = unescapeIdentifier(generateNameForNode(node))); } @@ -667,27 +706,27 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi sourceMapDir = getDirectoryPath(normalizePath(jsFilePath)); } - function emitNodeWithSourceMap(node: Node) { + function emitNodeWithSourceMapAndWithoutStackBehavior(node: Node) { if (node) { if (nodeIsSynthesized(node)) { - return emitNodeWithoutSourceMap(node); + return emitNodeWithoutSourceMapAndWithoutStackBehavior(node); } if (node.kind !== SyntaxKind.SourceFile) { recordEmitNodeStartSpan(node); - emitNodeWithoutSourceMap(node); + emitNodeWithoutSourceMapAndWithoutStackBehavior(node); recordEmitNodeEndSpan(node); } else { recordNewSourceFileStart(node); - emitNodeWithoutSourceMap(node); + emitNodeWithoutSourceMapAndWithoutStackBehavior(node); } } } function emitNodeWithCommentsAndWithSourcemap(node: Node) { - emitNodeConsideringCommentsOption(node, emitNodeWithSourceMap); + emitNodeConsideringCommentsOption(node, emitNodeWithSourceMapAndWithoutStackBehavior); } - + writeEmittedFiles = writeJavaScriptAndSourceMapFile; emit = emitNodeWithCommentsAndWithSourcemap; emitStart = recordEmitNodeStartSpan; @@ -724,6 +763,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitTempDeclarations(newLine: boolean) { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack); + if (tempVariables) { if (newLine) { writeLine(); @@ -738,6 +779,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitTokenText(tokenKind: SyntaxKind, startPos: number, emitFn?: () => void) { + verifyStackBehavior(StackBehavior.Unspecified); + let tokenString = tokenToString(tokenKind); if (emitFn) { emitFn(); @@ -749,6 +792,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitOptional(prefix: string, node: Node) { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack, node); + if (node) { write(prefix); emit(node); @@ -756,6 +801,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitParenthesizedIf(node: Node, parenthesized: boolean) { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack, node); + if (parenthesized) { write("("); } @@ -772,6 +819,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitLinePreservingList(parent: Node, nodes: NodeArray, allowTrailingComma: boolean, spacesBetweenBraces: boolean) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, parent); Debug.assert(nodes.length > 0); increaseIndent(); @@ -816,6 +864,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitList(nodes: TNode[], start: number, count: number, multiLine: boolean, trailingComma: boolean, leadingComma?: boolean, noTrailingNewLine?: boolean, emitNode?: (node: TNode) => void): number { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack); + if (!emitNode) { emitNode = emit; } @@ -853,16 +903,22 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitCommaList(nodes: Node[]) { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack); + if (nodes) { emitList(nodes, 0, nodes.length, /*multiline*/ false, /*trailingComma*/ false); } } function emitLines(nodes: Node[]) { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack); + emitLinesStartingAt(nodes, /*startIndex*/ 0); } function emitLinesStartingAt(nodes: Node[], startIndex: number): void { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack); + for (let i = startIndex; i < nodes.length; i++) { writeLine(); emit(nodes[i]); @@ -870,6 +926,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function isBinaryOrOctalIntegerLiteral(node: LiteralExpression, text: string): boolean { + verifyStackBehavior(StackBehavior.Unspecified); + if (node.kind === SyntaxKind.NumericLiteral && text.length > 1) { switch (text.charCodeAt(1)) { case CharacterCodes.b: @@ -884,6 +942,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitLiteral(node: LiteralExpression) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + let text = getLiteralText(node); if ((compilerOptions.sourceMap || compilerOptions.inlineSourceMap) && (node.kind === SyntaxKind.StringLiteral || isTemplateLiteralKind(node.kind))) { @@ -899,6 +959,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function getLiteralText(node: LiteralExpression) { + verifyStackBehavior(StackBehavior.Unspecified); + // Any template literal or string literal with an extended escape // (e.g. "\u{0067}") will need to be downleveled as a escaped string literal. if (languageVersion < ScriptTarget.ES6 && (isTemplateLiteralKind(node.kind) || node.hasExtendedUnicodeEscape)) { @@ -907,7 +969,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi // If we don't need to downlevel and we can reach the original source text using // the node's parent reference, then simply get the text as it was originally written. - if (node.parent) { + if (!nodeIsSynthesized(node) && node.parent) { return getSourceTextOfNodeFromSourceFile(currentSourceFile, node); } @@ -936,6 +998,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitDownlevelRawTemplateLiteral(node: LiteralExpression) { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack, node); + // Find original source text, since we need to emit the raw strings of the tagged template. // The raw strings contain the (escaped) strings of what the user wrote. // Examples: `\n` is converted to "\\n", a template string with a newline to "\n". @@ -958,13 +1022,17 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitDownlevelTaggedTemplateArray(node: TaggedTemplateExpression, literalEmitter: (literal: LiteralExpression) => void) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + write("["); - if (node.template.kind === SyntaxKind.NoSubstitutionTemplateLiteral) { - literalEmitter(node.template); + let template = node.template; + if (template.kind === SyntaxKind.NoSubstitutionTemplateLiteral) { + literalEmitter(template); } else { - literalEmitter((node.template).head); - forEach((node.template).templateSpans, (child) => { + let head = (template).head; + literalEmitter(head); + forEach((template).templateSpans, (child) => { write(", "); literalEmitter(child.literal); }); @@ -973,6 +1041,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitDownlevelTaggedTemplate(node: TaggedTemplateExpression) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + let tempVariable = createAndRecordTempVariable(TempFlags.Auto); write("("); emit(tempVariable); @@ -991,155 +1061,157 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi // Now we emit the expressions if (node.template.kind === SyntaxKind.TemplateExpression) { - forEach((node.template).templateSpans, templateSpan => { - write(", "); - let needsParens = templateSpan.expression.kind === SyntaxKind.BinaryExpression - && (templateSpan.expression).operatorToken.kind === SyntaxKind.CommaToken; - emitParenthesizedIf(templateSpan.expression, needsParens); - }); + visitNodeWithPushStackBehavior(node.template, emitTemplateSpansInTemplateExpressionForDownlevelTaggedTemplate); } write("))"); } - + + function emitTemplateSpansInTemplateExpressionForDownlevelTaggedTemplate(node: TemplateExpression) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + forEach(node.templateSpans, emitTemplateSpanForDownlevelTaggedTemplateWithPushStackBehavior); + } + + function emitTemplateSpanForDownlevelTaggedTemplateWithPushStackBehavior(node: TemplateSpan) { + visitNodeWithPushStackBehavior(node, emitTemplateSpanForDownlevelTaggedTemplate); + } + + function emitTemplateSpanForDownlevelTaggedTemplate(node: TemplateSpan) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + write(", "); + let needsParens = node.expression.kind === SyntaxKind.BinaryExpression + && (node.expression).operatorToken.kind === SyntaxKind.CommaToken; + emitParenthesizedIf(node.expression, needsParens); + } + function emitTemplateExpression(node: TemplateExpression): void { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + // In ES6 mode and above, we can simply emit each portion of a template in order, but in // ES3 & ES5 we must convert the template expression into a series of string concatenations. if (languageVersion >= ScriptTarget.ES6) { forEachChild(node, emit); return; } + + emitTemplateExpressionBelowES6(node); + } + + function emitTemplateExpressionBelowES6(node: TemplateExpression): void { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); - let emitOuterParens = isExpression(node.parent) - && templateNeedsParens(node, node.parent); - + let nav = nodeStack.createParentNavigator(); + nav.moveToParent(); + + let emitOuterParens = isExpression(nav) && templateNeedsParens(node); if (emitOuterParens) { write("("); } let headEmitted = false; - if (shouldEmitTemplateHead()) { - emitLiteral(node.head); + if (shouldEmitTemplateHead(node)) { + visitNodeWithPushStackBehavior(node.head, emitLiteral); headEmitted = true; } for (let i = 0, n = node.templateSpans.length; i < n; i++) { let templateSpan = node.templateSpans[i]; - - // Check if the expression has operands and binds its operands less closely than binary '+'. - // If it does, we need to wrap the expression in parentheses. Otherwise, something like - // `abc${ 1 << 2 }` - // becomes - // "abc" + 1 << 2 + "" - // which is really - // ("abc" + 1) << (2 + "") - // rather than - // "abc" + (1 << 2) + "" - let needsParens = templateSpan.expression.kind !== SyntaxKind.ParenthesizedExpression - && comparePrecedenceToBinaryPlus(templateSpan.expression) !== Comparison.GreaterThan; - if (i > 0 || headEmitted) { // If this is the first span and the head was not emitted, then this templateSpan's // expression will be the first to be emitted. Don't emit the preceding ' + ' in that // case. write(" + "); } - - emitParenthesizedIf(templateSpan.expression, needsParens); - - // Only emit if the literal is non-empty. - // The binary '+' operator is left-associative, so the first string concatenation - // with the head will force the result up to this point to be a string. - // Emitting a '+ ""' has no semantic effect for middles and tails. - if (templateSpan.literal.text.length !== 0) { - write(" + "); - emitLiteral(templateSpan.literal); - } + + visitNodeWithPushStackBehavior(templateSpan, emitTemplateSpanBelowES6); } if (emitOuterParens) { write(")"); } + } + + function shouldEmitTemplateHead(node: TemplateExpression) { + verifyStackBehavior(StackBehavior.Unspecified, node); + + // If this expression has an empty head literal and the first template span has a non-empty + // literal, then emitting the empty head literal is not necessary. + // `${ foo } and ${ bar }` + // can be emitted as + // foo + " and " + bar + // This is because it is only required that one of the first two operands in the emit + // output must be a string literal, so that the other operand and all following operands + // are forced into strings. + // + // If the first template span has an empty literal, then the head must still be emitted. + // `${ foo }${ bar }` + // must still be emitted as + // "" + foo + bar - function shouldEmitTemplateHead() { - // If this expression has an empty head literal and the first template span has a non-empty - // literal, then emitting the empty head literal is not necessary. - // `${ foo } and ${ bar }` - // can be emitted as - // foo + " and " + bar - // This is because it is only required that one of the first two operands in the emit - // output must be a string literal, so that the other operand and all following operands - // are forced into strings. - // - // If the first template span has an empty literal, then the head must still be emitted. - // `${ foo }${ bar }` - // must still be emitted as - // "" + foo + bar - - // There is always atleast one templateSpan in this code path, since - // NoSubstitutionTemplateLiterals are directly emitted via emitLiteral() - Debug.assert(node.templateSpans.length !== 0); - - return node.head.text.length !== 0 || node.templateSpans[0].literal.text.length === 0; - } - - function templateNeedsParens(template: TemplateExpression, parent: Expression) { - switch (parent.kind) { - case SyntaxKind.CallExpression: - case SyntaxKind.NewExpression: - return (parent).expression === template; - case SyntaxKind.TaggedTemplateExpression: - case SyntaxKind.ParenthesizedExpression: - return false; - default: - return comparePrecedenceToBinaryPlus(parent) !== Comparison.LessThan; - } - } + // There is always atleast one templateSpan in this code path, since + // NoSubstitutionTemplateLiterals are directly emitted via emitLiteral() + Debug.assert(node.templateSpans.length !== 0); - /** - * Returns whether the expression has lesser, greater, - * or equal precedence to the binary '+' operator - */ - function comparePrecedenceToBinaryPlus(expression: Expression): Comparison { - // All binary expressions have lower precedence than '+' apart from '*', '/', and '%' - // which have greater precedence and '-' which has equal precedence. - // All unary operators have a higher precedence apart from yield. - // Arrow functions and conditionals have a lower precedence, - // although we convert the former into regular function expressions in ES5 mode, - // and in ES6 mode this function won't get called anyway. - // - // TODO (drosen): Note that we need to account for the upcoming 'yield' and - // spread ('...') unary operators that are anticipated for ES6. - switch (expression.kind) { - case SyntaxKind.BinaryExpression: - switch ((expression).operatorToken.kind) { - case SyntaxKind.AsteriskToken: - case SyntaxKind.SlashToken: - case SyntaxKind.PercentToken: - return Comparison.GreaterThan; - case SyntaxKind.PlusToken: - case SyntaxKind.MinusToken: - return Comparison.EqualTo; - default: - return Comparison.LessThan; - } - case SyntaxKind.YieldExpression: - case SyntaxKind.ConditionalExpression: - return Comparison.LessThan; - default: - return Comparison.GreaterThan; - } - } + return node.head.text.length !== 0 || node.templateSpans[0].literal.text.length === 0; } + function templateNeedsParens(template: TemplateExpression) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, template); + + let parentNode = nodeStack.getParent(); + switch (parentNode.kind) { + case SyntaxKind.CallExpression: + case SyntaxKind.NewExpression: + return (parentNode).expression === template; + case SyntaxKind.TaggedTemplateExpression: + case SyntaxKind.ParenthesizedExpression: + return false; + default: + return comparePrecedenceToBinaryPlus(parentNode) !== Comparison.LessThan; + } + } + function emitTemplateSpan(span: TemplateSpan) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, span); + emit(span.expression); emit(span.literal); } + function emitTemplateSpanBelowES6(node: TemplateSpan) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + + // Check if the expression has operands and binds its operands less closely than binary '+'. + // If it does, we need to wrap the expression in parentheses. Otherwise, something like + // `abc${ 1 << 2 }` + // becomes + // "abc" + 1 << 2 + "" + // which is really + // ("abc" + 1) << (2 + "") + // rather than + // "abc" + (1 << 2) + "" + let needsParens = node.expression.kind !== SyntaxKind.ParenthesizedExpression + && comparePrecedenceToBinaryPlus(node.expression) !== Comparison.GreaterThan; + + emitParenthesizedIf(node.expression, needsParens); + + // Only emit if the literal is non-empty. + // The binary '+' operator is left-associative, so the first string concatenation + // with the head will force the result up to this point to be a string. + // Emitting a '+ ""' has no semantic effect for middles and tails. + if (node.literal.text.length !== 0) { + write(" + "); + visitNodeWithPushStackBehavior(node.literal, emitLiteral); + } + } + function jsxEmitReact(node: JsxElement|JsxSelfClosingElement) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + /// Emit a tag name, which is either '"div"' for lower-cased names, or /// 'Div' for upper-cased or dotted names function emitTagName(name: Identifier|QualifiedName) { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack, name); + if (name.kind === SyntaxKind.Identifier && isIntrinsicJsxName((name).text)) { write("\""); emit(name); @@ -1154,6 +1226,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi /// these emit into an object literal property name, we don't need to be worried /// about keywords, just non-identifier characters function emitAttributeName(name: Identifier) { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack, name); + if (/[A-Za-z_]+[\w*]/.test(name.text)) { write("\""); emit(name); @@ -1166,6 +1240,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi /// Emit an name/value pair for an attribute (e.g. "x: 3") function emitJsxAttribute(node: JsxAttribute) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + emitAttributeName(node.name); write(": "); if (node.initializer) { @@ -1175,8 +1251,25 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi write("true"); } } + + function emitJsxSpreadAttribute(node: JsxSpreadAttribute) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + emit(node.expression); + } + function emitNonEmptyJsxText(node: JsxText) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + let text = getTextToEmit(node); + if (text !== undefined) { + write(", \""); + write(text); + write("\""); + } + } + function emitJsxElement(openingNode: JsxOpeningLikeElement, children?: JsxChild[]) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, openingNode); + let syntheticReactRef = createSynthesizedNode(SyntaxKind.Identifier); syntheticReactRef.text = 'React'; syntheticReactRef.parent = openingNode; @@ -1216,7 +1309,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi if (i > 0) { write(", "); } - emit((attrs[i]).expression); + visitNodeWithPushStackBehavior(attrs[i], emitJsxSpreadAttribute); } else { Debug.assert(attrs[i].kind === SyntaxKind.JsxAttribute); @@ -1230,7 +1323,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } write("{"); } - emitJsxAttribute(attrs[i]); + visitNodeWithPushStackBehavior(attrs[i], emitJsxAttribute); } } if (haveOpenedObjectLiteral) write("}"); @@ -1244,7 +1337,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi if (i > 0) { write(", "); } - emitJsxAttribute(attrs[i]); + + visitNodeWithPushStackBehavior(attrs[i], emitJsxAttribute); } write("}"); } @@ -1260,18 +1354,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi // Don't emit empty strings if (children[i].kind === SyntaxKind.JsxText) { - let text = getTextToEmit(children[i]); - if (text !== undefined) { - write(", \""); - write(text); - write("\""); - } + visitNodeWithPushStackBehavior(children[i], emitNonEmptyJsxText); } else { write(", "); emit(children[i]); } - } } @@ -1279,35 +1367,46 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi write(")"); // closes "React.createElement(" emitTrailingComments(openingNode); } - + if (node.kind === SyntaxKind.JsxElement) { + pushNode((node).openingElement); emitJsxElement((node).openingElement, (node).children); + popNode(); } else { Debug.assert(node.kind === SyntaxKind.JsxSelfClosingElement); emitJsxElement(node); } } - + function jsxEmitPreserve(node: JsxElement|JsxSelfClosingElement) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + function emitJsxAttribute(node: JsxAttribute) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + emit(node.name); write("="); emit(node.initializer); } function emitJsxSpreadAttribute(node: JsxSpreadAttribute) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + write("{..."); emit(node.expression); write("}"); } function emitAttributes(attribs: NodeArray) { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack); + for (let i = 0, n = attribs.length; i < n; i++) { if (i > 0) { write(" "); } - + + pushNode(attribs[i]); if (attribs[i].kind === SyntaxKind.JsxSpreadAttribute) { emitJsxSpreadAttribute(attribs[i]); } @@ -1315,10 +1414,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi Debug.assert(attribs[i].kind === SyntaxKind.JsxAttribute); emitJsxAttribute(attribs[i]); } + popNode(); } } function emitJsxOpeningOrSelfClosingElement(node: JsxOpeningElement|JsxSelfClosingElement) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + write("<"); emit(node.tagName); if (node.attributes.length > 0 || (node.kind === SyntaxKind.JsxSelfClosingElement)) { @@ -1336,19 +1438,27 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitJsxClosingElement(node: JsxClosingElement) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + write(""); } function emitJsxElement(node: JsxElement) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + + pushNode(node.openingElement); emitJsxOpeningOrSelfClosingElement(node.openingElement); + popNode(); for (var i = 0, n = node.children.length; i < n; i++) { emit(node.children[i]); } + pushNode(node.closingElement); emitJsxClosingElement(node.closingElement); + popNode(); } if (node.kind === SyntaxKind.JsxElement) { @@ -1363,62 +1473,78 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi // This function specifically handles numeric/string literals for enum and accessor 'identifiers'. // In a sense, it does not actually emit identifiers as much as it declares a name for a specific property. // For example, this is utilized when feeding in a result to Object.defineProperty. - function emitExpressionForPropertyName(node: DeclarationName) { - Debug.assert(node.kind !== SyntaxKind.BindingElement); + function emitExpressionForPropertyName(node: Declaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + + let name = node.name; + Debug.assert(name.kind !== SyntaxKind.BindingElement); - if (node.kind === SyntaxKind.StringLiteral) { - emitLiteral(node); + if (name.kind === SyntaxKind.StringLiteral) { + pushNode(name); + emitLiteral(name); + popNode(); } - else if (node.kind === SyntaxKind.ComputedPropertyName) { - // if this is a decorated computed property, we will need to capture the result - // of the property expression so that we can apply decorators later. This is to ensure - // we don't introduce unintended side effects: - // - // class C { - // [_a = x]() { } - // } - // - // The emit for the decorated computed property decorator is: - // - // Object.defineProperty(C.prototype, _a, __decorate([dec], C.prototype, _a, Object.getOwnPropertyDescriptor(C.prototype, _a))); - // - if (nodeIsDecorated(node.parent)) { - if (!computedPropertyNamesToGeneratedNames) { - computedPropertyNamesToGeneratedNames = []; - } - - let generatedName = computedPropertyNamesToGeneratedNames[getNodeId(node)]; - if (generatedName) { - // we have already generated a variable for this node, write that value instead. - write(generatedName); - return; - } - - generatedName = createAndRecordTempVariable(TempFlags.Auto).text; - computedPropertyNamesToGeneratedNames[getNodeId(node)] = generatedName; - write(generatedName); - write(" = "); - } - - emit((node).expression); + else if (name.kind === SyntaxKind.ComputedPropertyName) { + pushNode(name); + emitExpressionForComputedPropertyName(name); + popNode(); } else { write("\""); - if (node.kind === SyntaxKind.NumericLiteral) { - write((node).text); + if (name.kind === SyntaxKind.NumericLiteral) { + write((name).text); } else { - writeTextOfNode(currentSourceFile, node); + writeTextOfNode(currentSourceFile, name); } write("\""); } } + + function emitExpressionForComputedPropertyName(name: ComputedPropertyName) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, name); + + // if this is a decorated computed property, we will need to capture the result + // of the property expression so that we can apply decorators later. This is to ensure + // we don't introduce unintended side effects: + // + // class C { + // [_a = x]() { } + // } + // + // The emit for the decorated computed property decorator is: + // + // Object.defineProperty(C.prototype, _a, __decorate([dec], C.prototype, _a, Object.getOwnPropertyDescriptor(C.prototype, _a))); + // + let parentNode = nodeStack.getParent(); + if (parentNode && nodeIsDecorated(parentNode)) { + if (!computedPropertyNamesToGeneratedNames) { + computedPropertyNamesToGeneratedNames = []; + } + + let generatedName = computedPropertyNamesToGeneratedNames[getNodeId(name)]; + if (generatedName) { + // we have already generated a variable for this node, write that value instead. + write(generatedName); + return; + } + + generatedName = createAndRecordTempVariable(TempFlags.Auto).text; + computedPropertyNamesToGeneratedNames[getNodeId(name)] = generatedName; + write(generatedName); + write(" = "); + } + + emit((name).expression); + } function isExpressionIdentifier(node: Node): boolean { - let parent = node.parent; - switch (parent.kind) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + + let parentNode = nodeStack.getParent(); + switch (parentNode.kind) { case SyntaxKind.ArrayLiteralExpression: case SyntaxKind.BinaryExpression: case SyntaxKind.CallExpression: @@ -1464,16 +1590,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi case SyntaxKind.PropertyAssignment: case SyntaxKind.PropertyDeclaration: case SyntaxKind.VariableDeclaration: - return (parent).initializer === node; + return (parentNode).initializer === node; case SyntaxKind.PropertyAccessExpression: - return (parent).expression === node; + return (parentNode).expression === node; case SyntaxKind.ArrowFunction: case SyntaxKind.FunctionExpression: - return (parent).body === node; + return (parentNode).body === node; case SyntaxKind.ImportEqualsDeclaration: - return (parent).moduleReference === node; + return (parentNode).moduleReference === node; case SyntaxKind.QualifiedName: - return (parent).left === node; + return (parentNode).left === node; } return false; } @@ -1531,21 +1657,25 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function isNameOfNestedRedeclaration(node: Identifier) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (languageVersion < ScriptTarget.ES6) { - let parent = node.parent; - switch (parent.kind) { + let parentNode = nodeStack.getParent(); + switch (parentNode.kind) { case SyntaxKind.BindingElement: case SyntaxKind.ClassDeclaration: case SyntaxKind.EnumDeclaration: case SyntaxKind.VariableDeclaration: - return (parent).name === node && resolver.isNestedRedeclaration(parent); + return (parentNode).name === node && resolver.isNestedRedeclaration(parentNode); } } return false; } function emitIdentifier(node: Identifier) { - if (!node.parent) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + + if (nodeIsSynthesized(node) || !node.parent) { write(node.text); } else if (isExpressionIdentifier(node)) { @@ -1563,6 +1693,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitThis(node: Node) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.LexicalThis) { write("_this"); } @@ -1572,6 +1704,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitSuper(node: Node) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (languageVersion >= ScriptTarget.ES6) { write("super"); } @@ -1587,6 +1721,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitObjectBindingPattern(node: BindingPattern) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + write("{ "); let elements = node.elements; emitList(elements, 0, elements.length, /*multiLine*/ false, /*trailingComma*/ elements.hasTrailingComma); @@ -1594,6 +1730,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitArrayBindingPattern(node: BindingPattern) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + write("["); let elements = node.elements; emitList(elements, 0, elements.length, /*multiLine*/ false, /*trailingComma*/ elements.hasTrailingComma); @@ -1601,6 +1739,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitBindingElement(node: BindingElement) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (node.propertyName) { emit(node.propertyName); write(": "); @@ -1618,11 +1758,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitSpreadElementExpression(node: SpreadElementExpression) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + write("..."); emit((node).expression); } function emitYieldExpression(node: YieldExpression) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + write(tokenToString(SyntaxKind.YieldKeyword)); if (node.asteriskToken) { write("*"); @@ -1634,6 +1778,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitAwaitExpression(node: AwaitExpression) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + let needsParenthesis = needsParenthesisForAwaitExpressionAsYield(node); if (needsParenthesis) { write("("); @@ -1647,10 +1793,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function needsParenthesisForAwaitExpressionAsYield(node: AwaitExpression) { - if (node.parent.kind === SyntaxKind.BinaryExpression && !isAssignmentOperator((node.parent).operatorToken.kind)) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + + let parentNode = nodeStack.getParent(); + if (parentNode.kind === SyntaxKind.BinaryExpression && !isAssignmentOperator((parentNode).operatorToken.kind)) { return true; } - else if (node.parent.kind === SyntaxKind.ConditionalExpression && (node.parent).condition === node) { + else if (parentNode.kind === SyntaxKind.ConditionalExpression && (parentNode).condition === node) { return true; } @@ -1658,6 +1807,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function needsParenthesisForPropertyAccessOrInvocation(node: Expression) { + verifyStackBehavior(StackBehavior.Unspecified, node); + switch (node.kind) { case SyntaxKind.Identifier: case SyntaxKind.ArrayLiteralExpression: @@ -1673,6 +1824,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitListWithSpread(elements: Expression[], needsUniqueCopy: boolean, multiLine: boolean, trailingComma: boolean, useConcat: boolean) { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack); + let pos = 0; let group = 0; let length = elements.length; @@ -1719,10 +1872,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function isSpreadElementExpression(node: Node) { + verifyStackBehavior(StackBehavior.Unspecified, node); + return node.kind === SyntaxKind.SpreadElementExpression; } function emitArrayLiteral(node: ArrayLiteralExpression) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + let elements = node.elements; if (elements.length === 0) { write("[]"); @@ -1739,6 +1896,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitObjectLiteralBody(node: ObjectLiteralExpression, numElements: number): void { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (numElements === 0) { write("{}"); return; @@ -1779,6 +1938,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitDownlevelObjectLiteralWithComputedProperties(node: ObjectLiteralExpression, firstComputedPropertyIndex: number) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + let multiLine = (node.flags & NodeFlags.MultiLine) !== 0; let properties = node.properties; @@ -1811,15 +1972,18 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi if (property !== accessors.firstAccessor) { continue; } + pushNode(property); write("Object.defineProperty("); emit(tempVar); write(", "); emitStart(node.name); - emitExpressionForPropertyName(property.name); + emitExpressionForPropertyName(property); emitEnd(property.name); write(", {"); increaseIndent(); + popNode(); if (accessors.getAccessor) { + pushNode(accessors.getAccessor); writeLine(); emitLeadingComments(accessors.getAccessor); write("get: "); @@ -1829,8 +1993,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitEnd(accessors.getAccessor); emitTrailingComments(accessors.getAccessor); write(","); + popNode(); } if (accessors.setAccessor) { + pushNode(accessors.setAccessor); writeLine(); emitLeadingComments(accessors.setAccessor); write("set: "); @@ -1840,7 +2006,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitEnd(accessors.setAccessor); emitTrailingComments(accessors.setAccessor); write(","); + popNode(); } + pushNode(property); writeLine(); write("enumerable: true,"); writeLine(); @@ -1849,12 +2017,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi writeLine(); write("})"); emitEnd(property); + popNode(); } else { + pushNode(property); emitLeadingComments(property); emitStart(property.name); emit(tempVar); - emitMemberAccessForPropertyName(property.name); + emitMemberAccessForPropertyName(property); emitEnd(property.name); write(" = "); @@ -1871,6 +2041,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi else { Debug.fail("ObjectLiteralElement type not accounted for: " + property.kind); } + popNode(); } emitEnd(property); @@ -1898,6 +2069,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitObjectLiteral(node: ObjectLiteralExpression): void { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + let properties = node.properties; if (languageVersion < ScriptTarget.ES6) { @@ -1926,6 +2099,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function createBinaryExpression(left: Expression, operator: SyntaxKind, right: Expression, startsOnNewLine?: boolean): BinaryExpression { + verifyStackBehavior(StackBehavior.Unspecified); + let result = createSynthesizedNode(SyntaxKind.BinaryExpression, startsOnNewLine); result.operatorToken = createSynthesizedNode(operator); result.left = left; @@ -1935,6 +2110,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function createPropertyAccessExpression(expression: Expression, name: Identifier): PropertyAccessExpression { + verifyStackBehavior(StackBehavior.Unspecified); + let result = createSynthesizedNode(SyntaxKind.PropertyAccessExpression); result.expression = parenthesizeForAccess(expression); result.dotToken = createSynthesizedNode(SyntaxKind.DotToken); @@ -1944,6 +2121,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function createElementAccessExpression(expression: Expression, argumentExpression: Expression): ElementAccessExpression { + verifyStackBehavior(StackBehavior.Unspecified); + let result = createSynthesizedNode(SyntaxKind.ElementAccessExpression); result.expression = parenthesizeForAccess(expression); result.argumentExpression = argumentExpression; @@ -1952,6 +2131,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function parenthesizeForAccess(expr: Expression): LeftHandSideExpression { + verifyStackBehavior(StackBehavior.Unspecified); + // 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) { @@ -1978,12 +2159,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitComputedPropertyName(node: ComputedPropertyName) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + write("["); - emitExpressionForPropertyName(node); + emitExpressionForComputedPropertyName(node); write("]"); } function emitMethod(node: MethodDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (languageVersion >= ScriptTarget.ES6 && node.asteriskToken) { write("*"); } @@ -1996,6 +2181,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitPropertyAssignment(node: PropertyDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + emit(node.name); write(": "); // This is to ensure that we emit comment in the following case: @@ -2011,11 +2198,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi // Return true if identifier resolves to an exported member of a namespace function isNamespaceExportReference(node: Identifier) { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack, node); + let container = resolver.getReferencedExportContainer(node); return container && container.kind !== SyntaxKind.SourceFile; } function emitShorthandPropertyAssignment(node: ShorthandPropertyAssignment) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + // The name property of a short-hand property assignment is considered an expression position, so here // we manually emit the identifier to avoid rewriting. writeTextOfNode(currentSourceFile, node.name); @@ -2036,6 +2227,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function tryEmitConstantValue(node: PropertyAccessExpression | ElementAccessExpression): boolean { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + let constantValue = tryGetConstEnumValue(node); if (constantValue !== undefined) { write(constantValue.toString()); @@ -2062,6 +2255,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi // If the code is not indented, an optional valueToWriteWhenNotIndenting will be // emitted instead. function indentIfOnDifferentLines(parent: Node, node1: Node, node2: Node, valueToWriteWhenNotIndenting?: string): boolean { + verifyStackBehavior(StackBehavior.Unspecified); + let realNodesAreOnDifferentLines = !nodeIsSynthesized(parent) && !nodeEndIsOnSameLineAsNodeStart(node1, node2); // Always use a newline for synthesized code if the synthesizer desires it. @@ -2081,6 +2276,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitPropertyAccess(node: PropertyAccessExpression) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (tryEmitConstantValue(node)) { return; } @@ -2118,12 +2315,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitQualifiedName(node: QualifiedName) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + emit(node.left); write("."); emit(node.right); } function emitQualifiedNameAsExpression(node: QualifiedName, useFallback: boolean) { + verifyStackBehavior(StackBehavior.Unspecified, node); + if (node.left.kind === SyntaxKind.Identifier) { emitEntityNameAsExpression(node.left, useFallback); } @@ -2145,6 +2346,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitEntityNameAsExpression(node: EntityName, useFallback: boolean) { + verifyStackBehavior(StackBehavior.Unspecified, node); + switch (node.kind) { case SyntaxKind.Identifier: if (useFallback) { @@ -2163,6 +2366,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitIndexedAccess(node: ElementAccessExpression) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (tryEmitConstantValue(node)) { return; } @@ -2173,10 +2378,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function hasSpreadElement(elements: Expression[]) { + verifyStackBehavior(StackBehavior.Unspecified); + return forEach(elements, e => e.kind === SyntaxKind.SpreadElementExpression); } function skipParentheses(node: Expression): Expression { + verifyStackBehavior(StackBehavior.Unspecified, node); + while (node.kind === SyntaxKind.ParenthesizedExpression || node.kind === SyntaxKind.TypeAssertionExpression || node.kind === SyntaxKind.AsExpression) { node = (node).expression; } @@ -2184,6 +2393,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitCallTarget(node: Expression): Expression { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack, node); + if (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.ThisKeyword || node.kind === SyntaxKind.SuperKeyword) { emit(node); return node; @@ -2199,20 +2410,26 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitCallWithSpread(node: CallExpression) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + let target: Expression; let expr = skipParentheses(node.expression); if (expr.kind === SyntaxKind.PropertyAccessExpression) { // Target will be emitted as "this" argument + pushNode(expr); target = emitCallTarget((expr).expression); write("."); emit((expr).name); + popNode(); } else if (expr.kind === SyntaxKind.ElementAccessExpression) { // Target will be emitted as "this" argument - target = emitCallTarget((expr).expression); + pushNode(expr); + target = emitCallTarget((expr).expression); write("["); emit((expr).argumentExpression); write("]"); + popNode(); } else if (expr.kind === SyntaxKind.SuperKeyword) { target = expr; @@ -2225,7 +2442,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi if (target) { if (target.kind === SyntaxKind.SuperKeyword) { // Calls of form super(...) and super.foo(...) + pushNode(target); emitThis(target); + popNode(); } else { // Calls of form obj.foo(...) @@ -2242,22 +2461,29 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitCallExpression(node: CallExpression) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (languageVersion < ScriptTarget.ES6 && hasSpreadElement(node.arguments)) { emitCallWithSpread(node); return; } let superCall = false; - if (node.expression.kind === SyntaxKind.SuperKeyword) { - emitSuper(node.expression); + let expression = node.expression; + if (expression.kind === SyntaxKind.SuperKeyword) { + pushNode(expression); + emitSuper(expression); + popNode(); superCall = true; } else { - emit(node.expression); + emit(expression); superCall = node.expression.kind === SyntaxKind.PropertyAccessExpression && (node.expression).expression.kind === SyntaxKind.SuperKeyword; } if (superCall && languageVersion < ScriptTarget.ES6) { write(".call("); - emitThis(node.expression); + pushNode(expression); + emitThis(expression); + popNode(); if (node.arguments.length) { write(", "); emitCommaList(node.arguments); @@ -2272,6 +2498,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitNewExpression(node: NewExpression) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + write("new "); // Spread operator logic is supported in new expressions in ES5 using a combination @@ -2313,6 +2541,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitTaggedTemplateExpression(node: TaggedTemplateExpression): void { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (languageVersion >= ScriptTarget.ES6) { emit(node.tag); write(" "); @@ -2324,10 +2554,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitParenExpression(node: ParenthesizedExpression) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + // If the node is synthesized, it means the emitter put the parentheses there, // not the user. If we didn't want them, the emitter would not have put them // there. - if (!nodeIsSynthesized(node) && node.parent.kind !== SyntaxKind.ArrowFunction) { + let parentNode = nodeStack.getParent(); + if (!nodeIsSynthesized(node) && parentNode.kind !== SyntaxKind.ArrowFunction) { if (node.expression.kind === SyntaxKind.TypeAssertionExpression || node.expression.kind === SyntaxKind.AsExpression) { let operand = (node.expression).expression; @@ -2351,8 +2584,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi operand.kind !== SyntaxKind.DeleteExpression && operand.kind !== SyntaxKind.PostfixUnaryExpression && operand.kind !== SyntaxKind.NewExpression && - !(operand.kind === SyntaxKind.CallExpression && node.parent.kind === SyntaxKind.NewExpression) && - !(operand.kind === SyntaxKind.FunctionExpression && node.parent.kind === SyntaxKind.CallExpression)) { + !(operand.kind === SyntaxKind.CallExpression && parentNode.kind === SyntaxKind.NewExpression) && + !(operand.kind === SyntaxKind.FunctionExpression && parentNode.kind === SyntaxKind.CallExpression)) { emit(operand); return; } @@ -2365,40 +2598,51 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitDeleteExpression(node: DeleteExpression) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + write(tokenToString(SyntaxKind.DeleteKeyword)); write(" "); emit(node.expression); } function emitVoidExpression(node: VoidExpression) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + write(tokenToString(SyntaxKind.VoidKeyword)); write(" "); emit(node.expression); } function emitTypeOfExpression(node: TypeOfExpression) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + write(tokenToString(SyntaxKind.TypeOfKeyword)); write(" "); emit(node.expression); } function isNameOfExportedSourceLevelDeclarationInSystemExternalModule(node: Node): boolean { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack, node); + if (!isCurrentFileSystemExternalModule() || node.kind !== SyntaxKind.Identifier || nodeIsSynthesized(node)) { return false; } - + + const parentNode = nodeStack.getNode(); const isVariableDeclarationOrBindingElement = - node.parent && (node.parent.kind === SyntaxKind.VariableDeclaration || node.parent.kind === SyntaxKind.BindingElement); + parentNode && (parentNode.kind === SyntaxKind.VariableDeclaration || parentNode.kind === SyntaxKind.BindingElement); const targetDeclaration = isVariableDeclarationOrBindingElement - ? node.parent + ? parentNode : resolver.getReferencedValueDeclaration(node); - + return isSourceFileLevelDeclarationInSystemJsModule(targetDeclaration, /*isExported*/ true); } function emitPrefixUnaryExpression(node: PrefixUnaryExpression) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + const exportChanged = isNameOfExportedSourceLevelDeclarationInSystemExternalModule(node.operand); if (exportChanged) { @@ -2441,6 +2685,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitPostfixUnaryExpression(node: PostfixUnaryExpression) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + const exportChanged = isNameOfExportedSourceLevelDeclarationInSystemExternalModule(node.operand); if (exportChanged) { // export function returns the value that was passes as the second argument @@ -2467,6 +2713,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function shouldHoistDeclarationInSystemJsModule(node: Node): boolean { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + return isSourceFileLevelDeclarationInSystemJsModule(node, /*isExported*/ false); } @@ -2481,28 +2729,39 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi * we we emit variable statement 'var' should be dropped. */ function isSourceFileLevelDeclarationInSystemJsModule(node: Node, isExported: boolean): boolean { + verifyStackBehavior(StackBehavior.Unspecified); + if (!node || languageVersion >= ScriptTarget.ES6 || !isCurrentFileSystemExternalModule()) { return false; } - - let current: Node = node; - while (current) { + + // If `node` is not on top of the stack, then we get the combined node flags by + // walking parent references. This should be safe as we should have received this + // node from the checker, so we will be looking at the original source file. + // If `node` is not on top of the stack, then we can get the combined node flags + // by walking the node stack (preferred). + let navigable = node !== nodeStack.getNode() ? node : nodeStack; + let nav = navigable.createParentNavigator(); + do { + let current = nav.getNode(); if (current.kind === SyntaxKind.SourceFile) { - return !isExported || ((getCombinedNodeFlags(node) & NodeFlags.Export) !== 0); + let nodeFlags = getCombinedNodeFlags(navigable); + return !isExported || ((nodeFlags & NodeFlags.Export) !== 0); } else if (isFunctionLike(current) || current.kind === SyntaxKind.ModuleBlock) { return false; } - else { - current = current.parent; - } } + while (nav.moveToParent()); } function emitBinaryExpression(node: BinaryExpression) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (languageVersion < ScriptTarget.ES6 && node.operatorToken.kind === SyntaxKind.EqualsToken && (node.left.kind === SyntaxKind.ObjectLiteralExpression || node.left.kind === SyntaxKind.ArrayLiteralExpression)) { - emitDestructuring(node, node.parent.kind === SyntaxKind.ExpressionStatement); + let parentNode = nodeStack.getParent(); + emitDestructuring(node, parentNode.kind === SyntaxKind.ExpressionStatement); } else { const exportChanged = @@ -2533,6 +2792,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitConditionalExpression(node: ConditionalExpression) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + emit(node.condition); let indentedBeforeQuestion = indentIfOnDifferentLines(node, node.condition, node.questionToken, " "); write("?"); @@ -2560,6 +2821,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function isSingleLineEmptyBlock(node: Node) { + verifyStackBehavior(StackBehavior.Unspecified, node); + if (node && node.kind === SyntaxKind.Block) { let block = node; return block.statements.length === 0 && nodeEndIsOnSameLineAsNodeStart(block, block); @@ -2567,6 +2830,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitBlock(node: Block) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (isSingleLineEmptyBlock(node)) { emitToken(SyntaxKind.OpenBraceToken, node.pos); write(" "); @@ -2574,12 +2839,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi return; } + let parentNode = nodeStack.getParent(); emitToken(SyntaxKind.OpenBraceToken, node.pos); increaseIndent(); - scopeEmitStart(node.parent); + scopeEmitStart(parentNode); if (node.kind === SyntaxKind.ModuleBlock) { - Debug.assert(node.parent.kind === SyntaxKind.ModuleDeclaration); - emitCaptureThisForNodeIfNecessary(node.parent); + Debug.assert(parentNode.kind === SyntaxKind.ModuleDeclaration); + emitCaptureThisForNodeIfNecessary(parentNode); } emitLines(node.statements); if (node.kind === SyntaxKind.ModuleBlock) { @@ -2592,6 +2858,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitEmbeddedStatement(node: Node) { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack, node); + if (node.kind === SyntaxKind.Block) { write(" "); emit(node); @@ -2605,11 +2873,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitExpressionStatement(node: ExpressionStatement) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + emitParenthesizedIf(node.expression, /*parenthesized*/ node.expression.kind === SyntaxKind.ArrowFunction); write(";"); } function emitIfStatement(node: IfStatement) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + let endPos = emitToken(SyntaxKind.IfKeyword, node.pos); write(" "); endPos = emitToken(SyntaxKind.OpenParenToken, endPos); @@ -2630,6 +2902,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitDoStatement(node: DoStatement) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + write("do"); emitEmbeddedStatement(node.statement); if (node.statement.kind === SyntaxKind.Block) { @@ -2644,6 +2918,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitWhileStatement(node: WhileStatement) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + write("while ("); emit(node.expression); write(")"); @@ -2656,6 +2932,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi * in system modules where such variable declarations are hoisted. */ function tryEmitStartOfVariableDeclarationList(decl: VariableDeclarationList, startPos?: number): boolean { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, decl); + if (shouldHoistVariable(decl, /*checkIfSourceFileLevelDecl*/ true)) { // variables in variable declaration list were already hoisted return false; @@ -2693,6 +2971,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitVariableDeclarationListSkippingUninitializedEntries(list: VariableDeclarationList): boolean { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, list); + let started = false; for (let decl of list.declarations) { if (!decl.initializer) { @@ -2713,11 +2993,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitForStatement(node: ForStatement) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + let endPos = emitToken(SyntaxKind.ForKeyword, node.pos); write(" "); endPos = emitToken(SyntaxKind.OpenParenToken, endPos); if (node.initializer && node.initializer.kind === SyntaxKind.VariableDeclarationList) { let variableDeclarationList = node.initializer; + pushNode(variableDeclarationList); let startIsEmitted = tryEmitStartOfVariableDeclarationList(variableDeclarationList, endPos); if (startIsEmitted) { emitCommaList(variableDeclarationList.declarations); @@ -2725,6 +3008,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi else { emitVariableDeclarationListSkippingUninitializedEntries(variableDeclarationList); } + popNode(); } else if (node.initializer) { emit(node.initializer); @@ -2738,6 +3022,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitForInOrForOfStatement(node: ForInStatement | ForOfStatement) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (languageVersion < ScriptTarget.ES6 && node.kind === SyntaxKind.ForOfStatement) { return emitDownLevelForOfStatement(node); } @@ -2748,8 +3034,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi if (node.initializer.kind === SyntaxKind.VariableDeclarationList) { let variableDeclarationList = node.initializer; if (variableDeclarationList.declarations.length >= 1) { + pushNode(variableDeclarationList); tryEmitStartOfVariableDeclarationList(variableDeclarationList, endPos); emit(variableDeclarationList.declarations[0]); + popNode(); } } else { @@ -2768,6 +3056,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitDownLevelForOfStatement(node: ForOfStatement) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + // The following ES6 code: // // for (let v of expr) { } @@ -2859,11 +3149,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi write("var "); let variableDeclarationList = node.initializer; if (variableDeclarationList.declarations.length > 0) { + pushNode(variableDeclarationList); let declaration = variableDeclarationList.declarations[0]; if (isBindingPattern(declaration.name)) { // This works whether the declaration is a var, let, or const. // It will use rhsIterationValue _a[_i] as the initializer. + pushNode(declaration); emitDestructuring(declaration, /*isAssignmentExpressionStatement*/ false, rhsIterationValue); + popNode(); } else { // The following call does not include the initializer, so we have @@ -2872,6 +3165,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi write(" = "); emitNodeWithoutSourceMap(rhsIterationValue); } + popNode(); } else { // It's an empty declaration list. This can only happen in an error case, if the user wrote @@ -2888,7 +3182,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi if (node.initializer.kind === SyntaxKind.ArrayLiteralExpression || node.initializer.kind === SyntaxKind.ObjectLiteralExpression) { // This is a destructuring pattern, so call emitDestructuring instead of emit. Calling emit will not work, because it will cause // the BinaryExpression to be passed in instead of the expression statement, which will cause emitDestructuring to crash. + pushNode(assignmentExpression); emitDestructuring(assignmentExpression, /*isAssignmentExpressionStatement*/ true, /*value*/ undefined); + popNode(); } else { emitNodeWithCommentsAndWithoutSourcemap(assignmentExpression); @@ -2911,18 +3207,24 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitBreakOrContinueStatement(node: BreakOrContinueStatement) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + emitToken(node.kind === SyntaxKind.BreakStatement ? SyntaxKind.BreakKeyword : SyntaxKind.ContinueKeyword, node.pos); emitOptional(" ", node.label); write(";"); } function emitReturnStatement(node: ReturnStatement) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + emitToken(SyntaxKind.ReturnKeyword, node.pos); emitOptional(" ", node.expression); write(";"); } function emitWithStatement(node: WithStatement) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + write("with ("); emit(node.expression); write(")"); @@ -2930,16 +3232,22 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitSwitchStatement(node: SwitchStatement) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + let endPos = emitToken(SyntaxKind.SwitchKeyword, node.pos); write(" "); emitToken(SyntaxKind.OpenParenToken, endPos); emit(node.expression); endPos = emitToken(SyntaxKind.CloseParenToken, node.expression.end); write(" "); + pushNode(node.caseBlock); emitCaseBlock(node.caseBlock, endPos); + popNode(); } function emitCaseBlock(node: CaseBlock, startPos: number): void { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + emitToken(SyntaxKind.OpenBraceToken, startPos); increaseIndent(); emitLines(node.clauses); @@ -2964,6 +3272,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitCaseOrDefaultClause(node: CaseOrDefaultClause) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (node.kind === SyntaxKind.CaseClause) { write("case "); emit((node).expression); @@ -2985,12 +3295,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitThrowStatement(node: ThrowStatement) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + write("throw "); emit(node.expression); write(";"); } function emitTryStatement(node: TryStatement) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + write("try "); emit(node.tryBlock); emit(node.catchClause); @@ -3002,6 +3316,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitCatchClause(node: CatchClause) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + writeLine(); let endPos = emitToken(SyntaxKind.CatchKeyword, node.pos); write(" "); @@ -3009,36 +3325,35 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emit(node.variableDeclaration); emitToken(SyntaxKind.CloseParenToken, node.variableDeclaration ? node.variableDeclaration.end : endPos); write(" "); - emitBlock(node.block); + emitNodeWithoutSourceMap(node.block); } function emitDebuggerStatement(node: Node) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + emitToken(SyntaxKind.DebuggerKeyword, node.pos); write(";"); } function emitLabelledStatement(node: LabeledStatement) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + emit(node.label); write(": "); emit(node.statement); } - function getContainingModule(node: Node): ModuleDeclaration { - do { - node = node.parent; - } while (node && node.kind !== SyntaxKind.ModuleDeclaration); - return node; - } - - function emitContainingModuleName(node: Node) { - let container = getContainingModule(node); + function emitContainingModuleName() { + let container = findAncestorNode(isModuleDeclaration); write(container ? getGeneratedNameForNode(container) : "exports"); } function emitModuleMemberName(node: Declaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + emitStart(node.name); - if (getCombinedNodeFlags(node) & NodeFlags.Export) { - let container = getContainingModule(node); + if (getCombinedNodeFlags(nodeStack) & NodeFlags.Export) { + let container = findAncestorNode(isModuleDeclaration); if (container) { write(getGeneratedNameForNode(container)); write("."); @@ -3060,6 +3375,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitEs6ExportDefaultCompat(node: Node) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); if (node.parent.kind === SyntaxKind.SourceFile) { Debug.assert(!!(node.flags & NodeFlags.Default) || node.kind === SyntaxKind.ExportAssignment); // only allow export default at a source file level @@ -3080,12 +3396,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitExportMemberAssignment(node: FunctionLikeDeclaration | ClassDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (node.flags & NodeFlags.Export) { writeLine(); emitStart(node); // emit call to exporter only for top level nodes - if (compilerOptions.module === ModuleKind.System && node.parent === currentSourceFile) { + let parentNode = nodeStack.getParent(); + if (compilerOptions.module === ModuleKind.System && parentNode === currentSourceFile) { // emit export default as // export("default", ) write(`${exportFunctionForFile}("`); @@ -3121,6 +3440,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitExportMemberAssignments(name: Identifier) { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack, name); if (compilerOptions.module === ModuleKind.System) { return; } @@ -3129,9 +3449,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi for (let specifier of exportSpecifiers[name.text]) { writeLine(); emitStart(specifier.name); - emitContainingModuleName(specifier); + emitContainingModuleName(); write("."); + pushNode(specifier); emitNodeWithCommentsAndWithoutSourcemap(specifier.name); + popNode(); emitEnd(specifier.name); write(" = "); emitExpressionIdentifier(name); @@ -3141,6 +3463,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitExportSpecifierInSystemModule(specifier: ExportSpecifier): void { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, specifier); Debug.assert(compilerOptions.module === ModuleKind.System); writeLine(); @@ -3155,6 +3478,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitDestructuring(root: BinaryExpression | VariableDeclaration | ParameterDeclaration, isAssignmentExpressionStatement: boolean, value?: Expression) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, root); + let emitCount = 0; // An exported declaration is actually emitted as an assignment (to a property on the module object), so @@ -3163,7 +3488,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi // because actual variable declarations are hoisted let canDefineTempVariablesInPlace = false; if (root.kind === SyntaxKind.VariableDeclaration) { - let isExported = getCombinedNodeFlags(root) & NodeFlags.Export; + let isExported = getCombinedNodeFlags(nodeStack) & NodeFlags.Export; let isSourceLevelForSystemModuleKind = shouldHoistDeclarationInSystemJsModule(root); canDefineTempVariablesInPlace = !isExported && !isSourceLevelForSystemModuleKind; } @@ -3179,13 +3504,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitBindingElement(root, value); } - function emitAssignment(name: Identifier, value: Expression) { + function emitAssignment(name: Identifier, value: Expression, isTempVariable?: boolean) { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack, name); + if (emitCount++) { write(", "); } + let parentNode = nodeStack.getNode(); const isVariableDeclarationOrBindingElement = - name.parent && (name.parent.kind === SyntaxKind.VariableDeclaration || name.parent.kind === SyntaxKind.BindingElement); + !isTempVariable && (parentNode.kind === SyntaxKind.VariableDeclaration || parentNode.kind === SyntaxKind.BindingElement); let exportChanged = isNameOfExportedSourceLevelDeclarationInSystemExternalModule(name); @@ -3196,7 +3524,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } if (isVariableDeclarationOrBindingElement) { - emitModuleMemberName(name.parent); + emitModuleMemberName(parentNode); } else { emit(name); @@ -3204,25 +3532,29 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi write(" = "); emit(value); - + if (exportChanged) { write(")"); } } function ensureIdentifier(expr: Expression): Expression { + verifyStackBehavior(StackBehavior.Unspecified); + if (expr.kind !== SyntaxKind.Identifier) { let identifier = createTempVariable(TempFlags.Auto); if (!canDefineTempVariablesInPlace) { recordTempDeclaration(identifier); } - emitAssignment(identifier, expr); + emitAssignment(identifier, expr, /*isTempVariable*/ true); expr = identifier; } return expr; } function createDefaultValueCheck(value: Expression, defaultValue: Expression): Expression { + verifyStackBehavior(StackBehavior.Unspecified); + // The value expression will be evaluated twice, so for anything but a simple identifier // we need to generate a temporary variable value = ensureIdentifier(value); @@ -3235,6 +3567,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function createConditionalExpression(condition: Expression, whenTrue: Expression, whenFalse: Expression) { + verifyStackBehavior(StackBehavior.Unspecified); + let cond = createSynthesizedNode(SyntaxKind.ConditionalExpression); cond.condition = condition; cond.questionToken = createSynthesizedNode(SyntaxKind.QuestionToken); @@ -3245,12 +3579,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function createNumericLiteral(value: number) { + verifyStackBehavior(StackBehavior.Unspecified); + let node = createSynthesizedNode(SyntaxKind.NumericLiteral); node.text = "" + value; return node; } function createPropertyAccessForDestructuringProperty(object: Expression, propName: Identifier | LiteralExpression): Expression { + verifyStackBehavior(StackBehavior.Unspecified); + // We create a synthetic copy of the identifier in order to avoid the rewriting that might // otherwise occur when the identifier is emitted. let syntheticName = createSynthesizedNode(propName.kind); @@ -3262,6 +3600,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function createSliceCall(value: Expression, sliceIndex: number): CallExpression { + verifyStackBehavior(StackBehavior.Unspecified); + let call = createSynthesizedNode(SyntaxKind.CallExpression); let sliceIdentifier = createSynthesizedNode(SyntaxKind.Identifier); sliceIdentifier.text = "slice"; @@ -3272,27 +3612,38 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitObjectLiteralAssignment(target: ObjectLiteralExpression, value: Expression) { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack, target); + let properties = target.properties; if (properties.length !== 1) { // For anything but a single element destructuring we need to generate a temporary // to ensure value is evaluated exactly once. value = ensureIdentifier(value); } + + pushNode(target); for (let p of properties) { if (p.kind === SyntaxKind.PropertyAssignment || p.kind === SyntaxKind.ShorthandPropertyAssignment) { + pushNode(p); let propName = (p).name; emitDestructuringAssignment((p).initializer || propName, createPropertyAccessForDestructuringProperty(value, propName)); + popNode(); } } + popNode(); } function emitArrayLiteralAssignment(target: ArrayLiteralExpression, value: Expression) { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack, target); + let elements = target.elements; if (elements.length !== 1) { // For anything but a single element destructuring we need to generate a temporary // to ensure value is evaluated exactly once. value = ensureIdentifier(value); } + + pushNode(target); for (let i = 0; i < elements.length; i++) { let e = elements[i]; if (e.kind !== SyntaxKind.OmittedExpression) { @@ -3300,13 +3651,18 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitDestructuringAssignment(e, createElementAccessExpression(value, createNumericLiteral(i))); } else if (i === elements.length - 1) { + pushNode(e); emitDestructuringAssignment((e).expression, createSliceCall(value, i)); + popNode(); } } } + popNode(); } function emitDestructuringAssignment(target: Expression, value: Expression) { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack, target); + if (target.kind === SyntaxKind.BinaryExpression && (target).operatorToken.kind === SyntaxKind.EqualsToken) { value = createDefaultValueCheck(value, (target).right); target = (target).left; @@ -3322,9 +3678,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } } - function emitAssignmentExpression(root: BinaryExpression) { - let target = root.left; - let value = root.right; + function emitAssignmentExpression(node: BinaryExpression) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + + let target = node.left; + let value = node.right; if (isEmptyObjectLiteralOrArrayLiteral(target)) { emit(value); @@ -3333,20 +3691,23 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitDestructuringAssignment(target, value); } else { - if (root.parent.kind !== SyntaxKind.ParenthesizedExpression) { + let parentNode = nodeStack.getParent(); + if (parentNode.kind !== SyntaxKind.ParenthesizedExpression) { write("("); } value = ensureIdentifier(value); emitDestructuringAssignment(target, value); write(", "); emit(value); - if (root.parent.kind !== SyntaxKind.ParenthesizedExpression) { + if (parentNode.kind !== SyntaxKind.ParenthesizedExpression) { write(")"); } } } function emitBindingElement(target: BindingElement, value: Expression) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, target); + if (target.initializer) { // Combine value and initializer value = value ? createDefaultValueCheck(value, target.initializer) : target.initializer; @@ -3355,6 +3716,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi // Use 'void 0' in absence of value and initializer value = createVoidZero(); } + if (isBindingPattern(target.name)) { let pattern = target.name; let elements = pattern.elements; @@ -3363,8 +3725,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi // to ensure value is evaluated exactly once. value = ensureIdentifier(value); } + pushNode(pattern); for (let i = 0; i < elements.length; i++) { let element = elements[i]; + pushNode(element); if (pattern.kind === SyntaxKind.ObjectBindingPattern) { // Rewrite element to a declaration with an initializer that fetches property let propName = element.propertyName || element.name; @@ -3379,7 +3743,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitBindingElement(element, createSliceCall(value, i)); } } + popNode(); } + popNode(); } else { emitAssignment(target.name, value); @@ -3388,6 +3754,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitVariableDeclaration(node: VariableDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (isBindingPattern(node.name)) { if (languageVersion < ScriptTarget.ES6) { emitDestructuring(node, /*isAssignmentExpressionStatement*/ false); @@ -3412,14 +3780,17 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi (getCombinedFlagsForIdentifier(node.name) & NodeFlags.Let); // NOTE: default initialization should not be added to let bindings in for-in\for-of statements - if (isUninitializedLet && - node.parent.parent.kind !== SyntaxKind.ForInStatement && - node.parent.parent.kind !== SyntaxKind.ForOfStatement) { - initializer = createVoidZero(); + if (isUninitializedLet) { + let grandparent = nodeStack.getGrandparent(); + if (grandparent.kind !== SyntaxKind.ForInStatement && + grandparent.kind !== SyntaxKind.ForOfStatement) { + initializer = createVoidZero(); + } } } let exportChanged = isNameOfExportedSourceLevelDeclarationInSystemExternalModule(node.name); + visitNodeWithPushStackBehavior if (exportChanged) { write(`${exportFunctionForFile}("`); @@ -3437,6 +3808,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitExportVariableAssignments(node: VariableDeclaration | BindingElement) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (node.kind === SyntaxKind.OmittedExpression) { return; } @@ -3445,54 +3818,82 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitExportMemberAssignments(name); } else if (isBindingPattern(name)) { - forEach((name).elements, emitExportVariableAssignments); + forEach((name).elements, emitExportVariableAssignmentsOfChild); } } + + function emitExportVariableAssignmentsOfChild(node: VariableDeclaration | BindingElement) { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack, node); + pushNode(node); + emitExportVariableAssignments(node); + popNode(); + } - function getCombinedFlagsForIdentifier(node: Identifier): NodeFlags { - if (!node.parent || (node.parent.kind !== SyntaxKind.VariableDeclaration && node.parent.kind !== SyntaxKind.BindingElement)) { + function getCombinedFlagsForIdentifier(name: Identifier): NodeFlags { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack, name); + + let parentNode = nodeStack.getNode(); + if (!parentNode || (parentNode.kind !== SyntaxKind.VariableDeclaration && parentNode.kind !== SyntaxKind.BindingElement)) { return 0; } - return getCombinedNodeFlags(node.parent); + return getCombinedNodeFlags(nodeStack); } function isES6ExportedDeclaration(node: Node) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + return !!(node.flags & NodeFlags.Export) && languageVersion >= ScriptTarget.ES6 && - node.parent.kind === SyntaxKind.SourceFile; + nodeStack.getParent().kind === SyntaxKind.SourceFile; } function emitVariableStatement(node: VariableStatement) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + + let declarationList = node.declarationList; let startIsEmitted = false; - + if (node.flags & NodeFlags.Export) { if (isES6ExportedDeclaration(node)) { // Exported ES6 module member write("export "); - startIsEmitted = tryEmitStartOfVariableDeclarationList(node.declarationList); + pushNode(declarationList); + startIsEmitted = tryEmitStartOfVariableDeclarationList(declarationList); + popNode(); } } else { - startIsEmitted = tryEmitStartOfVariableDeclarationList(node.declarationList); + pushNode(declarationList); + startIsEmitted = tryEmitStartOfVariableDeclarationList(declarationList); + popNode(); } if (startIsEmitted) { - emitCommaList(node.declarationList.declarations); + pushNode(declarationList); + emitCommaList(declarationList.declarations); + popNode(); write(";"); } else { - let atLeastOneItem = emitVariableDeclarationListSkippingUninitializedEntries(node.declarationList); + pushNode(declarationList); + let atLeastOneItem = emitVariableDeclarationListSkippingUninitializedEntries(declarationList); + popNode(); if (atLeastOneItem) { write(";"); } } - if (languageVersion < ScriptTarget.ES6 && node.parent === currentSourceFile) { - forEach(node.declarationList.declarations, emitExportVariableAssignments); + + if (languageVersion < ScriptTarget.ES6 && nodeStack.getParent() === currentSourceFile) { + pushNode(declarationList); + forEach(declarationList.declarations, emitExportVariableAssignmentsOfChild); + popNode(); } } function shouldEmitLeadingAndTrailingCommentsForVariableStatement(node: VariableStatement) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + // If we're not exporting the variables, there's nothing special here. // Always emit comments for these nodes. if (!(node.flags & NodeFlags.Export)) { @@ -3515,6 +3916,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitParameter(node: ParameterDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (languageVersion < ScriptTarget.ES6) { if (isBindingPattern(node.name)) { let name = createTempVariable(TempFlags.Auto); @@ -3538,6 +3941,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitDefaultValueAssignments(node: FunctionLikeDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (languageVersion < ScriptTarget.ES6) { let tempIndex = 0; forEach(node.parameters, parameter => { @@ -3547,6 +3952,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi return; } + pushNode(parameter); let { name: paramName, initializer } = parameter; if (isBindingPattern(paramName)) { // In cases where a binding pattern is simply '[]' or '{}', @@ -3585,11 +3991,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitEnd(parameter); write("; }"); } + popNode(); }); } } function emitRestParameter(node: FunctionLikeDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (languageVersion < ScriptTarget.ES6 && hasRestParameter(node)) { let restIndex = node.parameters.length - 1; let restParam = node.parameters[restIndex]; @@ -3598,7 +4007,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi if (isBindingPattern(restParam.name)) { return; } - + + pushNode(restParam); let tempName = createTempVariable(TempFlags._i).text; writeLine(); emitLeadingComments(restParam); @@ -3631,20 +4041,27 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi decreaseIndent(); writeLine(); write("}"); + popNode(); } } function emitAccessor(node: AccessorDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + write(node.kind === SyntaxKind.GetAccessor ? "get " : "set "); emit(node.name); emitSignatureAndBody(node); } function shouldEmitAsArrowFunction(node: FunctionLikeDeclaration): boolean { + verifyStackBehavior(StackBehavior.Unspecified, node); + return node.kind === SyntaxKind.ArrowFunction && languageVersion >= ScriptTarget.ES6; } - + function emitDeclarationName(node: Declaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (node.name) { emitNodeWithCommentsAndWithoutSourcemap(node.name); } @@ -3654,6 +4071,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function shouldEmitFunctionName(node: FunctionLikeDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (node.kind === SyntaxKind.FunctionExpression) { // Emit name if one is present return !!node.name; @@ -3665,8 +4084,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitFunctionDeclaration(node: FunctionLikeDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (nodeIsMissing(node.body)) { - return emitOnlyPinnedOrTripleSlashComments(node); + emitOnlyPinnedOrTripleSlashComments(node); + return; } // TODO (yuisu) : we should not have special cases to condition emitting comments @@ -3713,7 +4135,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } emitSignatureAndBody(node); - if (languageVersion < ScriptTarget.ES6 && node.kind === SyntaxKind.FunctionDeclaration && node.parent === currentSourceFile && node.name) { + if (languageVersion < ScriptTarget.ES6 && node.kind === SyntaxKind.FunctionDeclaration && nodeStack.getParent() === currentSourceFile && node.name) { emitExportMemberAssignments((node).name); } @@ -3724,6 +4146,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitCaptureThisForNodeIfNecessary(node: Node): void { + verifyStackBehavior(StackBehavior.Unspecified); + if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.CaptureThis) { writeLine(); emitStart(node); @@ -3733,6 +4157,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitSignatureParameters(node: FunctionLikeDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + increaseIndent(); write("("); if (node) { @@ -3745,6 +4171,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitSignatureParametersForArrow(node: FunctionLikeDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + // Check whether the parameter list needs parentheses and preserve no-parenthesis if (node.parameters.length === 1 && node.pos === node.parameters[0].pos) { emit(node.parameters[0]); @@ -3755,6 +4183,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitAsyncFunctionBodyForES6(node: FunctionLikeDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + let promiseConstructor = getEntityNameFromTypeNode(node.type); let isArrowFunction = node.kind === SyntaxKind.ArrowFunction; let hasLexicalArguments = (resolver.getNodeCheckFlags(node) & NodeCheckFlags.CaptureArguments) !== 0; @@ -3879,6 +4309,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitFunctionBody(node: FunctionLikeDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (!node.body) { // There can be no body when there are parse errors. Just emit an empty block // in that case. @@ -3895,6 +4327,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitSignatureAndBody(node: FunctionLikeDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + let saveTempFlags = tempFlags; let saveTempVariables = tempVariables; let saveTempParameters = tempParameters; @@ -3930,12 +4364,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi // Returns true if any preamble code was emitted. function emitFunctionBodyPreamble(node: FunctionLikeDeclaration): void { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + emitCaptureThisForNodeIfNecessary(node); emitDefaultValueAssignments(node); emitRestParameter(node); } function emitExpressionFunctionBody(node: FunctionLikeDeclaration, body: Expression) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (languageVersion < ScriptTarget.ES6 || node.flags & NodeFlags.Async) { emitDownLevelExpressionFunctionBody(node, body); return; @@ -3957,6 +4395,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitDownLevelExpressionFunctionBody(node: FunctionLikeDeclaration, body: Expression) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + write(" {"); scopeEmitStart(node); @@ -3982,11 +4422,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi else { increaseIndent(); writeLine(); + pushNode(node.body); emitLeadingComments(node.body); + popNode(); write("return "); emit(body); write(";"); + pushNode(node.body); emitTrailingComments(node.body); + popNode(); emitTempDeclarations(/*newLine*/ true); decreaseIndent(); @@ -4001,6 +4445,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitBlockFunctionBody(node: FunctionLikeDeclaration, body: Block) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + write(" {"); scopeEmitStart(node); @@ -4015,8 +4461,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitFunctionBodyPreamble(node); decreaseIndent(); + pushNode(body); let preambleEmitted = writer.getTextPos() !== initialTextPos; - if (!preambleEmitted && nodeEndIsOnSameLineAsNodeStart(body, body)) { for (let statement of body.statements) { write(" "); @@ -4038,9 +4484,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitToken(SyntaxKind.CloseBraceToken, body.statements.end); scopeEmitEnd(); + popNode(); } function findInitialSuperCall(ctor: ConstructorDeclaration): ExpressionStatement { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, ctor); + if (ctor.body) { let statement = (ctor.body).statements[0]; if (statement && statement.kind === SyntaxKind.ExpressionStatement) { @@ -4056,6 +4505,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitParameterPropertyAssignments(node: ConstructorDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + forEach(node.parameters, param => { if (param.flags & NodeFlags.AccessibilityModifier) { writeLine(); @@ -4072,7 +4523,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi }); } - function emitMemberAccessForPropertyName(memberName: DeclarationName) { + function emitMemberAccessForPropertyName(member: Declaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, member); + + let memberName = member.name; // This does not emit source map because it is emitted by caller as caller // is aware how the property name changes to the property access // eg. public x = 10; becomes this.x and static x = 10 becomes className.x @@ -4082,7 +4536,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi write("]"); } else if (memberName.kind === SyntaxKind.ComputedPropertyName) { - emitComputedPropertyName(memberName); + write("["); + pushNode(memberName); + emitExpressionForComputedPropertyName(memberName); + popNode(); + write("]"); } else { write("."); @@ -4091,6 +4549,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function getInitializedProperties(node: ClassLikeDeclaration, isStatic: boolean) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + let properties: PropertyDeclaration[] = []; for (let member of node.members) { if (member.kind === SyntaxKind.PropertyDeclaration && isStatic === ((member.flags & NodeFlags.Static) !== 0) && (member).initializer) { @@ -4102,14 +4562,20 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitPropertyDeclarations(node: ClassLikeDeclaration, properties: PropertyDeclaration[]) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + for (let property of properties) { emitPropertyDeclaration(node, property); } } function emitPropertyDeclaration(node: ClassLikeDeclaration, property: PropertyDeclaration, receiver?: Identifier, isExpression?: boolean) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + writeLine(); + pushNode(property); emitLeadingComments(property); + popNode(); emitStart(property); emitStart(property.name); if (receiver) { @@ -4123,7 +4589,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi write("this"); } } - emitMemberAccessForPropertyName(property.name); + pushNode(property); + emitMemberAccessForPropertyName(property); emitEnd(property.name); write(" = "); emit(property.initializer); @@ -4133,9 +4600,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitEnd(property); emitTrailingComments(property); + popNode(); } function emitMemberFunctionsForES5AndLower(node: ClassLikeDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + forEach(node.members, member => { if (member.kind === SyntaxKind.SemicolonClassElement) { writeLine(); @@ -4143,21 +4613,28 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } else if (member.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.MethodSignature) { if (!(member).body) { - return emitOnlyPinnedOrTripleSlashComments(member); + pushNode(member); + emitOnlyPinnedOrTripleSlashComments(member); + popNode(); + return; } writeLine(); + pushNode(member); emitLeadingComments(member); + popNode(); emitStart(member); emitStart((member).name); emitClassMemberPrefix(node, member); - emitMemberAccessForPropertyName((member).name); + pushNode(member); + emitMemberAccessForPropertyName(member); emitEnd((member).name); write(" = "); emitFunctionDeclaration(member); emitEnd(member); write(";"); emitTrailingComments(member); + popNode(); } else if (member.kind === SyntaxKind.GetAccessor || member.kind === SyntaxKind.SetAccessor) { let accessors = getAllAccessorDeclarations(node.members, member); @@ -4168,11 +4645,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitStart((member).name); emitClassMemberPrefix(node, member); write(", "); - emitExpressionForPropertyName((member).name); + pushNode(member); + emitExpressionForPropertyName(member); + popNode(); emitEnd((member).name); write(", {"); increaseIndent(); if (accessors.getAccessor) { + pushNode(accessors.getAccessor); writeLine(); emitLeadingComments(accessors.getAccessor); write("get: "); @@ -4182,8 +4662,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitEnd(accessors.getAccessor); emitTrailingComments(accessors.getAccessor); write(","); + popNode(); } if (accessors.setAccessor) { + pushNode(accessors.setAccessor); writeLine(); emitLeadingComments(accessors.setAccessor); write("set: "); @@ -4193,6 +4675,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitEnd(accessors.setAccessor); emitTrailingComments(accessors.setAccessor); write(","); + popNode(); } writeLine(); write("enumerable: true,"); @@ -4208,7 +4691,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitMemberFunctionsForES6AndHigher(node: ClassLikeDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + for (let member of node.members) { + pushNode(member); if ((member.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.MethodSignature) && !(member).body) { emitOnlyPinnedOrTripleSlashComments(member); } @@ -4240,10 +4726,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi writeLine(); write(";"); } + popNode(); } } function emitConstructor(node: ClassLikeDeclaration, baseTypeElement: ExpressionWithTypeArguments) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + let saveTempFlags = tempFlags; let saveTempVariables = tempVariables; let saveTempParameters = tempParameters; @@ -4259,6 +4748,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitConstructorWorker(node: ClassLikeDeclaration, baseTypeElement: ExpressionWithTypeArguments) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + // Check if we have property assignment inside class declaration. // If there is 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 @@ -4266,6 +4757,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi // Emit the constructor overload pinned comments forEach(node.members, member => { + pushNode(member); if (member.kind === SyntaxKind.Constructor && !(member).body) { emitOnlyPinnedOrTripleSlashComments(member); } @@ -4273,6 +4765,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi if (member.kind === SyntaxKind.PropertyDeclaration && (member).initializer && (member.flags & NodeFlags.Static) === 0) { hasInstancePropertyWithInitializer = true; } + popNode(); }); let ctor = getFirstConstructorWithBody(node); @@ -4284,19 +4777,30 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } if (ctor) { + pushNode(ctor); emitLeadingComments(ctor); + popNode(); } emitStart(ctor || node); if (languageVersion < ScriptTarget.ES6) { write("function "); emitDeclarationName(node); - emitSignatureParameters(ctor); + if (ctor) { + pushNode(ctor); + emitSignatureParameters(ctor); + popNode(); + } + else { + write("()"); + } } else { write("constructor"); if (ctor) { + pushNode(ctor); emitSignatureParameters(ctor); + popNode(); } else { // Based on EcmaScript6 section 14.5.14: Runtime Semantics: ClassDefinitionEvaluation. @@ -4322,22 +4826,30 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi if (ctor) { // Emit all the directive prologues (like "use strict"). These have to come before // any other preamble code we write (like parameter initializers). + pushNode(ctor); + pushNode(ctor.body); startIndex = emitDirectivePrologues(ctor.body.statements, /*startWithNewLine*/ true); emitDetachedComments(ctor.body.statements); + popNode(); + popNode(); } emitCaptureThisForNodeIfNecessary(node); let superCall: ExpressionStatement; if (ctor) { + pushNode(ctor); emitDefaultValueAssignments(ctor); emitRestParameter(ctor); if (baseTypeElement) { superCall = findInitialSuperCall(ctor); if (superCall) { writeLine(); + pushNode(ctor.body); emit(superCall); + popNode(); } } emitParameterPropertyAssignments(ctor); + popNode(); } else { if (baseTypeElement) { @@ -4358,7 +4870,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi if (superCall) { statements = statements.slice(1); } + pushNode(ctor); emitLinesStartingAt(statements, startIndex); + popNode(); } emitTempDeclarations(/*newLine*/ true); writeLine(); @@ -4370,19 +4884,27 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi scopeEmitEnd(); emitEnd(ctor || node); if (ctor) { + pushNode(ctor); emitTrailingComments(ctor); + popNode(); } } function emitClassExpression(node: ClassExpression) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + return emitClassLikeDeclaration(node); } function emitClassDeclaration(node: ClassDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + return emitClassLikeDeclaration(node); } function emitClassLikeDeclaration(node: ClassLikeDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (languageVersion < ScriptTarget.ES6) { emitClassLikeDeclarationBelowES6(node); } @@ -4392,6 +4914,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitClassLikeDeclarationForES6AndHigher(node: ClassLikeDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + let thisNodeIsDecorated = nodeIsDecorated(node); if (node.kind === SyntaxKind.ClassDeclaration) { if (thisNodeIsDecorated) { @@ -4497,7 +5021,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi let baseTypeNode = getClassExtendsHeritageClauseElement(node); if (baseTypeNode) { write(" extends "); + pushNode(baseTypeNode); emit(baseTypeNode.expression); + popNode(); } write(" {"); @@ -4569,6 +5095,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitClassLikeDeclarationBelowES6(node: ClassLikeDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (node.kind === SyntaxKind.ClassDeclaration) { // source file level classes in system modules are hoisted so 'var's for them are already defined if (!shouldHoistDeclarationInSystemJsModule(node)) { @@ -4626,7 +5154,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitStart(node); write(")("); if (baseTypeNode) { + pushNode(baseTypeNode); emit(baseTypeNode.expression); + popNode(); } write(")"); if (node.kind === SyntaxKind.ClassDeclaration) { @@ -4638,12 +5168,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitExportMemberAssignment(node); } - if (languageVersion < ScriptTarget.ES6 && node.parent === currentSourceFile && node.name) { + if (languageVersion < ScriptTarget.ES6 && nodeStack.getParent() === currentSourceFile && node.name) { emitExportMemberAssignments(node.name); } } function emitClassMemberPrefix(node: ClassLikeDeclaration, member: Node) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + emitDeclarationName(node); if (!(member.flags & NodeFlags.Static)) { write(".prototype"); @@ -4651,12 +5183,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitDecoratorsOfClass(node: ClassLikeDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + emitDecoratorsOfMembers(node, /*staticFlag*/ 0); emitDecoratorsOfMembers(node, NodeFlags.Static); emitDecoratorsOfConstructor(node); } function emitDecoratorsOfConstructor(node: ClassLikeDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + let decorators = node.decorators; let constructor = getFirstConstructorWithBody(node); let hasDecoratedParameters = constructor && forEach(constructor.parameters, nodeIsDecorated); @@ -4686,12 +5222,23 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi let decoratorCount = decorators ? decorators.length : 0; let argumentsWritten = emitList(decorators, 0, decoratorCount, /*multiLine*/ true, /*trailingComma*/ false, /*leadingComma*/ false, /*noTrailingNewLine*/ true, decorator => { + pushNode(decorator); emitStart(decorator); emit(decorator.expression); emitEnd(decorator); + popNode(); }); + if (constructor) { + pushNode(constructor); + } + argumentsWritten += emitDecoratorsOfParameters(constructor, /*leadingComma*/ argumentsWritten > 0); + + if (constructor) { + popNode(); + } + emitSerializedTypeMetadata(node, /*leadingComma*/ argumentsWritten >= 0); decreaseIndent(); @@ -4704,6 +5251,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitDecoratorsOfMembers(node: ClassLikeDeclaration, staticFlag: NodeFlags) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + for (let member of node.members) { // only emit members in the correct group if ((member.flags & NodeFlags.Static) !== staticFlag) { @@ -4711,7 +5260,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } // skip members that cannot be decorated (such as the constructor) - if (!nodeCanBeDecorated(member)) { + if (member.kind !== SyntaxKind.PropertyDeclaration + && ((member.kind !== SyntaxKind.GetAccessor + && member.kind !== SyntaxKind.SetAccessor + && member.kind !== SyntaxKind.MethodDeclaration) + || !(member).body)) { continue; } @@ -4719,7 +5272,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi if (!nodeOrChildIsDecorated(member)) { continue; } - + // skip an accessor declaration if it is not the first accessor let decorators: NodeArray; let functionLikeMember: FunctionLikeDeclaration; @@ -4787,7 +5340,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitStart(member.name); emitClassMemberPrefix(node, member); write(", "); - emitExpressionForPropertyName(member.name); + pushNode(member); + emitExpressionForPropertyName(member); + popNode(); emitEnd(member.name); write(","); increaseIndent(); @@ -4798,15 +5353,27 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi increaseIndent(); writeLine(); + pushNode(member); let decoratorCount = decorators ? decorators.length : 0; let argumentsWritten = emitList(decorators, 0, decoratorCount, /*multiLine*/ true, /*trailingComma*/ false, /*leadingComma*/ false, /*noTrailingNewLine*/ true, decorator => { + pushNode(decorator); emitStart(decorator); emit(decorator.expression); emitEnd(decorator); + popNode(); }); - - argumentsWritten += emitDecoratorsOfParameters(functionLikeMember, argumentsWritten > 0); + popNode(); + + + if (functionLikeMember) { + pushNode(functionLikeMember); + argumentsWritten += emitDecoratorsOfParameters(functionLikeMember, argumentsWritten > 0); + popNode(); + } + + pushNode(member); emitSerializedTypeMetadata(member, argumentsWritten > 0); + popNode(); decreaseIndent(); writeLine(); @@ -4814,7 +5381,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitStart(member.name); emitClassMemberPrefix(node, member); write(", "); - emitExpressionForPropertyName(member.name); + pushNode(member); + emitExpressionForPropertyName(member); + popNode(); emitEnd(member.name); if (member.kind !== SyntaxKind.PropertyDeclaration) { @@ -4822,7 +5391,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitStart(member.name); emitClassMemberPrefix(node, member); write(", "); - emitExpressionForPropertyName(member.name); + pushNode(member); + emitExpressionForPropertyName(member); + popNode(); emitEnd(member.name); write("))"); decreaseIndent(); @@ -4835,6 +5406,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitDecoratorsOfParameters(node: FunctionLikeDeclaration, leadingComma: boolean): number { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + let argumentsWritten = 0; if (node) { let parameterIndex = 0; @@ -4842,11 +5415,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi if (nodeIsDecorated(parameter)) { let decorators = parameter.decorators; argumentsWritten += emitList(decorators, 0, decorators.length, /*multiLine*/ true, /*trailingComma*/ false, /*leadingComma*/ leadingComma, /*noTrailingNewLine*/ true, decorator => { + pushNode(decorator); emitStart(decorator); write(`__param(${parameterIndex}, `); emit(decorator.expression); write(")"); emitEnd(decorator); + popNode(); }); leadingComma = true; } @@ -4857,6 +5432,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function shouldEmitTypeMetadata(node: Declaration): boolean { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + // This method determines whether to emit the "design:type" metadata based on the node's kind. // The caller should have already tested whether the node has decorators and whether the emitDecoratorMetadata // compiler option is set. @@ -4872,6 +5449,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function shouldEmitReturnTypeMetadata(node: Declaration): boolean { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + // This method determines whether to emit the "design:returntype" metadata based on the node's kind. // The caller should have already tested whether the node has decorators and whether the emitDecoratorMetadata // compiler option is set. @@ -4883,6 +5462,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function shouldEmitParamTypesMetadata(node: Declaration): boolean { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + // This method determines whether to emit the "design:paramtypes" metadata based on the node's kind. // The caller should have already tested whether the node has decorators and whether the emitDecoratorMetadata // compiler option is set. @@ -4897,6 +5478,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi /** Serializes the type of a declaration to an appropriate JS constructor value. Used by the __metadata decorator for a class member. */ function emitSerializedTypeOfNode(node: Node) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + // serialization of the type of a declaration uses the following rules: // // * The serialized type of a ClassDeclaration is "Function" @@ -4937,8 +5520,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi write("void 0"); } - + function emitSerializedTypeNode(node: TypeNode) { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack, node); + if (node) { switch (node.kind) { @@ -4947,7 +5532,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi return; case SyntaxKind.ParenthesizedType: + pushNode(node); emitSerializedTypeNode((node).type); + popNode(); return; case SyntaxKind.FunctionType: @@ -4979,7 +5566,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi return; case SyntaxKind.TypeReference: + pushNode(node); emitSerializedTypeReferenceNode(node); + popNode(); return; case SyntaxKind.TypeQuery: @@ -4999,6 +5588,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi /** Serializes a TypeReferenceNode to an appropriate JS constructor value. Used by the __metadata decorator. */ function emitSerializedTypeReferenceNode(node: TypeReferenceNode) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + let location: Node = node.parent; while (isDeclaration(location) || isTypeNode(location)) { location = location.parent; @@ -5066,6 +5657,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi /** Serializes the parameter types of a function or the constructor of a class. Used by the __metadata decorator for a method or set accessor. */ function emitSerializedParameterTypesOfNode(node: Node) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + // serialization of parameter types uses the following rules: // // * If the declaration is a class, the parameters of the first constructor with a body are used. @@ -5089,8 +5682,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi if (i > 0) { write(", "); } - - if (parameters[i].dotDotDotToken) { + + let parameter = parameters[i]; + if (parameter.dotDotDotToken) { let parameterType = parameters[i].type; if (parameterType.kind === SyntaxKind.ArrayType) { parameterType = (parameterType).elementType; @@ -5102,10 +5696,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi parameterType = undefined; } - emitSerializedTypeNode(parameterType); + visitNodeWithPushStackBehavior(parameterType, emitSerializedTypeNode); } else { - emitSerializedTypeOfNode(parameters[i]); + visitNodeWithPushStackBehavior(parameters[i], emitSerializedTypeOfNode); } } } @@ -5115,6 +5709,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi /** Serializes the return type of function. Used by the __metadata decorator for a method. */ function emitSerializedReturnTypeOfNode(node: Node): string | string[] { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (node && isFunctionLike(node) && (node).type) { emitSerializedTypeNode((node).type); return; @@ -5125,6 +5721,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi function emitSerializedTypeMetadata(node: Declaration, writeComma: boolean): number { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + // This method emits the serialized type metadata for a decorator target. // The caller should have already tested whether the node has decorators. let argumentsWritten = 0; @@ -5166,15 +5764,21 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitInterfaceDeclaration(node: InterfaceDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + emitOnlyPinnedOrTripleSlashComments(node); } function shouldEmitEnumDeclaration(node: EnumDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + let isConstEnum = isConst(node); return !isConstEnum || compilerOptions.preserveConstEnums || compilerOptions.isolatedModules; } function emitEnumDeclaration(node: EnumDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + // const enums are completely erased during compilation. if (!shouldEmitEnumDeclaration(node)) { return; @@ -5224,7 +5828,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitEnd(node); write(";"); } - if (languageVersion < ScriptTarget.ES6 && node.parent === currentSourceFile) { + if (languageVersion < ScriptTarget.ES6 && nodeStack.getParent() === currentSourceFile) { if (compilerOptions.module === ModuleKind.System && (node.flags & NodeFlags.Export)) { // write the call to exporter for enum writeLine(); @@ -5239,22 +5843,26 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitEnumMember(node: EnumMember) { - let enumParent = node.parent; + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + + let enumParent = nodeStack.getParent(); emitStart(node); write(getGeneratedNameForNode(enumParent)); write("["); write(getGeneratedNameForNode(enumParent)); write("["); - emitExpressionForPropertyName(node.name); + emitExpressionForPropertyName(node); write("] = "); writeEnumMemberDeclarationValue(node); write("] = "); - emitExpressionForPropertyName(node.name); + emitExpressionForPropertyName(node); emitEnd(node); write(";"); } function writeEnumMemberDeclarationValue(member: EnumMember) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, member); + let value = resolver.getConstantValue(member); if (value !== undefined) { write(value.toString()); @@ -5269,6 +5877,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function getInnerMostModuleDeclarationFromDottedModule(moduleDeclaration: ModuleDeclaration): ModuleDeclaration { + verifyStackBehavior(StackBehavior.Unspecified); + if (moduleDeclaration.body.kind === SyntaxKind.ModuleDeclaration) { let recursiveInnerModule = getInnerMostModuleDeclarationFromDottedModule(moduleDeclaration.body); return recursiveInnerModule || moduleDeclaration.body; @@ -5276,20 +5886,28 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function shouldEmitModuleDeclaration(node: ModuleDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + return isInstantiatedModule(node, compilerOptions.preserveConstEnums || compilerOptions.isolatedModules); } function isModuleMergedWithES6Class(node: ModuleDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + return languageVersion === ScriptTarget.ES6 && !!(resolver.getNodeCheckFlags(node) & NodeCheckFlags.LexicalModuleMergesWithClass); } function emitModuleDeclaration(node: ModuleDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + // Emit only if this module is non-ambient. let shouldEmit = shouldEmitModuleDeclaration(node); if (!shouldEmit) { - return emitOnlyPinnedOrTripleSlashComments(node); + emitOnlyPinnedOrTripleSlashComments(node); + return; } + let hoistedInDeclarationScope = shouldHoistDeclarationInSystemJsModule(node); let emitVarForModule = !hoistedInDeclarationScope && !isModuleMergedWithES6Class(node); @@ -5346,7 +5964,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitModuleMemberName(node); write(" = {}));"); emitEnd(node); - if (!isES6ExportedDeclaration(node) && node.name.kind === SyntaxKind.Identifier && node.parent === currentSourceFile) { + if (!isES6ExportedDeclaration(node) && node.name.kind === SyntaxKind.Identifier && nodeStack.getParent() === currentSourceFile) { if (compilerOptions.module === ModuleKind.System && (node.flags & NodeFlags.Export)) { writeLine(); write(`${exportFunctionForFile}("`); @@ -5371,6 +5989,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitRequire(moduleName: Expression) { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack, moduleName); + if (moduleName.kind === SyntaxKind.StringLiteral) { write("require("); let text = tryRenameExternalModule(moduleName); @@ -5379,7 +5999,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } else { emitStart(moduleName); + pushNode(moduleName); emitLiteral(moduleName); + popNode(); emitEnd(moduleName); } emitToken(SyntaxKind.CloseParenToken, moduleName.end); @@ -5390,6 +6012,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function getNamespaceDeclarationNode(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (node.kind === SyntaxKind.ImportEqualsDeclaration) { return node; } @@ -5400,51 +6024,75 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function isDefaultImport(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + return node.kind === SyntaxKind.ImportDeclaration && (node).importClause && !!(node).importClause.name; } function emitExportImportAssignments(node: Node) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (isAliasSymbolDeclaration(node) && resolver.isValueAliasDeclaration(node)) { emitExportMemberAssignments((node).name); } - forEachChild(node, emitExportImportAssignments); + forEachChild(node, emitExportImportAssignmentsOfChild); + } + + function emitExportImportAssignmentsOfChild(node: Node) { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack, node); + + pushNode(node); + emitExportImportAssignments(node); + popNode(); } function emitImportDeclaration(node: ImportDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (languageVersion < ScriptTarget.ES6) { return emitExternalImportDeclaration(node); } // ES6 import - if (node.importClause) { - let shouldEmitDefaultBindings = resolver.isReferencedAliasDeclaration(node.importClause); - let shouldEmitNamedBindings = node.importClause.namedBindings && resolver.isReferencedAliasDeclaration(node.importClause.namedBindings, /* checkChildren */ true); + let importClause = node.importClause; + if (importClause) { + let shouldEmitDefaultBindings = resolver.isReferencedAliasDeclaration(importClause); + let shouldEmitNamedBindings = importClause.namedBindings && resolver.isReferencedAliasDeclaration(importClause.namedBindings, /* checkChildren */ true); if (shouldEmitDefaultBindings || shouldEmitNamedBindings) { write("import "); - emitStart(node.importClause); + + pushNode(importClause); + emitStart(importClause); + if (shouldEmitDefaultBindings) { - emit(node.importClause.name); + emit(importClause.name); if (shouldEmitNamedBindings) { write(", "); } } + if (shouldEmitNamedBindings) { - emitLeadingComments(node.importClause.namedBindings); - emitStart(node.importClause.namedBindings); - if (node.importClause.namedBindings.kind === SyntaxKind.NamespaceImport) { + let namedBindings = importClause.namedBindings; + pushNode(namedBindings); + emitLeadingComments(namedBindings); + emitStart(namedBindings); + if (namedBindings.kind === SyntaxKind.NamespaceImport) { write("* as "); - emit((node.importClause.namedBindings).name); + emit((namedBindings).name); } else { write("{ "); - emitExportOrImportSpecifierList((node.importClause.namedBindings).elements, resolver.isReferencedAliasDeclaration); + emitExportOrImportSpecifierList((namedBindings).elements, resolver.isReferencedAliasDeclaration); write(" }"); } - emitEnd(node.importClause.namedBindings); - emitTrailingComments(node.importClause.namedBindings); + emitEnd(namedBindings); + emitTrailingComments(namedBindings); + popNode(); } - emitEnd(node.importClause); + emitEnd(importClause); + popNode(); + write(" from "); emit(node.moduleSpecifier); write(";"); @@ -5458,10 +6106,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitExternalImportDeclaration(node: ImportDeclaration | ImportEqualsDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (contains(externalImports, node)) { let isExportedImport = node.kind === SyntaxKind.ImportEqualsDeclaration && (node.flags & NodeFlags.Export) !== 0; let namespaceDeclaration = getNamespaceDeclarationNode(node); - + let namespaceDeclarationIsTopOfStack = namespaceDeclaration === node; if (compilerOptions.module !== ModuleKind.AMD) { emitLeadingComments(node); emitStart(node); @@ -5469,7 +6119,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi // import x = require("foo") // import * as x from "foo" if (!isExportedImport) write("var "); - emitModuleMemberName(namespaceDeclaration); + if (namespaceDeclarationIsTopOfStack) { + emitModuleMemberName(namespaceDeclaration); + } + else { + pushNode(namespaceDeclaration); + emitModuleMemberName(namespaceDeclaration); + popNode(); + } write(" = "); } else { @@ -5489,7 +6146,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi if (namespaceDeclaration && isDefaultImport(node)) { // import d, * as x from "foo" write(", "); - emitModuleMemberName(namespaceDeclaration); + if (namespaceDeclarationIsTopOfStack) { + emitModuleMemberName(namespaceDeclaration); + } + else { + pushNode(namespaceDeclaration); + emitModuleMemberName(namespaceDeclaration); + popNode(); + } write(" = "); write(getGeneratedNameForNode(node)); } @@ -5500,7 +6164,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } else { if (isExportedImport) { - emitModuleMemberName(namespaceDeclaration); + if (namespaceDeclarationIsTopOfStack) { + emitModuleMemberName(namespaceDeclaration); + } + else { + pushNode(namespaceDeclaration); + emitModuleMemberName(namespaceDeclaration); + popNode(); + } write(" = "); emit(namespaceDeclaration.name); write(";"); @@ -5508,7 +6179,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi else if (namespaceDeclaration && isDefaultImport(node)) { // import d, * as x from "foo" write("var "); - emitModuleMemberName(namespaceDeclaration); + if (namespaceDeclarationIsTopOfStack) { + emitModuleMemberName(namespaceDeclaration); + } + else { + pushNode(namespaceDeclaration); + emitModuleMemberName(namespaceDeclaration); + popNode(); + } write(" = "); write(getGeneratedNameForNode(node)); write(";"); @@ -5519,6 +6197,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitImportEqualsDeclaration(node: ImportEqualsDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (isExternalModuleImportEqualsDeclaration(node)) { emitExternalImportDeclaration(node); return; @@ -5574,6 +6254,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitExportDeclaration(node: ExportDeclaration) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + Debug.assert(compilerOptions.module !== ModuleKind.System); if (languageVersion < ScriptTarget.ES6) { @@ -5593,7 +6275,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi if (resolver.isValueAliasDeclaration(specifier)) { writeLine(); emitStart(specifier); - emitContainingModuleName(specifier); + emitContainingModuleName(); write("."); emitNodeWithCommentsAndWithoutSourcemap(specifier.name); write(" = "); @@ -5626,7 +6308,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi if (node.exportClause) { // export { x, y, ... } write("{ "); + pushNode(node.exportClause); emitExportOrImportSpecifierList(node.exportClause.elements, resolver.isValueAliasDeclaration); + popNode(); write(" }"); } else { @@ -5642,6 +6326,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitExportOrImportSpecifierList(specifiers: ImportOrExportSpecifier[], shouldEmit: (node: Node) => boolean) { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack); + Debug.assert(languageVersion >= ScriptTarget.ES6); let needsComma = false; @@ -5661,6 +6347,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitExportAssignment(node: ExportAssignment) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (!node.isExportEquals && resolver.isValueAliasDeclaration(node)) { if (languageVersion >= ScriptTarget.ES6) { writeLine(); @@ -5684,7 +6372,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } else { emitEs6ExportDefaultCompat(node); - emitContainingModuleName(node); + emitContainingModuleName(); if (languageVersion === ScriptTarget.ES3) { write("[\"default\"] = "); } else { @@ -5699,6 +6387,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function collectExternalModuleInfo(sourceFile: SourceFile) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, sourceFile); + externalImports = []; exportSpecifiers = {}; exportEquals = undefined; @@ -5752,6 +6442,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitExportStarHelper() { + verifyStackBehavior(StackBehavior.Unspecified); + if (hasExportStars) { writeLine(); write("function __export(m) {"); @@ -5763,8 +6455,18 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi write("}"); } } + + function getLocalNameForExternalImportWithPushStackBehavior(node: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration): string { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack, node); + pushNode(node); + let result = getLocalNameForExternalImport(node); + popNode(); + return result; + } function getLocalNameForExternalImport(node: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration): string { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + let namespaceDeclaration = getNamespaceDeclarationNode(node); if (namespaceDeclaration && !isDefaultImport(node)) { return getSourceTextOfNodeFromSourceFile(currentSourceFile, namespaceDeclaration.name); @@ -5778,6 +6480,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function getExternalModuleNameText(importNode: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration): string { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, importNode); + let moduleName = getExternalModuleName(importNode); if (moduleName.kind === SyntaxKind.StringLiteral) { return tryRenameExternalModule(moduleName) || getLiteralText(moduleName); @@ -5787,6 +6491,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitVariableDeclarationsForImports(): void { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack); + if (externalImports.length === 0) { return; } @@ -5811,7 +6517,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi write(", "); } - write(getLocalNameForExternalImport(importNode)); + write(getLocalNameForExternalImportWithPushStackBehavior(importNode)); } if (started) { @@ -5819,7 +6525,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } } - function emitLocalStorageForExportedNamesIfNecessary(exportedDeclarations: (Identifier | Declaration)[]): string { + function emitLocalStorageForExportedNamesIfNecessary(exportedDeclarations: Declaration[]): string { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack); + // when resolving exports local exported entries/indirect exported entries in the module // should always win over entries with similar names that were added via star exports // to support this we store names of local/indirect exported entries in a set. @@ -5865,7 +6573,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi for (let n in exportSpecifiers) { for (let specifier of exportSpecifiers[n]) { // write name of export specified, i.e. 'export {x}' + pushNode(specifier); writeExportedName(specifier.name); + popNode(); } } } @@ -5874,17 +6584,24 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi if (externalImport.kind !== SyntaxKind.ExportDeclaration) { continue; } - + let exportDecl = externalImport; if (!exportDecl.exportClause) { // export * from ... continue; } + pushNode(externalImport); + pushNode(exportDecl.exportClause); for (let element of exportDecl.exportClause.elements) { // write name of indirectly exported entry, i.e. 'export {x} from ...' + pushNode(element); writeExportedName(element.name || element.propertyName); + popNode(); } + + popNode(); + popNode(); } decreaseIndent(); @@ -5894,6 +6611,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi return emitExportStarFunction(exportedNamesStorageRef); function emitExportStarFunction(localNames: string): string { + verifyStackBehavior(StackBehavior.Unspecified); + const exportStarFunction = makeUniqueName("exportStar"); writeLine(); @@ -5925,6 +6644,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function writeExportedName(node: Identifier | Declaration): void { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack, node); + // do not record default exports // they are local to module and never overwritten (explicitly skipped) by star export if (node.kind !== SyntaxKind.Identifier && node.flags & NodeFlags.Default) { @@ -5944,14 +6665,18 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitNodeWithCommentsAndWithoutSourcemap(node); } else { + pushNode(node); emitDeclarationName(node); + popNode(); } write("': true"); } } - function processTopLevelVariableAndFunctionDeclarations(node: SourceFile): (Identifier | Declaration)[] { + function processTopLevelVariableAndFunctionDeclarations(node: SourceFile): Declaration[] { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + // per ES6 spec: // 15.2.1.16.4 ModuleDeclarationInstantiation() Concrete Method // - var declarations are initialized to undefined - 14.a.ii @@ -5961,52 +6686,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi // in theory we should hoist only exported functions and its dependencies // in practice to simplify things we'll hoist all source level functions and variable declaration // including variables declarations for module and class declarations - let hoistedVars: (Identifier | ClassDeclaration | ModuleDeclaration | EnumDeclaration)[]; + let seen: Map = {}; + let hasWrittenVar = false; let hoistedFunctionDeclarations: FunctionDeclaration[]; - let exportedDeclarations: (Identifier | Declaration)[]; + let exportedDeclarations: Declaration[]; visit(node); - if (hoistedVars) { - writeLine(); - write("var "); - let seen: Map = {}; - for (let i = 0; i < hoistedVars.length; ++i) { - let local = hoistedVars[i]; - let name = local.kind === SyntaxKind.Identifier - ? local - : (local).name; - - if (name) { - // do not emit duplicate entries (in case of declaration merging) in the list of hoisted variables - let text = unescapeIdentifier(name.text); - if (hasProperty(seen, text)) { - continue; - } - else { - seen[text] = text; - } - } - - if (i !== 0) { - write(", "); - } - - if (local.kind === SyntaxKind.ClassDeclaration || local.kind === SyntaxKind.ModuleDeclaration || local.kind === SyntaxKind.EnumDeclaration) { - emitDeclarationName(local); - } - else { - emit(local); - } - - let flags = getCombinedNodeFlags(local.kind === SyntaxKind.Identifier ? local.parent : local); - if (flags & NodeFlags.Export) { - if (!exportedDeclarations) { - exportedDeclarations = []; - } - exportedDeclarations.push(local); - } - } + if (hasWrittenVar) { write(";"); } @@ -6025,8 +6712,18 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } return exportedDeclarations; + + function visitChild(node: Node): void { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack, node); + + pushNode(node); + visit(node); + popNode(); + } function visit(node: Node): void { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (node.flags & NodeFlags.Ambient) { return; } @@ -6039,77 +6736,76 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi hoistedFunctionDeclarations.push(node); return; } - - if (node.kind === SyntaxKind.ClassDeclaration) { - if (!hoistedVars) { - hoistedVars = []; - } - - hoistedVars.push(node); - return; - } - - if (node.kind === SyntaxKind.EnumDeclaration) { - if (shouldEmitEnumDeclaration(node)) { - if (!hoistedVars) { - hoistedVars = []; + + hoist: if (shouldHoistNode(node)) { + let name = node.name; + if (name && name.kind === SyntaxKind.Identifier) { + // do not emit duplicate entries (in case of declaration merging) in the list of hoisted variables + let text = unescapeIdentifier(name.text); + if (hasProperty(seen, text)) { + break hoist; } - - hoistedVars.push(node); - } - - return; - } - - if (node.kind === SyntaxKind.ModuleDeclaration) { - if (shouldEmitModuleDeclaration(node)) { - if (!hoistedVars) { - hoistedVars = []; + else { + seen[text] = text; } - - hoistedVars.push(node); } - return; - } - if (node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.BindingElement) { - if (shouldHoistVariable(node, /*checkIfSourceFileLevelDecl*/ false)) { - let name = (node).name; - if (name.kind === SyntaxKind.Identifier) { - if (!hoistedVars) { - hoistedVars = []; - } - - hoistedVars.push(name); - } - else { - forEachChild(name, visit); + if (!hasWrittenVar) { + writeLine(); + write("var "); + hasWrittenVar = true; + } + else { + write(", "); + } + + emitDeclarationName(node); + + let flags = getCombinedNodeFlags(nodeStack); + if (flags & NodeFlags.Export) { + if (!exportedDeclarations) { + exportedDeclarations = []; } + exportedDeclarations.push(node); } - return; } - - if (isInternalModuleImportEqualsDeclaration(node)) { - if (!hoistedVars) { - hoistedVars = []; - } - - hoistedVars.push(node.name); - return; + else if (node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.BindingElement) { + forEachChild((node).name, visitChild); } - - if (isBindingPattern(node)) { - forEach((node).elements, visit); - return; + else if (isBindingPattern(node)) { + forEach((node).elements, visitChild); } - - if (!isDeclaration(node)) { - forEachChild(node, visit); + else if (!isDeclaration(node)) { + forEachChild(node, visitChild); } } } + + function shouldHoistNode(node: Node): node is Declaration { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + + switch (node.kind) { + case SyntaxKind.ClassDeclaration: + return true; + + case SyntaxKind.EnumDeclaration: + return shouldEmitEnumDeclaration(node); + + case SyntaxKind.ModuleDeclaration: + return shouldEmitModuleDeclaration(node); + + case SyntaxKind.VariableDeclaration: + case SyntaxKind.BindingElement: + return shouldHoistVariable(node, /*checkIfSourceFileLevelDecl*/ false) + && (node).name.kind === SyntaxKind.Identifier; + } + + return isInternalModuleImportEqualsDeclaration(node); + } function shouldHoistVariable(node: VariableDeclaration | VariableDeclarationList | BindingElement, checkIfSourceFileLevelDecl: boolean): boolean { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (checkIfSourceFileLevelDecl && !shouldHoistDeclarationInSystemJsModule(node)) { return false; } @@ -6118,8 +6814,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi // - it is top level block scoped // if block scoped variables are nested in some another block then // no other functions can use them except ones that are defined at least in the same block - return (getCombinedNodeFlags(node) & NodeFlags.BlockScoped) === 0 || - getEnclosingBlockScopeContainer(node).kind === SyntaxKind.SourceFile; + return (getCombinedNodeFlags(nodeStack) & NodeFlags.BlockScoped) === 0 || + getEnclosingBlockScopeContainer(nodeStack).kind === SyntaxKind.SourceFile; } function isCurrentFileSystemExternalModule() { @@ -6127,6 +6823,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitSystemModuleBody(node: SourceFile, dependencyGroups: DependencyGroup[], startIndex: number): void { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + // shape of the body in system modules: // function (exports) { // @@ -6181,6 +6879,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitSetters(exportStarFunction: string, dependencyGroups: DependencyGroup[]) { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack); + write("setters:["); for (let i = 0; i < dependencyGroups.length; ++i) { @@ -6194,12 +6894,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi let group = dependencyGroups[i]; // derive a unique name for parameter from the first named entry in the group - let parameterName = makeUniqueName(forEach(group, getLocalNameForExternalImport) || ""); + let parameterName = makeUniqueName(forEach(group, getLocalNameForExternalImportWithPushStackBehavior) || ""); write(`function (${parameterName}) {`); increaseIndent(); for(let entry of group) { - let importVariableName = getLocalNameForExternalImport(entry) || ""; + let importVariableName = getLocalNameForExternalImportWithPushStackBehavior(entry) || ""; switch (entry.kind) { case SyntaxKind.ImportDeclaration: @@ -6221,6 +6921,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi Debug.assert(importVariableName !== ""); if ((entry).exportClause) { + pushNode((entry).exportClause); // export {a, b as c} from 'foo' // emit as: // exports_({ @@ -6238,12 +6939,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } let e = (entry).exportClause.elements[i]; + pushNode(e); write(`"`); emitNodeWithCommentsAndWithoutSourcemap(e.name); write(`": ${parameterName}["`); emitNodeWithCommentsAndWithoutSourcemap(e.propertyName || e.name); write(`"]`); + popNode(); } + popNode(); decreaseIndent(); writeLine(); write("});") @@ -6271,6 +6975,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitExecute(node: SourceFile, startIndex: number) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + write("execute: function() {"); increaseIndent(); writeLine(); @@ -6286,10 +6992,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi continue; case SyntaxKind.ExportDeclaration: if (!(statement).moduleSpecifier) { + pushNode(statement); + pushNode((statement).exportClause); for (let element of (statement).exportClause.elements) { // write call to exporter function for every export specifier in exports list + pushNode(element); emitExportSpecifierInSystemModule(element); + popNode(); } + popNode(); + popNode(); } continue; case SyntaxKind.ImportEqualsDeclaration: @@ -6311,6 +7023,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi type DependencyGroup = Array; function emitSystemModule(node: SourceFile, startIndex: number): void { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + collectExternalModuleInfo(node); // System modules has the following shape // System.register(['dep-1', ... 'dep-n'], function(exports) {/* module body function */}) @@ -6334,16 +7048,19 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi let dependencyGroups: DependencyGroup[] = []; for (let i = 0; i < externalImports.length; ++i) { - let text = getExternalModuleNameText(externalImports[i]); + let externalImport = externalImports[i]; + pushNode(externalImport); + let text = getExternalModuleNameText(externalImport); + popNode(); if (hasProperty(groupIndices, text)) { // deduplicate/group entries in dependency list by the dependency name let groupIndex = groupIndices[text]; - dependencyGroups[groupIndex].push(externalImports[i]); + dependencyGroups[groupIndex].push(externalImport); continue; } else { groupIndices[text] = dependencyGroups.length; - dependencyGroups.push([externalImports[i]]); + dependencyGroups.push([externalImport]); } if (i !== 0) { @@ -6352,6 +7069,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi write(text); } + write(`], function(${exportFunctionForFile}) {`); writeLine(); increaseIndent(); @@ -6364,6 +7082,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitAMDDependencies(node: SourceFile, includeNonAmdDependencies: boolean) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + // An AMD define function has the following shape: // define(id?, dependencies?, factory); // @@ -6396,6 +7116,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } for (let importNode of externalImports) { + pushNode(importNode); // Find the name of the external module let externalModuleName = getExternalModuleNameText(importNode); @@ -6408,6 +7129,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi else { unaliasedModuleNames.push(externalModuleName); } + popNode(); } write("[\"require\", \"exports\""); @@ -6427,6 +7149,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitAMDModule(node: SourceFile, startIndex: number) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + emitEmitHelpers(node); collectExternalModuleInfo(node); @@ -6449,6 +7173,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitCommonJSModule(node: SourceFile, startIndex: number) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + emitEmitHelpers(node); collectExternalModuleInfo(node); emitExportStarHelper(); @@ -6459,6 +7185,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitUMDModule(node: SourceFile, startIndex: number) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + emitEmitHelpers(node); collectExternalModuleInfo(node); @@ -6485,6 +7213,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitES6Module(node: SourceFile, startIndex: number) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + externalImports = undefined; exportSpecifiers = undefined; exportEquals = undefined; @@ -6498,17 +7228,23 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitExportEquals(emitAsReturn: boolean) { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack, exportEquals); + if (exportEquals && resolver.isValueAliasDeclaration(exportEquals)) { + pushNode(exportEquals); writeLine(); emitStart(exportEquals); write(emitAsReturn ? "return " : "module.exports = "); emit((exportEquals).expression); write(";"); emitEnd(exportEquals); + popNode(); } } function emitJsxElement(node: JsxElement | JsxSelfClosingElement) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + switch (compilerOptions.jsx) { case JsxEmit.React: jsxEmitReact(node); @@ -6522,6 +7258,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function trimReactWhitespaceAndApplyEntities(node: JsxText): string { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + let result: string = undefined; let text = getTextOfNode(node, /*includeTrivia*/ true); let firstNonWhitespace = 0; @@ -6568,6 +7306,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function getTextToEmit(node: JsxText) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + switch (compilerOptions.jsx) { case JsxEmit.React: let text = trimReactWhitespaceAndApplyEntities(node); @@ -6584,6 +7324,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitJsxText(node: JsxText) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + switch (compilerOptions.jsx) { case JsxEmit.React: write("\""); @@ -6599,6 +7341,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitJsxExpression(node: JsxExpression) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + if (node.expression) { switch (compilerOptions.jsx) { case JsxEmit.Preserve: @@ -6615,6 +7359,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitDirectivePrologues(statements: Node[], startWithNewLine: boolean): number { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack); + for (let i = 0; i < statements.length; ++i) { if (isPrologueDirective(statements[i])) { if (startWithNewLine || i > 0) { @@ -6631,6 +7377,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function writeLines(text: string): void { + verifyStackBehavior(StackBehavior.Unspecified); + let lines = text.split(/\r\n|\r|\n/g); for (let i = 0; i < lines.length; ++i) { let line = lines[i]; @@ -6642,6 +7390,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitEmitHelpers(node: SourceFile): void { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + // Only emit helpers if the user did not say otherwise. if (!compilerOptions.noEmitHelpers) { // Only Emit __extends function when target ES5. @@ -6672,6 +7422,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitSourceFileNode(node: SourceFile) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + // Start new file on new line writeLine(); emitShebang(); @@ -6708,17 +7460,33 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitTempDeclarations(/*newLine*/ true); } - emitLeadingComments(node.endOfFileToken); + visitNodeWithPushStackBehavior(node.endOfFileToken, emitLeadingComments); + } + + function visitNodeWithPushStackBehavior(node: T, visit: (node: T) => U): U; + function visitNodeWithPushStackBehavior(node: T, pipe: (node: T, emitNode: (node: T) => U) => U, visit?: (node: T) => U): U; + function visitNodeWithPushStackBehavior(node: T, pipe: (node: T, emitNode?: (node: T) => U) => U, visit?: (node: T) => U): U { + let result: U; + if (node) { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack, node); + pushNode(node); + result = visit ? pipe(node, visit) : pipe(node); + popNode(); + } + + return result; } function emitNodeWithCommentsAndWithoutSourcemap(node: Node): void { - emitNodeConsideringCommentsOption(node, emitNodeWithoutSourceMap); + emitNodeConsideringCommentsOption(node, emitNodeWithoutSourceMapAndWithoutStackBehavior); } - function emitNodeConsideringCommentsOption(node: Node, emitNodeConsideringSourcemap: (node: Node) => void): void { + function emitNodeConsideringCommentsOption(node: Node, emitNodeConsideringSourcemapWithoutStackBehavior: (node: Node) => void): void { if (node) { + verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack, node); + if (node.flags & NodeFlags.Ambient) { - return emitOnlyPinnedOrTripleSlashComments(node); + return visitNodeWithPushStackBehavior(node, emitOnlyPinnedOrTripleSlashComments); } if (isSpecializedCommentHandling(node)) { @@ -6726,26 +7494,40 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi return emitNodeWithoutSourceMap(node); } - let emitComments = shouldEmitLeadingAndTrailingComments(node); - if (emitComments) { - emitLeadingComments(node); - } + visitNodeWithPushStackBehavior(node, emitNodeWithCommentsAndWithoutStackBehavior, emitNodeConsideringSourcemapWithoutStackBehavior); + } + } + + function emitNodeWithCommentsAndWithoutStackBehavior(node: Node, emitNodeConsideringSourcemapWithoutStackBehavior: (node: Node) => void): void { + verifyStackBehavior(StackBehavior.Unspecified, node); + + let emitComments = shouldEmitLeadingAndTrailingComments(node); + if (emitComments) { + emitLeadingComments(node); + } - emitNodeConsideringSourcemap(node); + emitNodeConsideringSourcemapWithoutStackBehavior(node); - if (emitComments) { - emitTrailingComments(node); - } + if (emitComments) { + emitTrailingComments(node); } } - + function emitNodeWithoutSourceMap(node: Node): void { + if (node) { + visitNodeWithPushStackBehavior(node, emitNodeWithoutSourceMapAndWithoutStackBehavior); + } + } + + function emitNodeWithoutSourceMapAndWithoutStackBehavior(node: Node): void { if (node) { emitJavaScriptWorker(node); } } function isSpecializedCommentHandling(node: Node): boolean { + verifyStackBehavior(StackBehavior.Unspecified, node); + switch (node.kind) { // All of these entities are emitted in a specialized fashion. As such, we allow // the specialized methods for each to handle the comments on the nodes. @@ -6783,10 +7565,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi // then we don't want to emit comments when we emit the body. It will have already // been taken care of when we emitted the 'return' statement for the function // expression body. + let parentNode = nodeStack.getParent(); if (node.kind !== SyntaxKind.Block && - node.parent && - node.parent.kind === SyntaxKind.ArrowFunction && - (node.parent).body === node && + parentNode && + parentNode.kind === SyntaxKind.ArrowFunction && + (parentNode).body === node && compilerOptions.target <= ScriptTarget.ES5) { return false; @@ -6797,6 +7580,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitJavaScriptWorker(node: Node) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + // Check if the node can be emitted regardless of the ScriptTarget switch (node.kind) { case SyntaxKind.Identifier: @@ -6968,10 +7753,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function hasDetachedComments(pos: number) { + verifyStackBehavior(StackBehavior.Unspecified); + return detachedCommentsInfo !== undefined && lastOrUndefined(detachedCommentsInfo).nodePos === pos; } function getLeadingCommentsWithoutDetachedComments() { + verifyStackBehavior(StackBehavior.Unspecified); + // get the leading comments from detachedPos let leadingComments = getLeadingCommentRanges(currentSourceFile.text, lastOrUndefined(detachedCommentsInfo).detachedCommentEndPos); @@ -6991,6 +7780,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi * @param onlyPinnedOrTripleSlashComments whether the filtering should be performed. */ function filterComments(ranges: CommentRange[], onlyPinnedOrTripleSlashComments: boolean): CommentRange[] { + verifyStackBehavior(StackBehavior.Unspecified); + // If we're removing comments, then we want to strip out all but the pinned or // triple slash comments. if (ranges && onlyPinnedOrTripleSlashComments) { @@ -7004,9 +7795,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function getLeadingCommentsToEmit(node: Node) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + // Emit the leading comments only if the parent's pos doesn't match because parent should take care of emitting these comments - if (node.parent) { - if (node.parent.kind === SyntaxKind.SourceFile || node.pos !== node.parent.pos) { + let parentNode = nodeStack.getParent(); + if (parentNode) { + if (parentNode.kind === SyntaxKind.SourceFile || node.pos !== parentNode.pos) { if (hasDetachedComments(node.pos)) { // get comments without detached comments return getLeadingCommentsWithoutDetachedComments(); @@ -7020,23 +7814,32 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function getTrailingCommentsToEmit(node: Node) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + // Emit the trailing comments only if the parent's pos doesn't match because parent should take care of emitting these comments - if (node.parent) { - if (node.parent.kind === SyntaxKind.SourceFile || node.end !== node.parent.end) { + let parentNode = nodeStack.getParent(); + if (parentNode && !nodeIsSynthesized(parentNode)) { + if (parentNode.kind === SyntaxKind.SourceFile || node.end !== parentNode.end) { return getTrailingCommentRanges(currentSourceFile.text, node.end); } } } function emitOnlyPinnedOrTripleSlashComments(node: Node) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + emitLeadingCommentsWorker(node, /*onlyPinnedOrTripleSlashComments:*/ true); } function emitLeadingComments(node: Node) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + return emitLeadingCommentsWorker(node, /*onlyPinnedOrTripleSlashComments:*/ compilerOptions.removeComments); } function emitLeadingCommentsWorker(node: Node, onlyPinnedOrTripleSlashComments: boolean) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + // If the caller only wants pinned or triple slash comments, then always filter // down to that set. Otherwise, filter based on the current compiler options. let leadingComments = filterComments(getLeadingCommentsToEmit(node), onlyPinnedOrTripleSlashComments); @@ -7048,6 +7851,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitTrailingComments(node: Node) { + verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node); + // Emit the trailing comments only if the parent's end doesn't match let trailingComments = filterComments(getTrailingCommentsToEmit(node), /*onlyPinnedOrTripleSlashComments:*/ compilerOptions.removeComments); @@ -7068,6 +7873,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitLeadingCommentsOfPosition(pos: number) { + verifyStackBehavior(StackBehavior.Unspecified); + let leadingComments: CommentRange[]; if (hasDetachedComments(pos)) { // get comments without detached comments @@ -7086,6 +7893,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function emitDetachedComments(node: TextRange) { + verifyStackBehavior(StackBehavior.Unspecified); + let leadingComments = getLeadingCommentRanges(currentSourceFile.text, node.pos); if (leadingComments) { let detachedComments: CommentRange[] = []; @@ -7138,6 +7947,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi } function isPinnedOrTripleSlashComment(comment: CommentRange) { + verifyStackBehavior(StackBehavior.Unspecified); + if (currentSourceFile.text.charCodeAt(comment.pos + 1) === CharacterCodes.asterisk) { return currentSourceFile.text.charCodeAt(comment.pos + 2) === CharacterCodes.exclamation; } @@ -7150,6 +7961,27 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi return true; } } + + function verifyStackBehavior(behavior: StackBehavior, node?: Node) { + let currentNode = nodeStack.getNode(); + switch (behavior) { + case StackBehavior.Unspecified: + return; + + case StackBehavior.NodeIsOnTopOfStack: + if (node) { + Debug.assert(currentNode === node, "Incorrect node stack behavior. Expected node to be on the top of the stack."); + } + else { + Debug.assert(currentNode !== undefined, "Incorrect node stack behavior. Expected node to be on the top of the stack."); + } + return; + + case StackBehavior.ParentIsOnTopOfStack: + Debug.assert(currentNode !== node, "Incorrect node stack behavior. Expected parent of node to be on the top of the stack."); + return; + } + } } function emitFile(jsFilePath: string, sourceFile?: SourceFile) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 47182e3952737..e6aa0979b07cf 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -441,6 +441,48 @@ namespace ts { FailedAndReported = 3 } + // @internal + export interface ParentNavigable { + /** Creates an object used to navigate parent nodes of this node. */ + createParentNavigator(): ParentNavigator; + } + + // @internal + export interface ParentNavigator extends ParentNavigable { + /** Gets the grandparent node for the current node. */ + getGrandparent(): Node; + /** Gets the parent node for the current node. */ + getParent(): Node; + /** Gets the current node. */ + getNode(): Node; + /** Gets the SyntaxKind of the current node. */ + getKind(): SyntaxKind; + /** Moves the navigator to the parent node of the current node. */ + moveToParent(): boolean; + } + + // @internal + export interface NodeStack extends ParentNavigable { + /** 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. */ + getParent(): Node; + /** Gets the node at the top of the stack. */ + getNode(): Node; + /** Gets the SyntaxKind for the node at the top of the stack. */ + getKind(): SyntaxKind; + /** Traverses the stack from top to bottom until it finds a node that matches the supplied predicate. */ + findAncestorNode(match: (node: Node) => node is T): T; + /** Traverses the stack from top to bottom until it finds a node that matches the supplied predicate. */ + findAncestorNode(match: (node: Node) => boolean): Node; + /** Pushes a node onto the stack. */ + pushNode(node: Node): void; + /** Replaces the node at the top of the stack. */ + setNode(node: Node): void; + /** Pops the top node from the stack. */ + popNode(): void; + } + export interface Node extends TextRange { kind: SyntaxKind; flags: NodeFlags; @@ -456,6 +498,7 @@ namespace ts { /* @internal */ locals?: SymbolTable; // Locals associated with node (initialized by binding) /* @internal */ nextContainer?: Node; // Next container in declaration order (initialized by binding) /* @internal */ localSymbol?: Symbol; // Local symbol declared by node (initialized by binding only for exported nodes) + /* @internal */ createParentNavigator(): ParentNavigator; } export interface NodeArray extends Array, TextRange { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 99ea06532a0ee..1fb2535945011 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -250,9 +250,10 @@ namespace ts { // Gets the nearest enclosing block scope container that has the provided node // as a descendant, that is not the provided node. - export function getEnclosingBlockScopeContainer(node: Node): Node { - let current = node.parent; - while (current) { + export function getEnclosingBlockScopeContainer(navigable: ParentNavigable): Node { + let nav = navigable.createParentNavigator(); + while (nav.moveToParent()) { + let current = nav.getNode(); if (isFunctionLike(current)) { return current; } @@ -268,12 +269,10 @@ namespace ts { case SyntaxKind.Block: // function block is not considered block-scope container // see comment in binder.ts: bind(...), case for SyntaxKind.Block - if (!isFunctionLike(current.parent)) { + if (!isFunctionLike(nav.getParent())) { return current; } } - - current = current.parent; } } @@ -368,32 +367,22 @@ namespace ts { return node.kind === SyntaxKind.EnumDeclaration && isConst(node); } - function walkUpBindingElementsAndPatterns(node: Node): Node { + /** Returns the node flags for this node and all relevant parent nodes. */ + export function getCombinedNodeFlags(navigable: ParentNavigable): NodeFlags { + let nav = navigable.createParentNavigator(); + let node = nav.getNode(); while (node && (node.kind === SyntaxKind.BindingElement || isBindingPattern(node))) { - node = node.parent; + node = nav.moveToParent() ? nav.getNode() : undefined; } - return node; - } - - // Returns the node flags for this node and all relevant parent nodes. This is done so that - // nodes like variable declarations and binding elements can returned a view of their flags - // that includes the modifiers from their container. i.e. flags like export/declare aren't - // stored on the variable declaration directly, but on the containing variable statement - // (if it has one). Similarly, flags for let/const are store on the variable declaration - // list. By calling this function, all those flags are combined so that the client can treat - // the node as if it actually had those flags. - export function getCombinedNodeFlags(node: Node): NodeFlags { - node = walkUpBindingElementsAndPatterns(node); - let flags = node.flags; if (node.kind === SyntaxKind.VariableDeclaration) { - node = node.parent; + node = nav.moveToParent() ? nav.getNode() : undefined; } if (node && node.kind === SyntaxKind.VariableDeclarationList) { flags |= node.flags; - node = node.parent; + node = nav.moveToParent() ? nav.getNode() : undefined; } if (node && node.kind === SyntaxKind.VariableStatement) { @@ -403,12 +392,12 @@ namespace ts { return flags; } - export function isConst(node: Node): boolean { - return !!(getCombinedNodeFlags(node) & NodeFlags.Const); + export function isConst(navigable: ParentNavigable): boolean { + return !!(getCombinedNodeFlags(navigable) & NodeFlags.Const); } - export function isLet(node: Node): boolean { - return !!(getCombinedNodeFlags(node) & NodeFlags.Let); + export function isLet(navigable: ParentNavigable): boolean { + return !!(getCombinedNodeFlags(navigable) & NodeFlags.Let); } export function isPrologueDirective(node: Node): boolean { @@ -663,37 +652,41 @@ namespace ts { return node && node.kind === SyntaxKind.MethodDeclaration && node.parent.kind === SyntaxKind.ObjectLiteralExpression; } - export function getContainingFunction(node: Node): FunctionLikeDeclaration { - while (true) { - node = node.parent; - if (!node || isFunctionLike(node)) { + export function getContainingFunction(navigable: ParentNavigable): FunctionLikeDeclaration { + let nav = navigable.createParentNavigator(); + while (nav.moveToParent()) { + let node = nav.getNode(); + if (isFunctionLike(node)) { return node; } } + + return undefined; } - export function getContainingClass(node: Node): ClassLikeDeclaration { - while (true) { - node = node.parent; - if (!node || isClassLike(node)) { + export function getContainingClass(navigable: ParentNavigable): ClassLikeDeclaration { + let nav = navigable.createParentNavigator(); + while (nav.moveToParent()) { + let node = nav.getNode(); + if (isClassLike(node)) { return node; } } + + return undefined; } - export function getThisContainer(node: Node, includeArrowFunctions: boolean): Node { - while (true) { - node = node.parent; - if (!node) { - return undefined; - } + export function getThisContainer(navigable: ParentNavigable, includeArrowFunctions: boolean): Node { + let nav = navigable.createParentNavigator(); + while (nav.moveToParent()) { + let node = nav.getNode(); switch (node.kind) { case SyntaxKind.ComputedPropertyName: // If the grandparent node is an object literal (as opposed to a class), // then the computed property is not a 'this' container. // A computed property name in a class needs to be a this container // so that we can error on it. - if (isClassLike(node.parent.parent)) { + if (isClassLike(nav.getGrandparent())) { return node; } // If this is a computed property, then the parent should not @@ -701,19 +694,20 @@ namespace ts { // in an object literal, like a method or accessor. But in order for // such a parent to be a this container, the reference must be in // the *body* of the container. - node = node.parent; + nav.moveToParent(); break; case SyntaxKind.Decorator: // Decorators are always applied outside of the body of a class or method. - if (node.parent.kind === SyntaxKind.Parameter && isClassElement(node.parent.parent)) { + if (nav.getParent().kind === SyntaxKind.Parameter && isClassElement(nav.getGrandparent())) { // If the decorator's parent is a Parameter, we resolve the this container from // the grandparent class declaration. - node = node.parent.parent; + nav.moveToParent(); + nav.moveToParent(); } - else if (isClassElement(node.parent)) { + else if (isClassElement(nav.getParent())) { // If the decorator's parent is a class element, we resolve the 'this' container // from the parent class declaration. - node = node.parent; + nav.moveToParent(); } break; case SyntaxKind.ArrowFunction: @@ -738,17 +732,17 @@ namespace ts { } } - export function getSuperContainer(node: Node, includeFunctions: boolean): Node { - while (true) { - node = node.parent; - if (!node) return node; + export function getSuperContainer(navigable: ParentNavigable, includeFunctions: boolean): Node { + let nav = navigable.createParentNavigator(); + while (nav.moveToParent()) { + let node = nav.getNode(); switch (node.kind) { case SyntaxKind.ComputedPropertyName: // If the grandparent node is an object literal (as opposed to a class), // then the computed property is not a 'super' container. // A computed property name in a class needs to be a super container // so that we can error on it. - if (isClassLike(node.parent.parent)) { + if (isClassLike(nav.getGrandparent())) { return node; } // If this is a computed property, then the parent should not @@ -756,19 +750,20 @@ namespace ts { // in an object literal, like a method or accessor. But in order for // such a parent to be a super container, the reference must be in // the *body* of the container. - node = node.parent; + nav.moveToParent(); break; case SyntaxKind.Decorator: // Decorators are always applied outside of the body of a class or method. - if (node.parent.kind === SyntaxKind.Parameter && isClassElement(node.parent.parent)) { + if (nav.getParent().kind === SyntaxKind.Parameter && isClassElement(nav.getGrandparent())) { // If the decorator's parent is a Parameter, we resolve the this container from // the grandparent class declaration. - node = node.parent.parent; + nav.moveToParent(); + nav.moveToParent(); } - else if (isClassElement(node.parent)) { + else if (isClassElement(nav.getParent())) { // If the decorator's parent is a class element, we resolve the 'this' container // from the parent class declaration. - node = node.parent; + nav.moveToParent(); } break; case SyntaxKind.FunctionDeclaration: @@ -814,7 +809,9 @@ namespace ts { return (node).expression; } - export function nodeCanBeDecorated(node: Node): boolean { + export function nodeCanBeDecorated(navigable: ParentNavigable): boolean { + let nav = navigable.createParentNavigator(); + let node = nav.getNode(); switch (node.kind) { case SyntaxKind.ClassDeclaration: // classes are valid targets @@ -822,17 +819,17 @@ namespace ts { case SyntaxKind.PropertyDeclaration: // property declarations are valid if their parent is a class declaration. - return node.parent.kind === SyntaxKind.ClassDeclaration; + return nav.getParent().kind === SyntaxKind.ClassDeclaration; case SyntaxKind.Parameter: // if the parameter's parent has a body and its grandparent is a class declaration, this is a valid target; - return (node.parent).body && node.parent.parent.kind === SyntaxKind.ClassDeclaration; + return (nav.getParent()).body && nav.getGrandparent().kind === SyntaxKind.ClassDeclaration; case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: case SyntaxKind.MethodDeclaration: // if this method has a body and its parent is a class declaration, this is a valid target. - return (node).body && node.parent.kind === SyntaxKind.ClassDeclaration; + return (node).body && nav.getParent().kind === SyntaxKind.ClassDeclaration; } return false; @@ -891,106 +888,151 @@ namespace ts { return nodeIsDecorated(node) || childIsDecorated(node); } - export function isExpression(node: Node): boolean { - switch (node.kind) { - case SyntaxKind.ThisKeyword: - case SyntaxKind.SuperKeyword: - case SyntaxKind.NullKeyword: - case SyntaxKind.TrueKeyword: - case SyntaxKind.FalseKeyword: - case SyntaxKind.RegularExpressionLiteral: - case SyntaxKind.ArrayLiteralExpression: - case SyntaxKind.ObjectLiteralExpression: - case SyntaxKind.PropertyAccessExpression: - case SyntaxKind.ElementAccessExpression: - case SyntaxKind.CallExpression: - case SyntaxKind.NewExpression: - case SyntaxKind.TaggedTemplateExpression: - case SyntaxKind.AsExpression: - case SyntaxKind.TypeAssertionExpression: - case SyntaxKind.ParenthesizedExpression: - case SyntaxKind.FunctionExpression: - case SyntaxKind.ClassExpression: - case SyntaxKind.ArrowFunction: - case SyntaxKind.VoidExpression: - case SyntaxKind.DeleteExpression: - case SyntaxKind.TypeOfExpression: - case SyntaxKind.PrefixUnaryExpression: - case SyntaxKind.PostfixUnaryExpression: - case SyntaxKind.BinaryExpression: - case SyntaxKind.ConditionalExpression: - case SyntaxKind.SpreadElementExpression: - case SyntaxKind.TemplateExpression: - case SyntaxKind.NoSubstitutionTemplateLiteral: - case SyntaxKind.OmittedExpression: - case SyntaxKind.JsxElement: - case SyntaxKind.JsxSelfClosingElement: - case SyntaxKind.YieldExpression: - return true; - case SyntaxKind.QualifiedName: - while (node.parent.kind === SyntaxKind.QualifiedName) { - node = node.parent; - } - return node.parent.kind === SyntaxKind.TypeQuery; - case SyntaxKind.Identifier: - if (node.parent.kind === SyntaxKind.TypeQuery) { + /** Returns whether the node is part of an expression. */ + export function isExpression(navigable: ParentNavigable): boolean { + let nav = navigable.createParentNavigator(); + while (true) { + let node = nav.getNode(); + switch (node.kind) { + case SyntaxKind.ThisKeyword: + case SyntaxKind.SuperKeyword: + case SyntaxKind.NullKeyword: + case SyntaxKind.TrueKeyword: + case SyntaxKind.FalseKeyword: + case SyntaxKind.RegularExpressionLiteral: + case SyntaxKind.ArrayLiteralExpression: + case SyntaxKind.ObjectLiteralExpression: + case SyntaxKind.PropertyAccessExpression: + case SyntaxKind.ElementAccessExpression: + case SyntaxKind.CallExpression: + case SyntaxKind.NewExpression: + case SyntaxKind.TaggedTemplateExpression: + case SyntaxKind.AsExpression: + case SyntaxKind.TypeAssertionExpression: + case SyntaxKind.ParenthesizedExpression: + case SyntaxKind.FunctionExpression: + case SyntaxKind.ClassExpression: + case SyntaxKind.ArrowFunction: + case SyntaxKind.VoidExpression: + case SyntaxKind.DeleteExpression: + case SyntaxKind.TypeOfExpression: + case SyntaxKind.PrefixUnaryExpression: + case SyntaxKind.PostfixUnaryExpression: + case SyntaxKind.BinaryExpression: + case SyntaxKind.ConditionalExpression: + case SyntaxKind.SpreadElementExpression: + case SyntaxKind.TemplateExpression: + case SyntaxKind.NoSubstitutionTemplateLiteral: + case SyntaxKind.OmittedExpression: + case SyntaxKind.JsxElement: + case SyntaxKind.JsxSelfClosingElement: + case SyntaxKind.YieldExpression: return true; - } - // fall through - case SyntaxKind.NumericLiteral: - case SyntaxKind.StringLiteral: - let parent = node.parent; - switch (parent.kind) { - case SyntaxKind.VariableDeclaration: - case SyntaxKind.Parameter: - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.PropertySignature: - case SyntaxKind.EnumMember: - case SyntaxKind.PropertyAssignment: - case SyntaxKind.BindingElement: - return (parent).initializer === node; - case SyntaxKind.ExpressionStatement: - case SyntaxKind.IfStatement: - case SyntaxKind.DoStatement: - case SyntaxKind.WhileStatement: - case SyntaxKind.ReturnStatement: - case SyntaxKind.WithStatement: - case SyntaxKind.SwitchStatement: - case SyntaxKind.CaseClause: - case SyntaxKind.ThrowStatement: - case SyntaxKind.SwitchStatement: - return (parent).expression === node; - case SyntaxKind.ForStatement: - let forStatement = parent; - return (forStatement.initializer === node && forStatement.initializer.kind !== SyntaxKind.VariableDeclarationList) || - forStatement.condition === node || - forStatement.incrementor === node; - case SyntaxKind.ForInStatement: - case SyntaxKind.ForOfStatement: - let forInStatement = parent; - return (forInStatement.initializer === node && forInStatement.initializer.kind !== SyntaxKind.VariableDeclarationList) || - forInStatement.expression === node; - case SyntaxKind.TypeAssertionExpression: - case SyntaxKind.AsExpression: - return node === (parent).expression; - case SyntaxKind.TemplateSpan: - return node === (parent).expression; - case SyntaxKind.ComputedPropertyName: - return node === (parent).expression; - case SyntaxKind.Decorator: - case SyntaxKind.JsxExpression: + case SyntaxKind.QualifiedName: + while (nav.moveToParent()) { + if (nav.getKind() !== SyntaxKind.QualifiedName) { + break; + } + } + + return nav.getKind() === SyntaxKind.TypeQuery; + + case SyntaxKind.Identifier: + if (nav.getParent().kind === SyntaxKind.TypeQuery) { return true; - case SyntaxKind.ExpressionWithTypeArguments: - return (parent).expression === node && isExpressionWithTypeArgumentsInClassExtendsClause(parent); - default: - if (isExpression(parent)) { + } + + // fall through + case SyntaxKind.NumericLiteral: + case SyntaxKind.StringLiteral: + nav.moveToParent(); + + let parent = nav.getNode(); + switch (parent.kind) { + case SyntaxKind.VariableDeclaration: + case SyntaxKind.Parameter: + case SyntaxKind.PropertyDeclaration: + case SyntaxKind.PropertySignature: + case SyntaxKind.EnumMember: + case SyntaxKind.PropertyAssignment: + case SyntaxKind.BindingElement: + return (parent).initializer === node; + case SyntaxKind.ExpressionStatement: + case SyntaxKind.IfStatement: + case SyntaxKind.DoStatement: + case SyntaxKind.WhileStatement: + case SyntaxKind.ReturnStatement: + case SyntaxKind.WithStatement: + case SyntaxKind.SwitchStatement: + case SyntaxKind.CaseClause: + case SyntaxKind.ThrowStatement: + case SyntaxKind.SwitchStatement: + return (parent).expression === node; + case SyntaxKind.ForStatement: + let forStatement = parent; + return (forStatement.initializer === node && forStatement.initializer.kind !== SyntaxKind.VariableDeclarationList) || + forStatement.condition === node || + forStatement.incrementor === node; + case SyntaxKind.ForInStatement: + case SyntaxKind.ForOfStatement: + let forInStatement = parent; + return (forInStatement.initializer === node && forInStatement.initializer.kind !== SyntaxKind.VariableDeclarationList) || + forInStatement.expression === node; + case SyntaxKind.TypeAssertionExpression: + case SyntaxKind.AsExpression: + return node === (parent).expression; + case SyntaxKind.TemplateSpan: + return node === (parent).expression; + case SyntaxKind.ComputedPropertyName: + return node === (parent).expression; + case SyntaxKind.Decorator: + case SyntaxKind.JsxExpression: return true; - } - } + case SyntaxKind.ExpressionWithTypeArguments: + return (parent).expression === node && isExpressionWithTypeArgumentsInClassExtendsClause(nav); + default: + continue; + } + } + return false; } - return false; } + /** + * Returns whether the expression has lesser, greater, + * or equal precedence to the binary '+' operator + */ + export function comparePrecedenceToBinaryPlus(expression: Expression): Comparison { + // All binary expressions have lower precedence than '+' apart from '*', '/', and '%' + // which have greater precedence and '-' which has equal precedence. + // All unary operators have a higher precedence apart from yield. + // Arrow functions and conditionals have a lower precedence, + // although we convert the former into regular function expressions in ES5 mode, + // and in ES6 mode this function won't get called anyway. + // + // TODO (drosen): Note that we need to account for the upcoming 'yield' and + // spread ('...') unary operators that are anticipated for ES6. + switch (expression.kind) { + case SyntaxKind.BinaryExpression: + switch ((expression).operatorToken.kind) { + case SyntaxKind.AsteriskToken: + case SyntaxKind.SlashToken: + case SyntaxKind.PercentToken: + return Comparison.GreaterThan; + case SyntaxKind.PlusToken: + case SyntaxKind.MinusToken: + return Comparison.EqualTo; + default: + return Comparison.LessThan; + } + case SyntaxKind.YieldExpression: + case SyntaxKind.ConditionalExpression: + return Comparison.LessThan; + default: + return Comparison.GreaterThan; + } + } + export function isInstantiatedModule(node: ModuleDeclaration, preserveConstEnums: boolean) { let moduleState = getModuleInstanceState(node); return moduleState === ModuleInstanceState.Instantiated || @@ -1217,6 +1259,10 @@ namespace ts { return false; } } + + export function isModuleDeclaration(node: Node): node is ModuleDeclaration { + return node && node.kind === SyntaxKind.ModuleDeclaration; + } // True if the given identifier, string literal, or number literal is the name of a declaration node export function isDeclarationName(name: Node): boolean { @@ -1462,7 +1508,7 @@ namespace ts { export function nodeStartsNewLexicalEnvironment(n: Node): boolean { return isFunctionLike(n) || n.kind === SyntaxKind.ModuleDeclaration || n.kind === SyntaxKind.SourceFile; } - + export function cloneEntityName(node: EntityName): EntityName { if (node.kind === SyntaxKind.Identifier) { let clone = createSynthesizedNode(SyntaxKind.Identifier); @@ -2007,10 +2053,18 @@ namespace ts { return token >= SyntaxKind.FirstAssignment && token <= SyntaxKind.LastAssignment; } - export function isExpressionWithTypeArgumentsInClassExtendsClause(node: Node): boolean { - return node.kind === SyntaxKind.ExpressionWithTypeArguments && - (node.parent).token === SyntaxKind.ExtendsKeyword && - isClassLike(node.parent.parent); + /** + * Tests whether the node is an ExpressionWithTypeArguments node that is part of the `extends` + * clause of a class. + */ + export function isExpressionWithTypeArgumentsInClassExtendsClause(navigable: ParentNavigable): boolean { + let nav = navigable.createParentNavigator(); + if (nav.getKind() === SyntaxKind.ExpressionWithTypeArguments) { + if ((nav.getParent()).token === SyntaxKind.ExtendsKeyword) { + return isClassLike(nav.getGrandparent()); + } + } + return false; } // Returns false if this heritage clause element's expression contains something unsupported diff --git a/src/services/services.ts b/src/services/services.ts index d5ede917b8f78..f32549dfd6c7a 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -322,6 +322,10 @@ namespace ts { return child.kind < SyntaxKind.FirstNode ? child : child.getLastToken(sourceFile); } + + public createParentNavigator(): ParentNavigator { + return createParentNavigator(this); + } } class SymbolObject implements Symbol {