diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 49434e80abab4..e2d31162127c9 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -2592,7 +2592,7 @@ namespace ts { } // Currently, we only support generators that were originally async function bodies. - if (asteriskToken && node.emitFlags & NodeEmitFlags.AsyncFunctionBody) { + if (asteriskToken && getEmitFlags(node) & EmitFlags.AsyncFunctionBody) { transformFlags |= TransformFlags.AssertGenerator; } @@ -2667,7 +2667,7 @@ namespace ts { // down-level generator. // Currently we do not support transforming any other generator fucntions // down level. - if (asteriskToken && node.emitFlags & NodeEmitFlags.AsyncFunctionBody) { + if (asteriskToken && getEmitFlags(node) & EmitFlags.AsyncFunctionBody) { transformFlags |= TransformFlags.AssertGenerator; } } @@ -2698,7 +2698,7 @@ namespace ts { // down-level generator. // Currently we do not support transforming any other generator fucntions // down level. - if (asteriskToken && node.emitFlags & NodeEmitFlags.AsyncFunctionBody) { + if (asteriskToken && getEmitFlags(node) & EmitFlags.AsyncFunctionBody) { transformFlags |= TransformFlags.AssertGenerator; } diff --git a/src/compiler/comments.ts b/src/compiler/comments.ts index 1bca13a4bdbc6..116a8e849a373 100644 --- a/src/compiler/comments.ts +++ b/src/compiler/comments.ts @@ -5,7 +5,7 @@ namespace ts { export interface CommentWriter { reset(): void; setSourceFile(sourceFile: SourceFile): void; - emitNodeWithComments(node: Node, emitCallback: (node: Node) => void): void; + emitNodeWithComments(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void; emitBodyWithDetachedComments(node: Node, detachedRange: TextRange, emitCallback: (node: Node) => void): void; emitTrailingCommentsOfPosition(pos: number): void; } @@ -34,22 +34,24 @@ namespace ts { emitTrailingCommentsOfPosition, }; - function emitNodeWithComments(node: Node, emitCallback: (node: Node) => void) { + function emitNodeWithComments(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) { if (disabled) { - emitCallback(node); + emitCallback(emitContext, node); return; } if (node) { - const { pos, end } = node.commentRange || node; - const emitFlags = node.emitFlags; + const { pos, end } = getCommentRange(node); + const emitFlags = getEmitFlags(node); if ((pos < 0 && end < 0) || (pos === end)) { // Both pos and end are synthesized, so just emit the node without comments. - if (emitFlags & NodeEmitFlags.NoNestedComments) { - disableCommentsAndEmit(node, emitCallback); + if (emitFlags & EmitFlags.NoNestedComments) { + disabled = true; + emitCallback(emitContext, node); + disabled = false; } else { - emitCallback(node); + emitCallback(emitContext, node); } } else { @@ -58,8 +60,8 @@ namespace ts { } const isEmittedNode = node.kind !== SyntaxKind.NotEmittedStatement; - const skipLeadingComments = pos < 0 || (emitFlags & NodeEmitFlags.NoLeadingComments) !== 0; - const skipTrailingComments = end < 0 || (emitFlags & NodeEmitFlags.NoTrailingComments) !== 0; + const skipLeadingComments = pos < 0 || (emitFlags & EmitFlags.NoLeadingComments) !== 0; + const skipTrailingComments = end < 0 || (emitFlags & EmitFlags.NoTrailingComments) !== 0; // Emit leading comments if the position is not synthesized and the node // has not opted out from emitting leading comments. @@ -90,11 +92,13 @@ namespace ts { performance.measure("commentTime", "preEmitNodeWithComment"); } - if (emitFlags & NodeEmitFlags.NoNestedComments) { - disableCommentsAndEmit(node, emitCallback); + if (emitFlags & EmitFlags.NoNestedComments) { + disabled = true; + emitCallback(emitContext, node); + disabled = false; } else { - emitCallback(node); + emitCallback(emitContext, node); } if (extendedDiagnostics) { @@ -125,9 +129,9 @@ namespace ts { } const { pos, end } = detachedRange; - const emitFlags = node.emitFlags; - const skipLeadingComments = pos < 0 || (emitFlags & NodeEmitFlags.NoLeadingComments) !== 0; - const skipTrailingComments = disabled || end < 0 || (emitFlags & NodeEmitFlags.NoTrailingComments) !== 0; + const emitFlags = getEmitFlags(node); + const skipLeadingComments = pos < 0 || (emitFlags & EmitFlags.NoLeadingComments) !== 0; + const skipTrailingComments = disabled || end < 0 || (emitFlags & EmitFlags.NoTrailingComments) !== 0; if (!skipLeadingComments) { emitDetachedCommentsAndUpdateCommentsInfo(detachedRange); @@ -137,8 +141,10 @@ namespace ts { performance.measure("commentTime", "preEmitBodyWithDetachedComments"); } - if (emitFlags & NodeEmitFlags.NoNestedComments) { - disableCommentsAndEmit(node, emitCallback); + if (emitFlags & EmitFlags.NoNestedComments && !disabled) { + disabled = true; + emitCallback(node); + disabled = false; } else { emitCallback(node); @@ -284,17 +290,6 @@ namespace ts { detachedCommentsInfo = undefined; } - function disableCommentsAndEmit(node: Node, emitCallback: (node: Node) => void): void { - if (disabled) { - emitCallback(node); - } - else { - disabled = true; - emitCallback(node); - disabled = false; - } - } - function hasDetachedComments(pos: number) { return detachedCommentsInfo !== undefined && lastOrUndefined(detachedCommentsInfo).nodePos === pos; } diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 66d967c69189f..4e1f455364201 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -840,6 +840,72 @@ namespace ts { }; } + /** + * High-order function, creates a function that executes a function composition. + * For example, `chain(a, b)` is the equivalent of `x => ((a', b') => y => b'(a'(y)))(a(x), b(x))` + * + * @param args The functions to chain. + */ + export function chain(...args: ((t: T) => (u: U) => U)[]): (t: T) => (u: U) => U; + export function chain(a: (t: T) => (u: U) => U, b: (t: T) => (u: U) => U, c: (t: T) => (u: U) => U, d: (t: T) => (u: U) => U, e: (t: T) => (u: U) => U): (t: T) => (u: U) => U { + if (e) { + const args: ((t: T) => (u: U) => U)[] = []; + for (let i = 0; i < arguments.length; i++) { + args[i] = arguments[i]; + } + + return t => compose(...map(args, f => f(t))); + } + else if (d) { + return t => compose(a(t), b(t), c(t), d(t)); + } + else if (c) { + return t => compose(a(t), b(t), c(t)); + } + else if (b) { + return t => compose(a(t), b(t)); + } + else if (a) { + return t => compose(a(t)); + } + else { + return t => u => u; + } + } + + /** + * High-order function, composes functions. Note that functions are composed inside-out; + * for example, `compose(a, b)` is the equivalent of `x => b(a(x))`. + * + * @param args The functions to compose. + */ + export function compose(...args: ((t: T) => T)[]): (t: T) => T; + export function compose(a: (t: T) => T, b: (t: T) => T, c: (t: T) => T, d: (t: T) => T, e: (t: T) => T): (t: T) => T { + if (e) { + const args: ((t: T) => T)[] = []; + for (let i = 0; i < arguments.length; i++) { + args[i] = arguments[i]; + } + + return t => reduceLeft<(t: T) => T, T>(args, (u, f) => f(u), t); + } + else if (d) { + return t => d(c(b(a(t)))); + } + else if (c) { + return t => c(b(a(t))); + } + else if (b) { + return t => b(a(t)); + } + else if (a) { + return t => a(t); + } + else { + return t => t; + } + } + function formatStringFromArgs(text: string, args: { [index: number]: any; }, baseIndex?: number): string { baseIndex = baseIndex || 0; @@ -1778,7 +1844,6 @@ namespace ts { this.transformFlags = TransformFlags.None; this.parent = undefined; this.original = undefined; - this.transformId = 0; } export let objectAllocator: ObjectAllocator = { diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 90742f30c16fd..b3242e211de92 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -206,10 +206,8 @@ const _super = (function (geti, seti) { const sourceMap = createSourceMapWriter(host, writer); const { - emitStart, - emitEnd, - emitTokenStart, - emitTokenEnd + emitNodeWithSourceMap, + emitTokenWithSourceMap } = sourceMap; const comments = createCommentWriter(host, writer, sourceMap); @@ -234,36 +232,27 @@ const _super = (function (geti, seti) { let isOwnFileEmit: boolean; let emitSkipped = false; - performance.mark("beforeTransform"); + const sourceFiles = getSourceFilesToEmit(host, targetSourceFile); // Transform the source files - const transformed = transformFiles( - resolver, - host, - getSourceFilesToEmit(host, targetSourceFile), - transformers); - - performance.measure("transformTime", "beforeTransform"); - - // Extract helpers from the result + performance.mark("beforeTransform"); const { - getTokenSourceMapRange, - isSubstitutionEnabled, - isEmitNotificationEnabled, - onSubstituteNode, - onEmitNode - } = transformed; - - performance.mark("beforePrint"); + transformed, + emitNodeWithSubstitution, + emitNodeWithNotification + } = transformFiles(resolver, host, sourceFiles, transformers); + performance.measure("transformTime", "beforeTransform"); // Emit each output file - forEachTransformedEmitFile(host, transformed.getSourceFiles(), emitFile, emitOnlyDtsFiles); - - // Clean up after transformation - transformed.dispose(); - + performance.mark("beforePrint"); + forEachTransformedEmitFile(host, transformed, emitFile, emitOnlyDtsFiles); performance.measure("printTime", "beforePrint"); + // Clean up emit nodes on parse tree + for (const sourceFile of sourceFiles) { + disposeEmitNodes(sourceFile); + } + return { emitSkipped, diagnostics: emitterDiagnostics.getDiagnostics(), @@ -358,154 +347,137 @@ const _super = (function (geti, seti) { currentFileIdentifiers = node.identifiers; sourceMap.setSourceFile(node); comments.setSourceFile(node); - emitNodeWithNotification(node, emitWorker); + pipelineEmitWithNotification(EmitContext.SourceFile, node); } /** * Emits a node. */ function emit(node: Node) { - emitNodeWithNotification(node, emitWithComments); - } - - - /** - * Emits a node with comments. - * - * NOTE: Do not call this method directly. It is part of the emit pipeline - * and should only be called indirectly from emit. - */ - function emitWithComments(node: Node) { - emitNodeWithComments(node, emitWithSourceMap); + pipelineEmitWithNotification(EmitContext.Unspecified, node); } /** - * Emits a node with source maps. - * - * NOTE: Do not call this method directly. It is part of the emit pipeline - * and should only be called indirectly from emitWithComments. + * Emits an IdentifierName. */ - function emitWithSourceMap(node: Node) { - emitNodeWithSourceMap(node, emitWorker); - } - function emitIdentifierName(node: Identifier) { - if (node) { - emitNodeWithNotification(node, emitIdentifierNameWithComments); - } - } - - function emitIdentifierNameWithComments(node: Identifier) { - emitNodeWithComments(node, emitWorker); + pipelineEmitWithNotification(EmitContext.IdentifierName, node); } /** * Emits an expression node. */ function emitExpression(node: Expression) { - emitNodeWithNotification(node, emitExpressionWithComments); + pipelineEmitWithNotification(EmitContext.Expression, node); } /** - * Emits an expression with comments. + * Emits a node with possible notification. * - * NOTE: Do not call this method directly. It is part of the emitExpression pipeline - * and should only be called indirectly from emitExpression. + * NOTE: Do not call this method directly. It is part of the emit pipeline + * and should only be called from printSourceFile, emit, emitExpression, or + * emitIdentifierName. */ - function emitExpressionWithComments(node: Expression) { - emitNodeWithComments(node, emitExpressionWithSourceMap); + function pipelineEmitWithNotification(emitContext: EmitContext, node: Node) { + emitNodeWithNotification(emitContext, node, pipelineEmitWithComments); } /** - * Emits an expression with source maps. + * Emits a node with comments. * - * NOTE: Do not call this method directly. It is part of the emitExpression pipeline - * and should only be called indirectly from emitExpressionWithComments. + * NOTE: Do not call this method directly. It is part of the emit pipeline + * and should only be called indirectly from pipelineEmitWithNotification. */ - function emitExpressionWithSourceMap(node: Expression) { - emitNodeWithSourceMap(node, emitExpressionWorker); + function pipelineEmitWithComments(emitContext: EmitContext, node: Node) { + // Do not emit comments for SourceFile + if (emitContext === EmitContext.SourceFile) { + pipelineEmitWithSourceMap(emitContext, node); + return; + } + + emitNodeWithComments(emitContext, node, pipelineEmitWithSourceMap); } /** - * Emits a node with emit notification if available. + * Emits a node with source maps. + * + * NOTE: Do not call this method directly. It is part of the emit pipeline + * and should only be called indirectly from pipelineEmitWithComments. */ - function emitNodeWithNotification(node: Node, emitCallback: (node: Node) => void) { - if (node) { - if (isEmitNotificationEnabled(node)) { - onEmitNode(node, emitCallback); - } - else { - emitCallback(node); - } - } - } - - function emitNodeWithSourceMap(node: Node, emitCallback: (node: Node) => void) { - if (node) { - emitStart(/*range*/ node, /*contextNode*/ node, shouldSkipLeadingSourceMapForNode, shouldSkipSourceMapForChildren, getSourceMapRange); - emitCallback(node); - emitEnd(/*range*/ node, /*contextNode*/ node, shouldSkipTrailingSourceMapForNode, shouldSkipSourceMapForChildren, getSourceMapRange); + function pipelineEmitWithSourceMap(emitContext: EmitContext, node: Node) { + // Do not emit source mappings for SourceFile or IdentifierName + if (emitContext === EmitContext.SourceFile + || emitContext === EmitContext.IdentifierName) { + pipelineEmitWithSubstitution(emitContext, node); + return; } - } - function getSourceMapRange(node: Node) { - return node.sourceMapRange || node; + emitNodeWithSourceMap(emitContext, node, pipelineEmitWithSubstitution); } /** - * Determines whether to skip leading comment emit for a node. - * - * We do not emit comments for NotEmittedStatement nodes or any node that has - * NodeEmitFlags.NoLeadingComments. + * Emits a node with possible substitution. * - * @param node A Node. + * NOTE: Do not call this method directly. It is part of the emit pipeline + * and should only be called indirectly from pipelineEmitWithSourceMap or + * pipelineEmitInUnspecifiedContext (when picking a more specific context). */ - function shouldSkipLeadingCommentsForNode(node: Node) { - return isNotEmittedStatement(node) - || (node.emitFlags & NodeEmitFlags.NoLeadingComments) !== 0; + function pipelineEmitWithSubstitution(emitContext: EmitContext, node: Node) { + emitNodeWithSubstitution(emitContext, node, pipelineEmitForContext); } /** - * Determines whether to skip source map emit for the start position of a node. - * - * We do not emit source maps for NotEmittedStatement nodes or any node that - * has NodeEmitFlags.NoLeadingSourceMap. + * Emits a node. * - * @param node A Node. + * NOTE: Do not call this method directly. It is part of the emit pipeline + * and should only be called indirectly from pipelineEmitWithSubstitution. */ - function shouldSkipLeadingSourceMapForNode(node: Node) { - return isNotEmittedStatement(node) - || (node.emitFlags & NodeEmitFlags.NoLeadingSourceMap) !== 0; + function pipelineEmitForContext(emitContext: EmitContext, node: Node): void { + switch (emitContext) { + case EmitContext.SourceFile: return pipelineEmitInSourceFileContext(node); + case EmitContext.IdentifierName: return pipelineEmitInIdentifierNameContext(node); + case EmitContext.Unspecified: return pipelineEmitInUnspecifiedContext(node); + case EmitContext.Expression: return pipelineEmitInExpressionContext(node); + } } - /** - * Determines whether to skip source map emit for the end position of a node. + * Emits a node in the SourceFile EmitContext. * - * We do not emit source maps for NotEmittedStatement nodes or any node that - * has NodeEmitFlags.NoTrailingSourceMap. - * - * @param node A Node. + * NOTE: Do not call this method directly. It is part of the emit pipeline + * and should only be called indirectly from pipelineEmitForContext. */ - function shouldSkipTrailingSourceMapForNode(node: Node) { - return isNotEmittedStatement(node) - || (node.emitFlags & NodeEmitFlags.NoTrailingSourceMap) !== 0; + function pipelineEmitInSourceFileContext(node: Node): void { + const kind = node.kind; + switch (kind) { + // Top-level nodes + case SyntaxKind.SourceFile: + return emitSourceFile(node); + } } /** - * Determines whether to skip source map emit for a node and its children. + * Emits a node in the IdentifierName EmitContext. * - * We do not emit source maps for a node that has NodeEmitFlags.NoNestedSourceMaps. + * NOTE: Do not call this method directly. It is part of the emit pipeline + * and should only be called indirectly from pipelineEmitForContext. */ - function shouldSkipSourceMapForChildren(node: Node) { - return (node.emitFlags & NodeEmitFlags.NoNestedSourceMaps) !== 0; - } - - function emitWorker(node: Node): void { - if (tryEmitSubstitute(node, emitWorker, /*isExpression*/ false)) { - return; + function pipelineEmitInIdentifierNameContext(node: Node): void { + const kind = node.kind; + switch (kind) { + // Identifiers + case SyntaxKind.Identifier: + return emitIdentifier(node); } + } + /** + * Emits a node in the Unspecified EmitContext. + * + * NOTE: Do not call this method directly. It is part of the emit pipeline + * and should only be called indirectly from pipelineEmitForContext. + */ + function pipelineEmitInUnspecifiedContext(node: Node): void { const kind = node.kind; switch (kind) { // Pseudo-literals @@ -541,7 +513,8 @@ const _super = (function (geti, seti) { case SyntaxKind.StringKeyword: case SyntaxKind.SymbolKeyword: case SyntaxKind.GlobalKeyword: - return writeTokenNode(node); + writeTokenText(kind); + return; // Parse tree nodes @@ -746,25 +719,24 @@ const _super = (function (geti, seti) { case SyntaxKind.EnumMember: return emitEnumMember(node); - // Top-level nodes - case SyntaxKind.SourceFile: - return emitSourceFile(node); - // JSDoc nodes (ignored) - // Transformation nodes (ignored) } + // If the node is an expression, try to emit it as an expression with + // substitution. if (isExpression(node)) { - return emitExpressionWorker(node); + return pipelineEmitWithSubstitution(EmitContext.Expression, node); } } - function emitExpressionWorker(node: Node) { - if (tryEmitSubstitute(node, emitExpressionWorker, /*isExpression*/ true)) { - return; - } - + /** + * Emits a node in the Expression EmitContext. + * + * NOTE: Do not call this method directly. It is part of the emit pipeline + * and should only be called indirectly from pipelineEmitForContext. + */ + function pipelineEmitInExpressionContext(node: Node): void { const kind = node.kind; switch (kind) { // Literals @@ -786,7 +758,8 @@ const _super = (function (geti, seti) { case SyntaxKind.SuperKeyword: case SyntaxKind.TrueKeyword: case SyntaxKind.ThisKeyword: - return writeTokenNode(node); + writeTokenText(kind); + return; // Expressions case SyntaxKind.ArrayLiteralExpression: @@ -888,7 +861,7 @@ const _super = (function (geti, seti) { // function emitIdentifier(node: Identifier) { - if (node.emitFlags & NodeEmitFlags.UMDDefine) { + if (getEmitFlags(node) & EmitFlags.UMDDefine) { writeLines(umdHelper); } else { @@ -1161,7 +1134,7 @@ const _super = (function (geti, seti) { write("{}"); } else { - const indentedFlag = node.emitFlags & NodeEmitFlags.Indented; + const indentedFlag = getEmitFlags(node) & EmitFlags.Indented; if (indentedFlag) { increaseIndent(); } @@ -1177,24 +1150,22 @@ const _super = (function (geti, seti) { } function emitPropertyAccessExpression(node: PropertyAccessExpression) { - if (tryEmitConstantValue(node)) { - return; - } - let indentBeforeDot = false; let indentAfterDot = false; - if (!(node.emitFlags & NodeEmitFlags.NoIndentation)) { + if (!(getEmitFlags(node) & EmitFlags.NoIndentation)) { const dotRangeStart = node.expression.end; const dotRangeEnd = skipTrivia(currentText, node.expression.end) + 1; const dotToken = { kind: SyntaxKind.DotToken, pos: dotRangeStart, end: dotRangeEnd }; indentBeforeDot = needsIndentation(node, node.expression, dotToken); indentAfterDot = needsIndentation(node, dotToken, node.name); } - const shouldEmitDotDot = !indentBeforeDot && needsDotDotForPropertyAccess(node.expression); emitExpression(node.expression); increaseIndentIf(indentBeforeDot); + + const shouldEmitDotDot = !indentBeforeDot && needsDotDotForPropertyAccess(node.expression); write(shouldEmitDotDot ? ".." : "."); + increaseIndentIf(indentAfterDot); emit(node.name); decreaseIndentIf(indentBeforeDot, indentAfterDot); @@ -1208,19 +1179,17 @@ const _super = (function (geti, seti) { const text = getLiteralTextOfNode(expression); return text.indexOf(tokenToString(SyntaxKind.DotToken)) < 0; } - else { + else if (isPropertyAccessExpression(expression) || isElementAccessExpression(expression)) { // check if constant enum value is integer - const constantValue = tryGetConstEnumValue(expression); + const constantValue = getConstantValue(expression); // isFinite handles cases when constantValue is undefined - return isFinite(constantValue) && Math.floor(constantValue) === constantValue; + return isFinite(constantValue) + && Math.floor(constantValue) === constantValue + && compilerOptions.removeComments; } } function emitElementAccessExpression(node: ElementAccessExpression) { - if (tryEmitConstantValue(node)) { - return; - } - emitExpression(node.expression); write("["); emitExpression(node.argumentExpression); @@ -1426,7 +1395,7 @@ const _super = (function (geti, seti) { } function emitBlockStatements(node: Block) { - if (node.emitFlags & NodeEmitFlags.SingleLine) { + if (getEmitFlags(node) & EmitFlags.SingleLine) { emitList(node, node.statements, ListFormat.SingleLineBlockStatements); } else { @@ -1630,12 +1599,12 @@ const _super = (function (geti, seti) { const body = node.body; if (body) { if (isBlock(body)) { - const indentedFlag = node.emitFlags & NodeEmitFlags.Indented; + const indentedFlag = getEmitFlags(node) & EmitFlags.Indented; if (indentedFlag) { increaseIndent(); } - if (node.emitFlags & NodeEmitFlags.ReuseTempVariableScope) { + if (getEmitFlags(node) & EmitFlags.ReuseTempVariableScope) { emitSignatureHead(node); emitBlockFunctionBody(node, body); } @@ -1679,7 +1648,7 @@ const _super = (function (geti, seti) { // * A non-synthesized body's start and end position are on different lines. // * Any statement in the body starts on a new line. - if (body.emitFlags & NodeEmitFlags.SingleLine) { + if (getEmitFlags(body) & EmitFlags.SingleLine) { return true; } @@ -1750,7 +1719,7 @@ const _super = (function (geti, seti) { write("class"); emitNodeWithPrefix(" ", node.name, emitIdentifierName); - const indentedFlag = node.emitFlags & NodeEmitFlags.Indented; + const indentedFlag = getEmitFlags(node) & EmitFlags.Indented; if (indentedFlag) { increaseIndent(); } @@ -2083,8 +2052,8 @@ const _super = (function (geti, seti) { // "comment1" is not considered to be leading comment for node.initializer // but rather a trailing comment on the previous node. const initializer = node.initializer; - if (!shouldSkipLeadingCommentsForNode(initializer)) { - const commentRange = initializer.commentRange || initializer; + if ((getEmitFlags(initializer) & EmitFlags.NoLeadingComments) === 0) { + const commentRange = getCommentRange(initializer); emitTrailingCommentsOfPosition(commentRange.pos); } @@ -2156,23 +2125,23 @@ const _super = (function (geti, seti) { } function emitHelpers(node: Node) { - const emitFlags = node.emitFlags; + const emitFlags = getEmitFlags(node); let helpersEmitted = false; - if (emitFlags & NodeEmitFlags.EmitEmitHelpers) { + if (emitFlags & EmitFlags.EmitEmitHelpers) { helpersEmitted = emitEmitHelpers(currentSourceFile); } - if (emitFlags & NodeEmitFlags.EmitExportStar) { + if (emitFlags & EmitFlags.EmitExportStar) { writeLines(exportStarHelper); helpersEmitted = true; } - if (emitFlags & NodeEmitFlags.EmitSuperHelper) { + if (emitFlags & EmitFlags.EmitSuperHelper) { writeLines(superHelper); helpersEmitted = true; } - if (emitFlags & NodeEmitFlags.EmitAdvancedSuperHelper) { + if (emitFlags & EmitFlags.EmitAdvancedSuperHelper) { writeLines(advancedSuperHelper); helpersEmitted = true; } @@ -2294,36 +2263,6 @@ const _super = (function (geti, seti) { } } - function tryEmitSubstitute(node: Node, emitNode: (node: Node) => void, isExpression: boolean) { - if (isSubstitutionEnabled(node) && (node.emitFlags & NodeEmitFlags.NoSubstitution) === 0) { - const substitute = onSubstituteNode(node, isExpression); - if (substitute !== node) { - substitute.emitFlags |= NodeEmitFlags.NoSubstitution; - emitNode(substitute); - return true; - } - } - - return false; - } - - function tryEmitConstantValue(node: PropertyAccessExpression | ElementAccessExpression): boolean { - const constantValue = tryGetConstEnumValue(node); - if (constantValue !== undefined) { - write(String(constantValue)); - if (!compilerOptions.removeComments) { - const propertyName = isPropertyAccessExpression(node) - ? declarationNameToString(node.name) - : getTextOfNode(node.argumentExpression); - write(` /* ${propertyName} */`); - } - - return true; - } - - return false; - } - function emitEmbeddedStatement(node: Statement) { if (isBlock(node)) { write(" "); @@ -2447,7 +2386,7 @@ const _super = (function (geti, seti) { } if (shouldEmitInterveningComments) { - const commentRange = child.commentRange || child; + const commentRange = getCommentRange(child); emitTrailingCommentsOfPosition(commentRange.pos); } else { @@ -2503,31 +2442,13 @@ const _super = (function (geti, seti) { } function writeToken(token: SyntaxKind, pos: number, contextNode?: Node) { - const tokenStartPos = emitTokenStart(token, pos, contextNode, shouldSkipLeadingSourceMapForToken, getTokenSourceMapRange); - const tokenEndPos = writeTokenText(token, tokenStartPos); - return emitTokenEnd(token, tokenEndPos, contextNode, shouldSkipTrailingSourceMapForToken, getTokenSourceMapRange); - } - - function shouldSkipLeadingSourceMapForToken(contextNode: Node) { - return (contextNode.emitFlags & NodeEmitFlags.NoTokenLeadingSourceMaps) !== 0; - } - - function shouldSkipTrailingSourceMapForToken(contextNode: Node) { - return (contextNode.emitFlags & NodeEmitFlags.NoTokenTrailingSourceMaps) !== 0; + return emitTokenWithSourceMap(contextNode, token, pos, writeTokenText); } function writeTokenText(token: SyntaxKind, pos?: number) { const tokenString = tokenToString(token); write(tokenString); - return positionIsSynthesized(pos) ? -1 : pos + tokenString.length; - } - - function writeTokenNode(node: Node) { - if (node) { - emitStart(/*range*/ node, /*contextNode*/ node, shouldSkipLeadingSourceMapForNode, shouldSkipSourceMapForChildren, getSourceMapRange); - writeTokenText(node.kind); - emitEnd(/*range*/ node, /*contextNode*/ node, shouldSkipTrailingSourceMapForNode, shouldSkipSourceMapForChildren, getSourceMapRange); - } + return pos < 0 ? pos : pos + tokenString.length; } function increaseIndentIf(value: boolean, valueToWriteWhenNotIndenting?: string) { @@ -2692,16 +2613,6 @@ const _super = (function (geti, seti) { return getLiteralText(node, currentSourceFile, languageVersion); } - function tryGetConstEnumValue(node: Node): number { - if (compilerOptions.isolatedModules) { - return undefined; - } - - return isPropertyAccessExpression(node) || isElementAccessExpression(node) - ? resolver.getConstantValue(node) - : undefined; - } - function isSingleLineEmptyBlock(block: Block) { return !block.multiLine && block.statements.length === 0 diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 48296145dbc11..d873c3ef87777 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -76,7 +76,7 @@ namespace ts { // the original node. We also need to exclude specific properties and only include own- // properties (to skip members already defined on the shared prototype). const clone = createNode(node.kind, /*location*/ undefined, node.flags); - clone.original = node; + setOriginalNode(clone, node); for (const key in node) { if (clone.hasOwnProperty(key) || !node.hasOwnProperty(key)) { @@ -435,7 +435,7 @@ namespace ts { export function createPropertyAccess(expression: Expression, name: string | Identifier, location?: TextRange, flags?: NodeFlags) { const node = createNode(SyntaxKind.PropertyAccessExpression, location, flags); node.expression = parenthesizeForAccess(expression); - node.emitFlags = NodeEmitFlags.NoIndentation; + (node.emitNode || (node.emitNode = {})).flags |= EmitFlags.NoIndentation; node.name = typeof name === "string" ? createIdentifier(name) : name; return node; } @@ -444,7 +444,7 @@ namespace ts { if (node.expression !== expression || node.name !== name) { const propertyAccess = createPropertyAccess(expression, name, /*location*/ node, node.flags); // Because we are updating existed propertyAccess we want to inherit its emitFlags instead of using default from createPropertyAccess - propertyAccess.emitFlags = node.emitFlags; + (propertyAccess.emitNode || (propertyAccess.emitNode = {})).flags = getEmitFlags(node); return updateNode(propertyAccess, node); } return node; @@ -1551,7 +1551,7 @@ namespace ts { } else { const expression = isIdentifier(memberName) ? createPropertyAccess(target, memberName, location) : createElementAccess(target, memberName, location); - expression.emitFlags |= NodeEmitFlags.NoNestedSourceMaps; + (expression.emitNode || (expression.emitNode = {})).flags |= EmitFlags.NoNestedSourceMaps; return expression; } } @@ -1744,7 +1744,7 @@ namespace ts { ); // Mark this node as originally an async function - generatorFunc.emitFlags |= NodeEmitFlags.AsyncFunctionBody; + (generatorFunc.emitNode || (generatorFunc.emitNode = {})).flags |= EmitFlags.AsyncFunctionBody; return createCall( createHelperName(externalHelpersModuleName, "__awaiter"), @@ -2222,7 +2222,7 @@ namespace ts { target.push(startOnNewLine(createStatement(createLiteral("use strict")))); foundUseStrict = true; } - if (statement.emitFlags & NodeEmitFlags.CustomPrologue) { + if (getEmitFlags(statement) & EmitFlags.CustomPrologue) { target.push(visitor ? visitNode(statement, visitor, isStatement) : statement); } else { @@ -2643,14 +2643,178 @@ namespace ts { export function setOriginalNode(node: T, original: Node): T { node.original = original; if (original) { - const { emitFlags, commentRange, sourceMapRange } = original; - if (emitFlags) node.emitFlags = emitFlags; - if (commentRange) node.commentRange = commentRange; - if (sourceMapRange) node.sourceMapRange = sourceMapRange; + const emitNode = original.emitNode; + if (emitNode) node.emitNode = mergeEmitNode(emitNode, node.emitNode); } return node; } + function mergeEmitNode(sourceEmitNode: EmitNode, destEmitNode: EmitNode) { + const { flags, commentRange, sourceMapRange, tokenSourceMapRanges } = sourceEmitNode; + if (!destEmitNode && (flags || commentRange || sourceMapRange || tokenSourceMapRanges)) destEmitNode = {}; + if (flags) destEmitNode.flags = flags; + if (commentRange) destEmitNode.commentRange = commentRange; + if (sourceMapRange) destEmitNode.sourceMapRange = sourceMapRange; + if (tokenSourceMapRanges) destEmitNode.tokenSourceMapRanges = mergeTokenSourceMapRanges(tokenSourceMapRanges, destEmitNode.tokenSourceMapRanges); + return destEmitNode; + } + + function mergeTokenSourceMapRanges(sourceRanges: Map, destRanges: Map) { + if (!destRanges) destRanges = createMap(); + copyProperties(sourceRanges, destRanges); + return destRanges; + } + + /** + * Clears any EmitNode entries from parse-tree nodes. + * @param sourceFile A source file. + */ + export function disposeEmitNodes(sourceFile: SourceFile) { + // During transformation we may need to annotate a parse tree node with transient + // transformation properties. As parse tree nodes live longer than transformation + // nodes, we need to make sure we reclaim any memory allocated for custom ranges + // from these nodes to ensure we do not hold onto entire subtrees just for position + // information. We also need to reset these nodes to a pre-transformation state + // for incremental parsing scenarios so that we do not impact later emit. + sourceFile = getSourceFileOfNode(getParseTreeNode(sourceFile)); + const emitNode = sourceFile && sourceFile.emitNode; + const annotatedNodes = emitNode && emitNode.annotatedNodes; + if (annotatedNodes) { + for (const node of annotatedNodes) { + node.emitNode = undefined; + } + } + } + + /** + * Associates a node with the current transformation, initializing + * various transient transformation properties. + * + * @param node The node. + */ + function getOrCreateEmitNode(node: Node) { + if (!node.emitNode) { + if (isParseTreeNode(node)) { + // To avoid holding onto transformation artifacts, we keep track of any + // parse tree node we are annotating. This allows us to clean them up after + // all transformations have completed. + if (node.kind === SyntaxKind.SourceFile) { + return node.emitNode = { annotatedNodes: [node] }; + } + + const sourceFile = getSourceFileOfNode(node); + getOrCreateEmitNode(sourceFile).annotatedNodes.push(node); + } + + node.emitNode = {}; + } + + return node.emitNode; + } + + /** + * Gets flags that control emit behavior of a node. + * + * @param node The node. + */ + export function getEmitFlags(node: Node) { + const emitNode = node.emitNode; + return emitNode && emitNode.flags; + } + + /** + * Sets flags that control emit behavior of a node. + * + * @param node The node. + * @param emitFlags The NodeEmitFlags for the node. + */ + export function setEmitFlags(node: T, emitFlags: EmitFlags) { + getOrCreateEmitNode(node).flags = emitFlags; + return node; + } + + /** + * Sets a custom text range to use when emitting source maps. + * + * @param node The node. + * @param range The text range. + */ + export function setSourceMapRange(node: T, range: TextRange) { + getOrCreateEmitNode(node).sourceMapRange = range; + return node; + } + + /** + * Sets the TextRange to use for source maps for a token of a node. + * + * @param node The node. + * @param token The token. + * @param range The text range. + */ + export function setTokenSourceMapRange(node: T, token: SyntaxKind, range: TextRange) { + const emitNode = getOrCreateEmitNode(node); + const tokenSourceMapRanges = emitNode.tokenSourceMapRanges || (emitNode.tokenSourceMapRanges = createMap()); + tokenSourceMapRanges[token] = range; + return node; + } + + /** + * Sets a custom text range to use when emitting comments. + */ + export function setCommentRange(node: T, range: TextRange) { + getOrCreateEmitNode(node).commentRange = range; + return node; + } + + /** + * Gets a custom text range to use when emitting comments. + * + * @param node The node. + */ + export function getCommentRange(node: Node) { + const emitNode = node.emitNode; + return (emitNode && emitNode.commentRange) || node; + } + + /** + * Gets a custom text range to use when emitting source maps. + * + * @param node The node. + */ + export function getSourceMapRange(node: Node) { + const emitNode = node.emitNode; + return (emitNode && emitNode.sourceMapRange) || node; + } + + /** + * Gets the TextRange to use for source maps for a token of a node. + * + * @param node The node. + * @param token The token. + */ + export function getTokenSourceMapRange(node: Node, token: SyntaxKind) { + const emitNode = node.emitNode; + const tokenSourceMapRanges = emitNode && emitNode.tokenSourceMapRanges; + return tokenSourceMapRanges && tokenSourceMapRanges[token]; + } + + /** + * Gets the constant value to emit for an expression. + */ + export function getConstantValue(node: PropertyAccessExpression | ElementAccessExpression) { + const emitNode = node.emitNode; + return emitNode && emitNode.constantValue; + } + + /** + * Sets the constant value to emit for an expression. + */ + export function setConstantValue(node: PropertyAccessExpression | ElementAccessExpression, value: number) { + const emitNode = getOrCreateEmitNode(node); + emitNode.constantValue = value; + return node; + } + export function setTextRange(node: T, location: TextRange): T { if (location) { node.pos = location.pos; @@ -2692,7 +2856,7 @@ namespace ts { return undefined; } - /** + /** * Get the name of a target module from an import/export declaration as should be written in the emitted output. * The emitted output name can be different from the input if: * 1. The module has a /// diff --git a/src/compiler/sourcemap.ts b/src/compiler/sourcemap.ts index eb369be39d777..46db5188e339c 100644 --- a/src/compiler/sourcemap.ts +++ b/src/compiler/sourcemap.ts @@ -18,11 +18,6 @@ namespace ts { */ reset(): void; - /** - * Gets test data for source maps. - */ - getSourceMapData(): SourceMapData; - /** * Set the current source file. * @@ -41,123 +36,23 @@ namespace ts { emitPos(pos: number): void; /** - * Emits a mapping for the start of a range. - * - * If the range's start position is synthetic (undefined or a negative value), no mapping - * will be created. Any trivia at the start position in the original source will be - * skipped. - * - * @param range The range to emit. - */ - emitStart(range: TextRange): void; - - /** - * Emits a mapping for the start of a range. - * - * If the node's start position is synthetic (undefined or a negative value), no mapping - * will be created. Any trivia at the start position in the original source will be - * skipped. - * - * @param range The range to emit. - * @param contextNode The node for the current range. - * @param ignoreNodeCallback A callback used to determine whether to skip source map - * emit for the start position of this node. - * @param ignoreChildrenCallback A callback used to determine whether to skip source - * map emit for all children of this node. - * @param getTextRangeCallbackCallback A callback used to get a custom source map - * range for this node. - */ - emitStart(range: TextRange, contextNode: Node, ignoreNodeCallback: (node: Node) => boolean, ignoreChildrenCallback: (node: Node) => boolean, getTextRangeCallbackCallback: (node: Node) => TextRange): void; - - /** - * Emits a mapping for the end of a range. - * - * If the range's end position is synthetic (undefined or a negative value), no mapping - * will be created. - * - * @param range The range to emit. - */ - emitEnd(range: TextRange): void; - - /** - * Emits a mapping for the end of a range. + * Emits a node with possible leading and trailing source maps. * - * If the node's end position is synthetic (undefined or a negative value), no mapping - * will be created. - * - * @param range The range to emit. - * @param contextNode The node for the current range. - * @param ignoreNodeCallback A callback used to determine whether to skip source map - * emit for the end position of this node. - * @param ignoreChildrenCallback A callback used to determine whether to skip source - * map emit for all children of this node. - * @param getTextRangeCallbackCallback A callback used to get a custom source map - * range for this node. - */ - emitEnd(range: TextRange, contextNode: Node, ignoreNodeCallback: (node: Node) => boolean, ignoreChildrenCallback: (node: Node) => boolean, getTextRangeCallbackCallback: (node: Node) => TextRange): void; - - /** - * Emits a mapping for the start position of a token. - * - * If the token's start position is synthetic (undefined or a negative value), no mapping - * will be created. Any trivia at the start position in the original source will be - * skipped. - * - * @param token The token to emit. - * @param tokenStartPos The start position of the token. - * @returns The start position of the token, following any trivia. - */ - emitTokenStart(token: SyntaxKind, tokenStartPos: number): number; - - /** - * Emits a mapping for the start position of a token. - * - * If the token's start position is synthetic (undefined or a negative value), no mapping - * will be created. Any trivia at the start position in the original source will be - * skipped. - * - * @param token The token to emit. - * @param tokenStartPos The start position of the token. - * @param contextNode The node containing this token. - * @param ignoreTokenCallback A callback used to determine whether to skip source map - * emit for the start position of this token. - * @param getTokenTextRangeCallback A callback used to get a custom source - * map range for this node. - * @returns The start position of the token, following any trivia. + * @param emitContext The current emit context + * @param node The node to emit. + * @param emitCallback The callback used to emit the node. */ - emitTokenStart(token: SyntaxKind, tokenStartPos: number, contextNode: Node, ignoreTokenCallback: (node: Node, token: SyntaxKind) => boolean, getTokenTextRangeCallback: (node: Node, token: SyntaxKind) => TextRange): number; + emitNodeWithSourceMap(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void; /** - * Emits a mapping for the end position of a token. - * - * If the token's end position is synthetic (undefined or a negative value), no mapping - * will be created. + * Emits a token of a node node with possible leading and trailing source maps. * + * @param node The node containing the token. * @param token The token to emit. - * @param tokenEndPos The end position of the token. - * @returns The end position of the token. + * @param tokenStartPos The start pos of the token. + * @param emitCallback The callback used to emit the token. */ - emitTokenEnd(token: SyntaxKind, tokenEndPos: number): number; - - /** - * Emits a mapping for the end position of a token. - * - * If the token's end position is synthetic (undefined or a negative value), no mapping - * will be created. - * - * @param token The token to emit. - * @param tokenEndPos The end position of the token. - * @param contextNode The node containing this token. - * @param ignoreTokenCallback A callback used to determine whether to skip source map - * emit for the end position of this token. - * @param getTokenTextRangeCallback A callback used to get a custom source - * map range for this node. - * @returns The end position of the token. - */ - emitTokenEnd(token: SyntaxKind, tokenEndPos: number, contextNode: Node, ignoreTokenCallback: (node: Node, token: SyntaxKind) => boolean, getTokenTextRangeCallback: (node: Node, token: SyntaxKind) => TextRange): number; - - /*@deprecated*/ changeEmitSourcePos(): void; - /*@deprecated*/ stopOverridingSpan(): void; + emitTokenWithSourceMap(node: Node, token: SyntaxKind, tokenStartPos: number, emitCallback: (token: SyntaxKind, tokenStartPos: number) => number): number; /** * Gets the text for the source map. @@ -168,44 +63,11 @@ namespace ts { * Gets the SourceMappingURL for the source map. */ getSourceMappingURL(): string; - } - - export function createSourceMapWriter(host: EmitHost, writer: EmitTextWriter): SourceMapWriter { - const compilerOptions = host.getCompilerOptions(); - if (compilerOptions.sourceMap || compilerOptions.inlineSourceMap) { - if (compilerOptions.extendedDiagnostics) { - return createSourceMapWriterWithExtendedDiagnostics(host, writer); - } - - return createSourceMapWriterWorker(host, writer); - } - else { - return getNullSourceMapWriter(); - } - } - let nullSourceMapWriter: SourceMapWriter; - - export function getNullSourceMapWriter(): SourceMapWriter { - if (nullSourceMapWriter === undefined) { - nullSourceMapWriter = { - initialize(filePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean): void { }, - reset(): void { }, - getSourceMapData(): SourceMapData { return undefined; }, - setSourceFile(sourceFile: SourceFile): void { }, - emitPos(pos: number): void { }, - emitStart(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (node: Node) => boolean, ignoreChildrenCallback?: (node: Node) => boolean, getTextRangeCallback?: (node: Node) => TextRange): void { }, - emitEnd(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (node: Node) => boolean, ignoreChildrenCallback?: (node: Node) => boolean, getTextRangeCallback?: (node: Node) => TextRange): void { }, - emitTokenStart(token: SyntaxKind, pos: number, contextNode?: Node, ignoreTokenCallback?: (node: Node) => boolean, getTokenTextRangeCallback?: (node: Node, token: SyntaxKind) => TextRange): number { return -1; }, - emitTokenEnd(token: SyntaxKind, end: number, contextNode?: Node, ignoreTokenCallback?: (node: Node) => boolean, getTokenTextRangeCallback?: (node: Node, token: SyntaxKind) => TextRange): number { return -1; }, - changeEmitSourcePos(): void { }, - stopOverridingSpan(): void { }, - getText(): string { return undefined; }, - getSourceMappingURL(): string { return undefined; } - }; - } - - return nullSourceMapWriter; + /** + * Gets test data for source maps. + */ + getSourceMapData(): SourceMapData; } // Used for initialize lastEncodedSourceMapSpan and reset lastEncodedSourceMapSpan when updateLastEncodedAndRecordedSpans @@ -217,14 +79,12 @@ namespace ts { sourceIndex: 0 }; - function createSourceMapWriterWorker(host: EmitHost, writer: EmitTextWriter): SourceMapWriter { + export function createSourceMapWriter(host: EmitHost, writer: EmitTextWriter): SourceMapWriter { const compilerOptions = host.getCompilerOptions(); const extendedDiagnostics = compilerOptions.extendedDiagnostics; let currentSourceFile: SourceFile; let currentSourceText: string; let sourceMapDir: string; // The directory in which sourcemap will be - let stopOverridingSpan = false; - let modifyLastSourcePos = false; // Current source map file and its index in the sources list let sourceMapSourceIndex: number; @@ -236,13 +96,7 @@ namespace ts { // Source map data let sourceMapData: SourceMapData; - - // This keeps track of the number of times `disable` has been called without a - // corresponding call to `enable`. As long as this value is non-zero, mappings will not - // be recorded. - // This is primarily used to provide a better experience when debugging binding - // patterns and destructuring assignments for simple expressions. - let disableDepth: number; + let disabled: boolean = !(compilerOptions.sourceMap || compilerOptions.inlineSourceMap); return { initialize, @@ -250,12 +104,8 @@ namespace ts { getSourceMapData: () => sourceMapData, setSourceFile, emitPos, - emitStart, - emitEnd, - emitTokenStart, - emitTokenEnd, - changeEmitSourcePos, - stopOverridingSpan: () => stopOverridingSpan = true, + emitNodeWithSourceMap, + emitTokenWithSourceMap, getText, getSourceMappingURL, }; @@ -269,13 +119,16 @@ namespace ts { * @param isBundledEmit A value indicating whether the generated output file is a bundle. */ function initialize(filePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) { + if (disabled) { + return; + } + if (sourceMapData) { reset(); } currentSourceFile = undefined; currentSourceText = undefined; - disableDepth = 0; // Current source map file and its index in the sources list sourceMapSourceIndex = -1; @@ -338,6 +191,10 @@ namespace ts { * Reset the SourceMapWriter to an empty state. */ function reset() { + if (disabled) { + return; + } + currentSourceFile = undefined; sourceMapDir = undefined; sourceMapSourceIndex = undefined; @@ -345,64 +202,6 @@ namespace ts { lastEncodedSourceMapSpan = undefined; lastEncodedNameIndex = undefined; sourceMapData = undefined; - disableDepth = 0; - } - - /** - * Re-enables the recording of mappings. - */ - function enable() { - if (disableDepth > 0) { - disableDepth--; - } - } - - /** - * Disables the recording of mappings. - */ - function disable() { - disableDepth++; - } - - function updateLastEncodedAndRecordedSpans() { - if (modifyLastSourcePos) { - // Reset the source pos - modifyLastSourcePos = false; - - // Change Last recorded Map with last encoded emit line and character - lastRecordedSourceMapSpan.emittedLine = lastEncodedSourceMapSpan.emittedLine; - lastRecordedSourceMapSpan.emittedColumn = lastEncodedSourceMapSpan.emittedColumn; - - // Pop sourceMapDecodedMappings to remove last entry - sourceMapData.sourceMapDecodedMappings.pop(); - - // Point the lastEncodedSourceMapSpace to the previous encoded sourceMapSpan - // If the list is empty which indicates that we are at the beginning of the file, - // we have to reset it to default value (same value when we first initialize sourceMapWriter) - lastEncodedSourceMapSpan = sourceMapData.sourceMapDecodedMappings.length ? - sourceMapData.sourceMapDecodedMappings[sourceMapData.sourceMapDecodedMappings.length - 1] : - defaultLastEncodedSourceMapSpan; - - // TODO: Update lastEncodedNameIndex - // Since we dont support this any more, lets not worry about it right now. - // When we start supporting nameIndex, we will get back to this - - // Change the encoded source map - const sourceMapMappings = sourceMapData.sourceMapMappings; - let lenthToSet = sourceMapMappings.length - 1; - for (; lenthToSet >= 0; lenthToSet--) { - const currentChar = sourceMapMappings.charAt(lenthToSet); - if (currentChar === ",") { - // Separator for the entry found - break; - } - if (currentChar === ";" && lenthToSet !== 0 && sourceMapMappings.charAt(lenthToSet - 1) !== ";") { - // Last line separator found - break; - } - } - sourceMapData.sourceMapMappings = sourceMapMappings.substr(0, Math.max(0, lenthToSet)); - } } // Encoding for sourcemap span @@ -459,7 +258,7 @@ namespace ts { * @param pos The position. */ function emitPos(pos: number) { - if (positionIsSynthesized(pos) || disableDepth > 0) { + if (disabled || positionIsSynthesized(pos)) { return; } @@ -495,209 +294,89 @@ namespace ts { sourceColumn: sourceLinePos.character, sourceIndex: sourceMapSourceIndex }; - - stopOverridingSpan = false; } - else if (!stopOverridingSpan) { + else { // Take the new pos instead since there is no change in emittedLine and column since last location lastRecordedSourceMapSpan.sourceLine = sourceLinePos.line; lastRecordedSourceMapSpan.sourceColumn = sourceLinePos.character; lastRecordedSourceMapSpan.sourceIndex = sourceMapSourceIndex; } - updateLastEncodedAndRecordedSpans(); - if (extendedDiagnostics) { performance.mark("afterSourcemap"); performance.measure("Source Map", "beforeSourcemap", "afterSourcemap"); } } - function getStartPosPastDecorators(range: TextRange) { - const rangeHasDecorators = !!(range as Node).decorators; - return skipTrivia(currentSourceText, rangeHasDecorators ? (range as Node).decorators.end : range.pos); - } - /** - * Emits a mapping for the start of a range. - * - * If the range's start position is synthetic (undefined or a negative value), no mapping - * will be created. Any trivia at the start position in the original source will be - * skipped. - * - * @param range The range to emit.0 - */ - function emitStart(range: TextRange): void; - /** - * Emits a mapping for the start of a range. - * - * If the node's start position is synthetic (undefined or a negative value), no mapping - * will be created. Any trivia at the start position in the original source will be - * skipped. + * Emits a node with possible leading and trailing source maps. * - * @param range The range to emit. - * @param contextNode The node for the current range. - * @param ignoreNodeCallback A callback used to determine whether to skip source map - * emit for the start position of this node. - * @param ignoreChildrenCallback A callback used to determine whether to skip source - * map emit for all children of this node. - * @param getTextRangeCallbackCallback A callback used to get a custom source map - * range for this node. + * @param node The node to emit. + * @param emitCallback The callback used to emit the node. */ - function emitStart(range: TextRange, contextNode: Node, ignoreNodeCallback: (node: Node) => boolean, ignoreChildrenCallback: (node: Node) => boolean, getTextRangeCallback: (node: Node) => TextRange): void; - function emitStart(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (node: Node) => boolean, ignoreChildrenCallback?: (node: Node) => boolean, getTextRangeCallback?: (node: Node) => TextRange) { - if (contextNode) { - if (!ignoreNodeCallback(contextNode)) { - range = getTextRangeCallback(contextNode) || range; - emitPos(getStartPosPastDecorators(range)); - } - - if (ignoreChildrenCallback(contextNode)) { - disable(); - } - } - else { - emitPos(getStartPosPastDecorators(range)); + function emitNodeWithSourceMap(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) { + if (disabled) { + return emitCallback(emitContext, node); } - } - /** - * Emits a mapping for the end of a range. - * - * If the range's end position is synthetic (undefined or a negative value), no mapping - * will be created. - * - * @param range The range to emit. - */ - function emitEnd(range: TextRange): void; - /** - * Emits a mapping for the end of a range. - * - * If the node's end position is synthetic (undefined or a negative value), no mapping - * will be created. - * - * @param range The range to emit. - * @param contextNode The node for the current range. - * @param ignoreNodeCallback A callback used to determine whether to skip source map - * emit for the end position of this node. - * @param ignoreChildrenCallback A callback used to determine whether to skip source - * map emit for all children of this node. - * @param getTextRangeCallbackCallback A callback used to get a custom source map - * range for this node. - */ - function emitEnd(range: TextRange, contextNode: Node, ignoreNodeCallback: (node: Node) => boolean, ignoreChildrenCallback: (node: Node) => boolean, getTextRangeCallback: (node: Node) => TextRange): void; - function emitEnd(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (node: Node) => boolean, ignoreChildrenCallback?: (node: Node) => boolean, getTextRangeCallback?: (node: Node) => TextRange) { - if (contextNode) { - if (ignoreChildrenCallback(contextNode)) { - enable(); - } + if (node) { + const emitNode = node.emitNode; + const emitFlags = emitNode && emitNode.flags; + const { pos, end } = emitNode && emitNode.sourceMapRange || node; - if (!ignoreNodeCallback(contextNode)) { - range = getTextRangeCallback(contextNode) || range; - emitPos(range.end); + if (node.kind !== SyntaxKind.NotEmittedStatement + && (emitFlags & EmitFlags.NoLeadingSourceMap) === 0 + && pos >= 0) { + emitPos(skipTrivia(currentSourceText, pos)); } - } - else { - emitPos(range.end); - } - - stopOverridingSpan = false; - } - /** - * Emits a mapping for the start position of a token. - * - * If the token's start position is synthetic (undefined or a negative value), no mapping - * will be created. Any trivia at the start position in the original source will be - * skipped. - * - * @param token The token to emit. - * @param tokenStartPos The start position of the token. - * @returns The start position of the token, following any trivia. - */ - function emitTokenStart(token: SyntaxKind, tokenStartPos: number): number; - /** - * Emits a mapping for the start position of a token. - * - * If the token's start position is synthetic (undefined or a negative value), no mapping - * will be created. Any trivia at the start position in the original source will be - * skipped. - * - * @param token The token to emit. - * @param tokenStartPos The start position of the token. - * @param contextNode The node containing this token. - * @param ignoreTokenCallback A callback used to determine whether to skip source map - * emit for the start position of this token. - * @param getTokenTextRangeCallback A callback used to get a custom source - * map range for this node. - * @returns The start position of the token, following any trivia. - */ - function emitTokenStart(token: SyntaxKind, tokenStartPos: number, contextNode: Node, ignoreTokenCallback: (node: Node, token: SyntaxKind) => boolean, getTokenTextRangeCallback: (node: Node, token: SyntaxKind) => TextRange): number; - function emitTokenStart(token: SyntaxKind, tokenStartPos: number, contextNode?: Node, ignoreTokenCallback?: (node: Node, token: SyntaxKind) => boolean, getTokenTextRangeCallback?: (node: Node, token: SyntaxKind) => TextRange): number { - if (contextNode) { - if (ignoreTokenCallback(contextNode, token)) { - return skipTrivia(currentSourceText, tokenStartPos); + if (emitFlags & EmitFlags.NoNestedSourceMaps) { + disabled = true; + emitCallback(emitContext, node); + disabled = false; + } + else { + emitCallback(emitContext, node); } - const range = getTokenTextRangeCallback(contextNode, token); - if (range) { - tokenStartPos = range.pos; + if (node.kind !== SyntaxKind.NotEmittedStatement + && (emitFlags & EmitFlags.NoTrailingSourceMap) === 0 + && end >= 0) { + emitPos(end); } } - - tokenStartPos = skipTrivia(currentSourceText, tokenStartPos); - emitPos(tokenStartPos); - return tokenStartPos; } /** - * Emits a mapping for the end position of a token. - * - * If the token's end position is synthetic (undefined or a negative value), no mapping - * will be created. - * - * @param token The token to emit. - * @param tokenEndPos The end position of the token. - * @returns The end position of the token. - */ - function emitTokenEnd(token: SyntaxKind, tokenEndPos: number): number; - /** - * Emits a mapping for the end position of a token. - * - * If the token's end position is synthetic (undefined or a negative value), no mapping - * will be created. + * Emits a token of a node with possible leading and trailing source maps. * + * @param node The node containing the token. * @param token The token to emit. - * @param tokenEndPos The end position of the token. - * @param contextNode The node containing this token. - * @param ignoreTokenCallback A callback used to determine whether to skip source map - * emit for the end position of this token. - * @param getTokenTextRangeCallback A callback used to get a custom source - * map range for this node. - * @returns The end position of the token. + * @param tokenStartPos The start pos of the token. + * @param emitCallback The callback used to emit the token. */ - function emitTokenEnd(token: SyntaxKind, tokenEndPos: number, contextNode: Node, ignoreTokenCallback: (node: Node, token: SyntaxKind) => boolean, getTokenTextRangeCallback: (node: Node, token: SyntaxKind) => TextRange): number; - function emitTokenEnd(token: SyntaxKind, tokenEndPos: number, contextNode?: Node, ignoreTokenCallback?: (node: Node, token: SyntaxKind) => boolean, getTokenTextRangeCallback?: (node: Node, token: SyntaxKind) => TextRange): number { - if (contextNode) { - if (ignoreTokenCallback(contextNode, token)) { - return tokenEndPos; - } + function emitTokenWithSourceMap(node: Node, token: SyntaxKind, tokenPos: number, emitCallback: (token: SyntaxKind, tokenStartPos: number) => number) { + if (disabled) { + return emitCallback(token, tokenPos); + } - const range = getTokenTextRangeCallback(contextNode, token); - if (range) { - tokenEndPos = range.end; - } + const emitNode = node && node.emitNode; + const emitFlags = emitNode && emitNode.flags; + const range = emitNode && emitNode.tokenSourceMapRanges && emitNode.tokenSourceMapRanges[token]; + + tokenPos = skipTrivia(currentSourceText, range ? range.pos : tokenPos); + if ((emitFlags & EmitFlags.NoTokenLeadingSourceMaps) === 0 && tokenPos >= 0) { + emitPos(tokenPos); } - emitPos(tokenEndPos); - return tokenEndPos; - } + tokenPos = emitCallback(token, tokenPos); + if (range) tokenPos = range.end; + if ((emitFlags & EmitFlags.NoTokenTrailingSourceMaps) === 0 && tokenPos >= 0) { + emitPos(tokenPos); + } - // @deprecated - function changeEmitSourcePos() { - Debug.assert(!modifyLastSourcePos); - modifyLastSourcePos = true; + return tokenPos; } /** @@ -706,6 +385,10 @@ namespace ts { * @param sourceFile The source file. */ function setSourceFile(sourceFile: SourceFile) { + if (disabled) { + return; + } + currentSourceFile = sourceFile; currentSourceText = currentSourceFile.text; @@ -738,6 +421,10 @@ namespace ts { * Gets the text for the source map. */ function getText() { + if (disabled) { + return; + } + encodeLastRecordedSourceMapSpan(); return stringify({ @@ -755,6 +442,10 @@ namespace ts { * Gets the SourceMappingURL for the source map. */ function getSourceMappingURL() { + if (disabled) { + return; + } + if (compilerOptions.inlineSourceMap) { // Encode the sourceMap into the sourceMap url const base64SourceMapText = convertToBase64(getText()); @@ -766,61 +457,6 @@ namespace ts { } } - function createSourceMapWriterWithExtendedDiagnostics(host: EmitHost, writer: EmitTextWriter): SourceMapWriter { - const { - initialize, - reset, - getSourceMapData, - setSourceFile, - emitPos, - emitStart, - emitEnd, - emitTokenStart, - emitTokenEnd, - changeEmitSourcePos, - stopOverridingSpan, - getText, - getSourceMappingURL, - } = createSourceMapWriterWorker(host, writer); - return { - initialize, - reset, - getSourceMapData, - setSourceFile, - emitPos(pos: number): void { - performance.mark("sourcemapStart"); - emitPos(pos); - performance.measure("sourceMapTime", "sourcemapStart"); - }, - emitStart(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (node: Node) => boolean, ignoreChildrenCallback?: (node: Node) => boolean, getTextRangeCallback?: (node: Node) => TextRange): void { - performance.mark("emitSourcemap:emitStart"); - emitStart(range, contextNode, ignoreNodeCallback, ignoreChildrenCallback, getTextRangeCallback); - performance.measure("sourceMapTime", "emitSourcemap:emitStart"); - }, - emitEnd(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (node: Node) => boolean, ignoreChildrenCallback?: (node: Node) => boolean, getTextRangeCallback?: (node: Node) => TextRange): void { - performance.mark("emitSourcemap:emitEnd"); - emitEnd(range, contextNode, ignoreNodeCallback, ignoreChildrenCallback, getTextRangeCallback); - performance.measure("sourceMapTime", "emitSourcemap:emitEnd"); - }, - emitTokenStart(token: SyntaxKind, tokenStartPos: number, contextNode?: Node, ignoreTokenCallback?: (node: Node) => boolean, getTokenTextRangeCallback?: (node: Node, token: SyntaxKind) => TextRange): number { - performance.mark("emitSourcemap:emitTokenStart"); - tokenStartPos = emitTokenStart(token, tokenStartPos, contextNode, ignoreTokenCallback, getTokenTextRangeCallback); - performance.measure("sourceMapTime", "emitSourcemap:emitTokenStart"); - return tokenStartPos; - }, - emitTokenEnd(token: SyntaxKind, tokenEndPos: number, contextNode?: Node, ignoreTokenCallback?: (node: Node) => boolean, getTokenTextRangeCallback?: (node: Node, token: SyntaxKind) => TextRange): number { - performance.mark("emitSourcemap:emitTokenEnd"); - tokenEndPos = emitTokenEnd(token, tokenEndPos, contextNode, ignoreTokenCallback, getTokenTextRangeCallback); - performance.measure("sourceMapTime", "emitSourcemap:emitTokenEnd"); - return tokenEndPos; - }, - changeEmitSourcePos, - stopOverridingSpan, - getText, - getSourceMappingURL, - }; - } - const base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; function base64FormatEncode(inValue: number) { diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index 6242a2f23474c..92407af2bb9fe 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -28,46 +28,25 @@ namespace ts { /** * Gets the transformed source files. */ - getSourceFiles(): SourceFile[]; + transformed: SourceFile[]; /** - * Gets the TextRange to use for source maps for a token of a node. - */ - getTokenSourceMapRange(node: Node, token: SyntaxKind): TextRange; - - /** - * Determines whether expression substitutions are enabled for the provided node. - */ - isSubstitutionEnabled(node: Node): boolean; - - /** - * Determines whether before/after emit notifications should be raised in the pretty - * printer when it emits a node. - */ - isEmitNotificationEnabled(node: Node): boolean; - - /** - * Hook used by transformers to substitute expressions just before they - * are emitted by the pretty printer. + * Emits the substitute for a node, if one is available; otherwise, emits the node. * + * @param emitContext The current emit context. * @param node The node to substitute. - * @param isExpression A value indicating whether the node is in an expression context. + * @param emitCallback A callback used to emit the node or its substitute. */ - onSubstituteNode(node: Node, isExpression: boolean): Node; + emitNodeWithSubstitution(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void; /** - * Hook used to allow transformers to capture state before or after - * the printer emits a node. + * Emits a node with possible notification. * + * @param emitContext The current emit context. * @param node The node to emit. * @param emitCallback A callback used to emit the node. */ - onEmitNode(node: Node, emitCallback: (node: Node) => void): void; - - /** - * Reset transient transformation properties on parse tree nodes. - */ - dispose(): void; + emitNodeWithNotification(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void; } export interface TransformationContext extends LexicalEnvironment { @@ -75,46 +54,6 @@ namespace ts { getEmitResolver(): EmitResolver; getEmitHost(): EmitHost; - /** - * Gets flags used to customize later transformations or emit. - */ - getNodeEmitFlags(node: Node): NodeEmitFlags; - - /** - * Sets flags used to customize later transformations or emit. - */ - setNodeEmitFlags(node: T, flags: NodeEmitFlags): T; - - /** - * Gets the TextRange to use for source maps for the node. - */ - getSourceMapRange(node: Node): TextRange; - - /** - * Sets the TextRange to use for source maps for the node. - */ - setSourceMapRange(node: T, range: TextRange): T; - - /** - * Gets the TextRange to use for source maps for a token of a node. - */ - getTokenSourceMapRange(node: Node, token: SyntaxKind): TextRange; - - /** - * Sets the TextRange to use for source maps for a token of a node. - */ - setTokenSourceMapRange(node: T, token: SyntaxKind, range: TextRange): T; - - /** - * Gets the TextRange to use for comments for the node. - */ - getCommentRange(node: Node): TextRange; - - /** - * Sets the TextRange to use for comments for the node. - */ - setCommentRange(node: T, range: TextRange): T; - /** * Hoists a function declaration to the containing scope. */ @@ -139,7 +78,7 @@ namespace ts { * Hook used by transformers to substitute expressions just before they * are emitted by the pretty printer. */ - onSubstituteNode?: (node: Node, isExpression: boolean) => Node; + onSubstituteNode?: (emitContext: EmitContext, node: Node) => Node; /** * Enables before/after emit notifications in the pretty printer for the provided @@ -157,7 +96,7 @@ namespace ts { * Hook used to allow transformers to capture state before or after * the printer emits a node. */ - onEmitNode?: (node: Node, emit: (node: Node) => void) => void; + onEmitNode?: (emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) => void; } /* @internal */ @@ -186,14 +125,6 @@ namespace ts { return transformers; } - /** - * Tracks a monotonically increasing transformation id used to associate a node with a specific - * transformation. This ensures transient properties related to transformations can be safely - * stored on source tree nodes that may be reused across multiple transformations (such as - * with compile-on-save). - */ - let nextTransformId = 1; - /** * Transforms an array of SourceFiles by passing them through each transformer. * @@ -203,18 +134,10 @@ namespace ts { * @param transforms An array of Transformers. */ export function transformFiles(resolver: EmitResolver, host: EmitHost, sourceFiles: SourceFile[], transformers: Transformer[]): TransformationResult { - const transformId = nextTransformId; - nextTransformId++; - - const tokenSourceMapRanges = createMap(); const lexicalEnvironmentVariableDeclarationsStack: VariableDeclaration[][] = []; const lexicalEnvironmentFunctionDeclarationsStack: FunctionDeclaration[][] = []; const enabledSyntaxKindFeatures = new Array(SyntaxKind.Count); - const parseTreeNodesWithAnnotations: Node[] = []; - let lastTokenSourceMapRangeNode: Node; - let lastTokenSourceMapRangeToken: SyntaxKind; - let lastTokenSourceMapRange: TextRange; let lexicalEnvironmentStackOffset = 0; let hoistedVariableDeclarations: VariableDeclaration[]; let hoistedFunctionDeclarations: FunctionDeclaration[]; @@ -226,22 +149,14 @@ namespace ts { getCompilerOptions: () => host.getCompilerOptions(), getEmitResolver: () => resolver, getEmitHost: () => host, - getNodeEmitFlags, - setNodeEmitFlags, - getSourceMapRange, - setSourceMapRange, - getTokenSourceMapRange, - setTokenSourceMapRange, - getCommentRange, - setCommentRange, hoistVariableDeclaration, hoistFunctionDeclaration, startLexicalEnvironment, endLexicalEnvironment, - onSubstituteNode, + onSubstituteNode: (emitContext, node) => node, enableSubstitution, isSubstitutionEnabled, - onEmitNode, + onEmitNode: (node, emitContext, emitCallback) => emitCallback(node, emitContext), enableEmitNotification, isEmitNotificationEnabled }; @@ -256,30 +171,9 @@ namespace ts { lexicalEnvironmentDisabled = true; return { - getSourceFiles: () => transformed, - getTokenSourceMapRange, - isSubstitutionEnabled, - isEmitNotificationEnabled, - onSubstituteNode: context.onSubstituteNode, - onEmitNode: context.onEmitNode, - dispose() { - // During transformation we may need to annotate a parse tree node with transient - // transformation properties. As parse tree nodes live longer than transformation - // nodes, we need to make sure we reclaim any memory allocated for custom ranges - // from these nodes to ensure we do not hold onto entire subtrees just for position - // information. We also need to reset these nodes to a pre-transformation state - // for incremental parsing scenarios so that we do not impact later emit. - for (const node of parseTreeNodesWithAnnotations) { - if (node.transformId === transformId) { - node.transformId = 0; - node.emitFlags = 0; - node.commentRange = undefined; - node.sourceMapRange = undefined; - } - } - - parseTreeNodesWithAnnotations.length = 0; - } + transformed, + emitNodeWithSubstitution, + emitNodeWithNotification }; /** @@ -306,18 +200,29 @@ namespace ts { * Determines whether expression substitutions are enabled for the provided node. */ function isSubstitutionEnabled(node: Node) { - return (enabledSyntaxKindFeatures[node.kind] & SyntaxKindFeatureFlags.Substitution) !== 0; + return (enabledSyntaxKindFeatures[node.kind] & SyntaxKindFeatureFlags.Substitution) !== 0 + && (getEmitFlags(node) & EmitFlags.NoSubstitution) === 0; } /** - * Default hook for node substitutions. + * Emits a node with possible substitution. * - * @param node The node to substitute. - * @param isExpression A value indicating whether the node is to be used in an expression - * position. - */ - function onSubstituteNode(node: Node, isExpression: boolean) { - return node; + * @param emitContext The current emit context. + * @param node The node to emit. + * @param emitCallback The callback used to emit the node or its substitute. + */ + function emitNodeWithSubstitution(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) { + if (node) { + if (isSubstitutionEnabled(node)) { + const substitute = context.onSubstituteNode(emitContext, node); + if (substitute && substitute !== node) { + emitCallback(emitContext, substitute); + return; + } + } + + emitCallback(emitContext, node); + } } /** @@ -333,154 +238,25 @@ namespace ts { */ function isEmitNotificationEnabled(node: Node) { return (enabledSyntaxKindFeatures[node.kind] & SyntaxKindFeatureFlags.EmitNotifications) !== 0 - || (getNodeEmitFlags(node) & NodeEmitFlags.AdviseOnEmitNode) !== 0; + || (getEmitFlags(node) & EmitFlags.AdviseOnEmitNode) !== 0; } /** - * Default hook for node emit. + * Emits a node with possible emit notification. * + * @param emitContext The current emit context. * @param node The node to emit. - * @param emit A callback used to emit the node in the printer. + * @param emitCallback The callback used to emit the node. */ - function onEmitNode(node: Node, emit: (node: Node) => void) { - emit(node); - } - - /** - * Associates a node with the current transformation, initializing - * various transient transformation properties. - * - * @param node The node. - */ - function beforeSetAnnotation(node: Node) { - if ((node.flags & NodeFlags.Synthesized) === 0 && node.transformId !== transformId) { - // To avoid holding onto transformation artifacts, we keep track of any - // parse tree node we are annotating. This allows us to clean them up after - // all transformations have completed. - parseTreeNodesWithAnnotations.push(node); - node.transformId = transformId; - } - } - - /** - * Gets flags that control emit behavior of a node. - * - * If the node does not have its own NodeEmitFlags set, the node emit flags of its - * original pointer are used. - * - * @param node The node. - */ - function getNodeEmitFlags(node: Node) { - return node.emitFlags; - } - - /** - * Sets flags that control emit behavior of a node. - * - * @param node The node. - * @param emitFlags The NodeEmitFlags for the node. - */ - function setNodeEmitFlags(node: T, emitFlags: NodeEmitFlags) { - beforeSetAnnotation(node); - node.emitFlags = emitFlags; - return node; - } - - /** - * Gets a custom text range to use when emitting source maps. - * - * If a node does not have its own custom source map text range, the custom source map - * text range of its original pointer is used. - * - * @param node The node. - */ - function getSourceMapRange(node: Node) { - return node.sourceMapRange || node; - } - - /** - * Sets a custom text range to use when emitting source maps. - * - * @param node The node. - * @param range The text range. - */ - function setSourceMapRange(node: T, range: TextRange) { - beforeSetAnnotation(node); - node.sourceMapRange = range; - return node; - } - - /** - * Gets the TextRange to use for source maps for a token of a node. - * - * If a node does not have its own custom source map text range for a token, the custom - * source map text range for the token of its original pointer is used. - * - * @param node The node. - * @param token The token. - */ - function getTokenSourceMapRange(node: Node, token: SyntaxKind) { - // As a performance optimization, use the cached value of the most recent node. - // This helps for cases where this function is called repeatedly for the same node. - if (lastTokenSourceMapRangeNode === node && lastTokenSourceMapRangeToken === token) { - return lastTokenSourceMapRange; - } - - // Get the custom token source map range for a node or from one of its original nodes. - // Custom token ranges are not stored on the node to avoid the GC burden. - let range: TextRange; - let current = node; - while (current) { - range = current.id ? tokenSourceMapRanges[current.id + "-" + token] : undefined; - if (range !== undefined) { - break; + function emitNodeWithNotification(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) { + if (node) { + if (isEmitNotificationEnabled(node)) { + context.onEmitNode(emitContext, node, emitCallback); + } + else { + emitCallback(emitContext, node); } - - current = current.original; } - - // Cache the most recently requested value. - lastTokenSourceMapRangeNode = node; - lastTokenSourceMapRangeToken = token; - lastTokenSourceMapRange = range; - return range; - } - - /** - * Sets the TextRange to use for source maps for a token of a node. - * - * @param node The node. - * @param token The token. - * @param range The text range. - */ - function setTokenSourceMapRange(node: T, token: SyntaxKind, range: TextRange) { - // Cache the most recently requested value. - lastTokenSourceMapRangeNode = node; - lastTokenSourceMapRangeToken = token; - lastTokenSourceMapRange = range; - tokenSourceMapRanges[getNodeId(node) + "-" + token] = range; - return node; - } - - /** - * Gets a custom text range to use when emitting comments. - * - * If a node does not have its own custom source map text range, the custom source map - * text range of its original pointer is used. - * - * @param node The node. - */ - function getCommentRange(node: Node) { - return node.commentRange || node; - } - - /** - * Sets a custom text range to use when emitting comments. - */ - function setCommentRange(node: T, range: TextRange) { - beforeSetAnnotation(node); - node.commentRange = range; - return node; } /** @@ -563,70 +339,4 @@ namespace ts { return statements; } } - - /** - * High-order function, creates a function that executes a function composition. - * For example, `chain(a, b)` is the equivalent of `x => ((a', b') => y => b'(a'(y)))(a(x), b(x))` - * - * @param args The functions to chain. - */ - function chain(...args: ((t: T) => (u: U) => U)[]): (t: T) => (u: U) => U; - function chain(a: (t: T) => (u: U) => U, b: (t: T) => (u: U) => U, c: (t: T) => (u: U) => U, d: (t: T) => (u: U) => U, e: (t: T) => (u: U) => U): (t: T) => (u: U) => U { - if (e) { - const args: ((t: T) => (u: U) => U)[] = []; - for (let i = 0; i < arguments.length; i++) { - args[i] = arguments[i]; - } - - return t => compose(...map(args, f => f(t))); - } - else if (d) { - return t => compose(a(t), b(t), c(t), d(t)); - } - else if (c) { - return t => compose(a(t), b(t), c(t)); - } - else if (b) { - return t => compose(a(t), b(t)); - } - else if (a) { - return t => compose(a(t)); - } - else { - return t => u => u; - } - } - - /** - * High-order function, composes functions. Note that functions are composed inside-out; - * for example, `compose(a, b)` is the equivalent of `x => b(a(x))`. - * - * @param args The functions to compose. - */ - function compose(...args: ((t: T) => T)[]): (t: T) => T; - function compose(a: (t: T) => T, b: (t: T) => T, c: (t: T) => T, d: (t: T) => T, e: (t: T) => T): (t: T) => T { - if (e) { - const args: ((t: T) => T)[] = []; - for (let i = 0; i < arguments.length; i++) { - args[i] = arguments[i]; - } - - return t => reduceLeft<(t: T) => T, T>(args, (u, f) => f(u), t); - } - else if (d) { - return t => d(c(b(a(t)))); - } - else if (c) { - return t => c(b(a(t))); - } - else if (b) { - return t => b(a(t)); - } - else if (a) { - return t => a(t); - } - else { - return t => t; - } - } } \ No newline at end of file diff --git a/src/compiler/transformers/destructuring.ts b/src/compiler/transformers/destructuring.ts index a66f5e21d784d..3bfa778cf9b33 100644 --- a/src/compiler/transformers/destructuring.ts +++ b/src/compiler/transformers/destructuring.ts @@ -66,7 +66,7 @@ namespace ts { // NOTE: this completely disables source maps, but aligns with the behavior of // `emitAssignment` in the old emitter. - context.setNodeEmitFlags(expression, NodeEmitFlags.NoNestedSourceMaps); + setEmitFlags(expression, EmitFlags.NoNestedSourceMaps); aggregateTransformFlags(expression); expressions.push(expression); @@ -102,7 +102,7 @@ namespace ts { // NOTE: this completely disables source maps, but aligns with the behavior of // `emitAssignment` in the old emitter. - context.setNodeEmitFlags(declaration, NodeEmitFlags.NoNestedSourceMaps); + setEmitFlags(declaration, EmitFlags.NoNestedSourceMaps); aggregateTransformFlags(declaration); declarations.push(declaration); @@ -147,7 +147,7 @@ namespace ts { // NOTE: this completely disables source maps, but aligns with the behavior of // `emitAssignment` in the old emitter. - context.setNodeEmitFlags(declaration, NodeEmitFlags.NoNestedSourceMaps); + setEmitFlags(declaration, EmitFlags.NoNestedSourceMaps); declarations.push(declaration); aggregateTransformFlags(declaration); @@ -211,7 +211,7 @@ namespace ts { // NOTE: this completely disables source maps, but aligns with the behavior of // `emitAssignment` in the old emitter. - context.setNodeEmitFlags(expression, NodeEmitFlags.NoNestedSourceMaps); + setEmitFlags(expression, EmitFlags.NoNestedSourceMaps); pendingAssignments.push(expression); return expression; @@ -271,8 +271,8 @@ namespace ts { } else { const name = getMutableClone(target); - context.setSourceMapRange(name, target); - context.setCommentRange(name, target); + setSourceMapRange(name, target); + setCommentRange(name, target); emitAssignment(name, value, location, /*original*/ undefined); } } diff --git a/src/compiler/transformers/es6.ts b/src/compiler/transformers/es6.ts index e8ba800e19062..4a6cbd0943443 100644 --- a/src/compiler/transformers/es6.ts +++ b/src/compiler/transformers/es6.ts @@ -144,13 +144,6 @@ namespace ts { startLexicalEnvironment, endLexicalEnvironment, hoistVariableDeclaration, - getNodeEmitFlags, - setNodeEmitFlags, - getCommentRange, - setCommentRange, - getSourceMapRange, - setSourceMapRange, - setTokenSourceMapRange, } = context; const resolver = context.getEmitResolver(); @@ -185,6 +178,10 @@ namespace ts { return transformSourceFile; function transformSourceFile(node: SourceFile) { + if (isDeclarationFile(node)) { + return node; + } + currentSourceFile = node; currentText = node.text; return visitNode(node, visitor, isSourceFile); @@ -408,7 +405,7 @@ namespace ts { enclosingFunction = currentNode; if (currentNode.kind !== SyntaxKind.ArrowFunction) { enclosingNonArrowFunction = currentNode; - if (!(currentNode.emitFlags & NodeEmitFlags.AsyncFunctionBody)) { + if (!(getEmitFlags(currentNode) & EmitFlags.AsyncFunctionBody)) { enclosingNonAsyncFunctionBody = currentNode; } } @@ -671,19 +668,19 @@ namespace ts { // To preserve the behavior of the old emitter, we explicitly indent // the body of the function here if it was requested in an earlier // transformation. - if (getNodeEmitFlags(node) & NodeEmitFlags.Indented) { - setNodeEmitFlags(classFunction, NodeEmitFlags.Indented); + if (getEmitFlags(node) & EmitFlags.Indented) { + setEmitFlags(classFunction, EmitFlags.Indented); } // "inner" and "outer" below are added purely to preserve source map locations from // the old emitter const inner = createPartiallyEmittedExpression(classFunction); inner.end = node.end; - setNodeEmitFlags(inner, NodeEmitFlags.NoComments); + setEmitFlags(inner, EmitFlags.NoComments); const outer = createPartiallyEmittedExpression(inner); outer.end = skipTrivia(currentText, node.pos); - setNodeEmitFlags(outer, NodeEmitFlags.NoComments); + setEmitFlags(outer, EmitFlags.NoComments); return createParen( createCall( @@ -717,17 +714,17 @@ namespace ts { // emit with the original emitter. const outer = createPartiallyEmittedExpression(localName); outer.end = closingBraceLocation.end; - setNodeEmitFlags(outer, NodeEmitFlags.NoComments); + setEmitFlags(outer, EmitFlags.NoComments); const statement = createReturn(outer); statement.pos = closingBraceLocation.pos; - setNodeEmitFlags(statement, NodeEmitFlags.NoComments | NodeEmitFlags.NoTokenSourceMaps); + setEmitFlags(statement, EmitFlags.NoComments | EmitFlags.NoTokenSourceMaps); statements.push(statement); addRange(statements, endLexicalEnvironment()); const block = createBlock(createNodeArray(statements, /*location*/ node.members), /*location*/ undefined, /*multiLine*/ true); - setNodeEmitFlags(block, NodeEmitFlags.NoComments); + setEmitFlags(block, EmitFlags.NoComments); return block; } @@ -830,7 +827,7 @@ namespace ts { ); if (!constructor) { - setNodeEmitFlags(block, NodeEmitFlags.NoComments); + setEmitFlags(block, EmitFlags.NoComments); } return block; @@ -967,27 +964,27 @@ namespace ts { // of an initializer, we must emit that expression to preserve side effects. if (name.elements.length > 0) { statements.push( - setNodeEmitFlags( + setEmitFlags( createVariableStatement( /*modifiers*/ undefined, createVariableDeclarationList( flattenParameterDestructuring(context, parameter, temp, visitor) ) ), - NodeEmitFlags.CustomPrologue + EmitFlags.CustomPrologue ) ); } else if (initializer) { statements.push( - setNodeEmitFlags( + setEmitFlags( createStatement( createAssignment( temp, visitNode(initializer, visitor, isExpression) ) ), - NodeEmitFlags.CustomPrologue + EmitFlags.CustomPrologue ) ); } @@ -1008,23 +1005,23 @@ namespace ts { getSynthesizedClone(name), createVoidZero() ), - setNodeEmitFlags( + setEmitFlags( createBlock([ createStatement( createAssignment( - setNodeEmitFlags(getMutableClone(name), NodeEmitFlags.NoSourceMap), - setNodeEmitFlags(initializer, NodeEmitFlags.NoSourceMap | getNodeEmitFlags(initializer)), + setEmitFlags(getMutableClone(name), EmitFlags.NoSourceMap), + setEmitFlags(initializer, EmitFlags.NoSourceMap | getEmitFlags(initializer)), /*location*/ parameter ) ) ], /*location*/ parameter), - NodeEmitFlags.SingleLine | NodeEmitFlags.NoTrailingSourceMap | NodeEmitFlags.NoTokenSourceMaps + EmitFlags.SingleLine | EmitFlags.NoTrailingSourceMap | EmitFlags.NoTokenSourceMaps ), /*elseStatement*/ undefined, /*location*/ parameter ); statement.startsOnNewLine = true; - setNodeEmitFlags(statement, NodeEmitFlags.NoTokenSourceMaps | NodeEmitFlags.NoTrailingSourceMap | NodeEmitFlags.CustomPrologue); + setEmitFlags(statement, EmitFlags.NoTokenSourceMaps | EmitFlags.NoTrailingSourceMap | EmitFlags.CustomPrologue); statements.push(statement); } @@ -1057,7 +1054,7 @@ namespace ts { // `declarationName` is the name of the local declaration for the parameter. const declarationName = getMutableClone(parameter.name); - setNodeEmitFlags(declarationName, NodeEmitFlags.NoSourceMap); + setEmitFlags(declarationName, EmitFlags.NoSourceMap); // `expressionName` is the name of the parameter used in expressions. const expressionName = getSynthesizedClone(parameter.name); @@ -1066,7 +1063,7 @@ namespace ts { // var param = []; statements.push( - setNodeEmitFlags( + setEmitFlags( createVariableStatement( /*modifiers*/ undefined, createVariableDeclarationList([ @@ -1078,7 +1075,7 @@ namespace ts { ]), /*location*/ parameter ), - NodeEmitFlags.CustomPrologue + EmitFlags.CustomPrologue ) ); @@ -1111,7 +1108,7 @@ namespace ts { ]) ); - setNodeEmitFlags(forStatement, NodeEmitFlags.CustomPrologue); + setEmitFlags(forStatement, EmitFlags.CustomPrologue); startOnNewLine(forStatement); statements.push(forStatement); } @@ -1136,7 +1133,7 @@ namespace ts { ]) ); - setNodeEmitFlags(captureThisStatement, NodeEmitFlags.NoComments | NodeEmitFlags.CustomPrologue); + setEmitFlags(captureThisStatement, EmitFlags.NoComments | EmitFlags.CustomPrologue); setSourceMapRange(captureThisStatement, node); statements.push(captureThisStatement); } @@ -1200,7 +1197,7 @@ namespace ts { const sourceMapRange = getSourceMapRange(member); const func = transformFunctionLikeToExpression(member, /*location*/ member, /*name*/ undefined); - setNodeEmitFlags(func, NodeEmitFlags.NoComments); + setEmitFlags(func, EmitFlags.NoComments); setSourceMapRange(func, sourceMapRange); const statement = createStatement( @@ -1221,7 +1218,7 @@ namespace ts { // The location for the statement is used to emit comments only. // No source map should be emitted for this statement to align with the // old emitter. - setNodeEmitFlags(statement, NodeEmitFlags.NoSourceMap); + setEmitFlags(statement, EmitFlags.NoSourceMap); return statement; } @@ -1240,7 +1237,7 @@ namespace ts { // The location for the statement is used to emit source maps only. // No comments should be emitted for this statement to align with the // old emitter. - setNodeEmitFlags(statement, NodeEmitFlags.NoComments); + setEmitFlags(statement, EmitFlags.NoComments); return statement; } @@ -1254,11 +1251,11 @@ namespace ts { // To align with source maps in the old emitter, the receiver and property name // arguments are both mapped contiguously to the accessor name. const target = getMutableClone(receiver); - setNodeEmitFlags(target, NodeEmitFlags.NoComments | NodeEmitFlags.NoTrailingSourceMap); + setEmitFlags(target, EmitFlags.NoComments | EmitFlags.NoTrailingSourceMap); setSourceMapRange(target, firstAccessor.name); const propertyName = createExpressionForPropertyName(visitNode(firstAccessor.name, visitor, isPropertyName)); - setNodeEmitFlags(propertyName, NodeEmitFlags.NoComments | NodeEmitFlags.NoLeadingSourceMap); + setEmitFlags(propertyName, EmitFlags.NoComments | EmitFlags.NoLeadingSourceMap); setSourceMapRange(propertyName, firstAccessor.name); const properties: ObjectLiteralElementLike[] = []; @@ -1309,7 +1306,7 @@ namespace ts { } const func = transformFunctionLikeToExpression(node, /*location*/ node, /*name*/ undefined); - setNodeEmitFlags(func, NodeEmitFlags.CapturesThis); + setEmitFlags(func, EmitFlags.CapturesThis); return func; } @@ -1434,7 +1431,7 @@ namespace ts { const expression = visitNode(body, visitor, isExpression); const returnStatement = createReturn(expression, /*location*/ body); - setNodeEmitFlags(returnStatement, NodeEmitFlags.NoTokenSourceMaps | NodeEmitFlags.NoTrailingSourceMap | NodeEmitFlags.NoTrailingComments); + setEmitFlags(returnStatement, EmitFlags.NoTokenSourceMaps | EmitFlags.NoTrailingSourceMap | EmitFlags.NoTrailingComments); statements.push(returnStatement); // To align with the source map emit for the old emitter, we set a custom @@ -1452,7 +1449,7 @@ namespace ts { const block = createBlock(createNodeArray(statements, statementsLocation), node.body, multiLine); if (!multiLine && singleLine) { - setNodeEmitFlags(block, NodeEmitFlags.SingleLine); + setEmitFlags(block, EmitFlags.SingleLine); } if (closeBraceLocation) { @@ -1875,7 +1872,7 @@ namespace ts { } // The old emitter does not emit source maps for the expression - setNodeEmitFlags(expression, NodeEmitFlags.NoSourceMap | getNodeEmitFlags(expression)); + setEmitFlags(expression, EmitFlags.NoSourceMap | getEmitFlags(expression)); // The old emitter does not emit source maps for the block. // We add the location to preserve comments. @@ -1884,7 +1881,7 @@ namespace ts { /*location*/ bodyLocation ); - setNodeEmitFlags(body, NodeEmitFlags.NoSourceMap | NodeEmitFlags.NoTokenSourceMaps); + setEmitFlags(body, EmitFlags.NoSourceMap | EmitFlags.NoTokenSourceMaps); const forStatement = createFor( createVariableDeclarationList([ @@ -1902,7 +1899,7 @@ namespace ts { ); // Disable trailing source maps for the OpenParenToken to align source map emit with the old emitter. - setNodeEmitFlags(forStatement, NodeEmitFlags.NoTokenTrailingSourceMaps); + setEmitFlags(forStatement, EmitFlags.NoTokenTrailingSourceMaps); return forStatement; } @@ -1938,13 +1935,13 @@ namespace ts { const expressions: Expression[] = []; const assignment = createAssignment( temp, - setNodeEmitFlags( + setEmitFlags( createObjectLiteral( visitNodes(properties, visitor, isObjectLiteralElementLike, 0, numInitialProperties), /*location*/ undefined, node.multiLine ), - NodeEmitFlags.Indented + EmitFlags.Indented ) ); if (node.multiLine) { @@ -2067,16 +2064,16 @@ namespace ts { const isAsyncBlockContainingAwait = enclosingNonArrowFunction - && (enclosingNonArrowFunction.emitFlags & NodeEmitFlags.AsyncFunctionBody) !== 0 + && (getEmitFlags(enclosingNonArrowFunction) & EmitFlags.AsyncFunctionBody) !== 0 && (node.statement.transformFlags & TransformFlags.ContainsYield) !== 0; - let loopBodyFlags: NodeEmitFlags = 0; + let loopBodyFlags: EmitFlags = 0; if (currentState.containsLexicalThis) { - loopBodyFlags |= NodeEmitFlags.CapturesThis; + loopBodyFlags |= EmitFlags.CapturesThis; } if (isAsyncBlockContainingAwait) { - loopBodyFlags |= NodeEmitFlags.AsyncFunctionBody; + loopBodyFlags |= EmitFlags.AsyncFunctionBody; } const convertedLoopVariable = @@ -2087,7 +2084,7 @@ namespace ts { createVariableDeclaration( functionName, /*type*/ undefined, - setNodeEmitFlags( + setEmitFlags( createFunctionExpression( isAsyncBlockContainingAwait ? createToken(SyntaxKind.AsteriskToken) : undefined, /*name*/ undefined, @@ -2480,7 +2477,7 @@ namespace ts { // Methods with computed property names are handled in visitObjectLiteralExpression. Debug.assert(!isComputedPropertyName(node.name)); const functionExpression = transformFunctionLikeToExpression(node, /*location*/ moveRangePos(node, -1), /*name*/ undefined); - setNodeEmitFlags(functionExpression, NodeEmitFlags.NoLeadingComments | getNodeEmitFlags(functionExpression)); + setEmitFlags(functionExpression, EmitFlags.NoLeadingComments | getEmitFlags(functionExpression)); return createPropertyAssignment( node.name, functionExpression, @@ -2855,7 +2852,7 @@ namespace ts { * * @param node The node to be printed. */ - function onEmitNode(node: Node, emit: (node: Node) => void) { + function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) { const savedEnclosingFunction = enclosingFunction; if (enabledSubstitutions & ES6SubstitutionFlags.CapturedThis && isFunctionLike(node)) { @@ -2863,7 +2860,7 @@ namespace ts { enclosingFunction = node; } - previousOnEmitNode(node, emit); + previousOnEmitNode(emitContext, node, emitCallback); enclosingFunction = savedEnclosingFunction; } @@ -2904,10 +2901,10 @@ namespace ts { * @param isExpression A value indicating whether the node is to be used in an expression * position. */ - function onSubstituteNode(node: Node, isExpression: boolean) { - node = previousOnSubstituteNode(node, isExpression); + function onSubstituteNode(emitContext: EmitContext, node: Node) { + node = previousOnSubstituteNode(emitContext, node); - if (isExpression) { + if (emitContext === EmitContext.Expression) { return substituteExpression(node); } @@ -2995,7 +2992,7 @@ namespace ts { function substituteThisKeyword(node: PrimaryExpression): PrimaryExpression { if (enabledSubstitutions & ES6SubstitutionFlags.CapturedThis && enclosingFunction - && enclosingFunction.emitFlags & NodeEmitFlags.CapturesThis) { + && getEmitFlags(enclosingFunction) & EmitFlags.CapturesThis) { return createIdentifier("_this", /*location*/ node); } @@ -3013,7 +3010,7 @@ namespace ts { * @param allowSourceMaps A value indicating whether source maps may be emitted for the name. */ function getLocalName(node: ClassDeclaration | ClassExpression | FunctionDeclaration, allowComments?: boolean, allowSourceMaps?: boolean) { - return getDeclarationName(node, allowComments, allowSourceMaps, NodeEmitFlags.LocalName); + return getDeclarationName(node, allowComments, allowSourceMaps, EmitFlags.LocalName); } /** @@ -3022,18 +3019,18 @@ namespace ts { * @param node The declaration. * @param allowComments Allow comments for the name. */ - function getDeclarationName(node: DeclarationStatement | ClassExpression, allowComments?: boolean, allowSourceMaps?: boolean, emitFlags?: NodeEmitFlags) { + function getDeclarationName(node: DeclarationStatement | ClassExpression, allowComments?: boolean, allowSourceMaps?: boolean, emitFlags?: EmitFlags) { if (node.name && !isGeneratedIdentifier(node.name)) { const name = getMutableClone(node.name); - emitFlags |= getNodeEmitFlags(node.name); + emitFlags |= getEmitFlags(node.name); if (!allowSourceMaps) { - emitFlags |= NodeEmitFlags.NoSourceMap; + emitFlags |= EmitFlags.NoSourceMap; } if (!allowComments) { - emitFlags |= NodeEmitFlags.NoComments; + emitFlags |= EmitFlags.NoComments; } if (emitFlags) { - setNodeEmitFlags(name, emitFlags); + setEmitFlags(name, emitFlags); } return name; } diff --git a/src/compiler/transformers/es7.ts b/src/compiler/transformers/es7.ts index 5d848bc608bad..4d5e96134c40d 100644 --- a/src/compiler/transformers/es7.ts +++ b/src/compiler/transformers/es7.ts @@ -9,6 +9,10 @@ namespace ts { return transformSourceFile; function transformSourceFile(node: SourceFile) { + if (isDeclarationFile(node)) { + return node; + } + return visitEachChild(node, visitor, context); } diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts index 5f142b6abe96e..6fe3187bee184 100644 --- a/src/compiler/transformers/generators.ts +++ b/src/compiler/transformers/generators.ts @@ -231,9 +231,6 @@ namespace ts { endLexicalEnvironment, hoistFunctionDeclaration, hoistVariableDeclaration, - setSourceMapRange, - setCommentRange, - setNodeEmitFlags } = context; const compilerOptions = context.getCompilerOptions(); @@ -294,6 +291,10 @@ namespace ts { return transformSourceFile; function transformSourceFile(node: SourceFile) { + if (isDeclarationFile(node)) { + return node; + } + if (node.transformFlags & TransformFlags.ContainsGenerator) { currentSourceFile = node; node = visitEachChild(node, visitor, context); @@ -444,7 +445,7 @@ namespace ts { */ function visitFunctionDeclaration(node: FunctionDeclaration): Statement { // Currently, we only support generators that were originally async functions. - if (node.asteriskToken && node.emitFlags & NodeEmitFlags.AsyncFunctionBody) { + if (node.asteriskToken && getEmitFlags(node) & EmitFlags.AsyncFunctionBody) { node = setOriginalNode( createFunctionDeclaration( /*decorators*/ undefined, @@ -492,7 +493,7 @@ namespace ts { */ function visitFunctionExpression(node: FunctionExpression): Expression { // Currently, we only support generators that were originally async functions. - if (node.asteriskToken && node.emitFlags & NodeEmitFlags.AsyncFunctionBody) { + if (node.asteriskToken && getEmitFlags(node) & EmitFlags.AsyncFunctionBody) { node = setOriginalNode( createFunctionExpression( /*asteriskToken*/ undefined, @@ -616,7 +617,7 @@ namespace ts { } else { // Do not hoist custom prologues. - if (node.emitFlags & NodeEmitFlags.CustomPrologue) { + if (getEmitFlags(node) & EmitFlags.CustomPrologue) { return node; } @@ -1887,9 +1888,9 @@ namespace ts { return -1; } - function onSubstituteNode(node: Node, isExpression: boolean): Node { - node = previousOnSubstituteNode(node, isExpression); - if (isExpression) { + function onSubstituteNode(emitContext: EmitContext, node: Node): Node { + node = previousOnSubstituteNode(emitContext, node); + if (emitContext === EmitContext.Expression) { return substituteExpression(node); } return node; @@ -2585,7 +2586,7 @@ namespace ts { /*typeArguments*/ undefined, [ createThis(), - setNodeEmitFlags( + setEmitFlags( createFunctionExpression( /*asteriskToken*/ undefined, /*name*/ undefined, @@ -2598,7 +2599,7 @@ namespace ts { /*multiLine*/ buildResult.length > 0 ) ), - NodeEmitFlags.ReuseTempVariableScope + EmitFlags.ReuseTempVariableScope ) ] ); diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index 9e6aa507cce5a..95a4016bb0aeb 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -16,6 +16,10 @@ namespace ts { * @param node A SourceFile node. */ function transformSourceFile(node: SourceFile) { + if (isDeclarationFile(node)) { + return node; + } + currentSourceFile = node; node = visitEachChild(node, visitor, context); currentSourceFile = undefined; diff --git a/src/compiler/transformers/module/es6.ts b/src/compiler/transformers/module/es6.ts index 2716165f2d398..09a2890727c58 100644 --- a/src/compiler/transformers/module/es6.ts +++ b/src/compiler/transformers/module/es6.ts @@ -12,6 +12,10 @@ namespace ts { return transformSourceFile; function transformSourceFile(node: SourceFile) { + if (isDeclarationFile(node)) { + return node; + } + if (isExternalModule(node) || compilerOptions.isolatedModules) { currentSourceFile = node; return visitEachChild(node, visitor, context); diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index 53004da2c171e..c4e9b4c31c802 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -15,9 +15,6 @@ namespace ts { startLexicalEnvironment, endLexicalEnvironment, hoistVariableDeclaration, - setNodeEmitFlags, - getNodeEmitFlags, - setSourceMapRange, } = context; const compilerOptions = context.getCompilerOptions(); @@ -54,6 +51,10 @@ namespace ts { * @param node The SourceFile node. */ function transformSourceFile(node: SourceFile) { + if (isDeclarationFile(node)) { + return node; + } + if (isExternalModule(node) || compilerOptions.isolatedModules) { currentSourceFile = node; @@ -92,7 +93,7 @@ namespace ts { const updated = updateSourceFile(node, statements); if (hasExportStarsToExportValues) { - setNodeEmitFlags(updated, NodeEmitFlags.EmitExportStar | getNodeEmitFlags(node)); + setEmitFlags(updated, EmitFlags.EmitExportStar | getEmitFlags(node)); } return updated; @@ -116,7 +117,7 @@ namespace ts { */ function transformUMDModule(node: SourceFile) { const define = createIdentifier("define"); - setNodeEmitFlags(define, NodeEmitFlags.UMDDefine); + setEmitFlags(define, EmitFlags.UMDDefine); return transformAsynchronousModule(node, define, /*moduleName*/ undefined, /*includeNonAmdDependencies*/ false); } @@ -220,7 +221,7 @@ namespace ts { if (hasExportStarsToExportValues) { // If we have any `export * from ...` declarations // we need to inform the emitter to add the __export helper. - setNodeEmitFlags(body, NodeEmitFlags.EmitExportStar); + setEmitFlags(body, EmitFlags.EmitExportStar); } return body; @@ -234,7 +235,7 @@ namespace ts { /*location*/ exportEquals ); - setNodeEmitFlags(statement, NodeEmitFlags.NoTokenSourceMaps | NodeEmitFlags.NoComments); + setEmitFlags(statement, EmitFlags.NoTokenSourceMaps | EmitFlags.NoComments); statements.push(statement); } else { @@ -249,7 +250,7 @@ namespace ts { /*location*/ exportEquals ); - setNodeEmitFlags(statement, NodeEmitFlags.NoComments); + setEmitFlags(statement, EmitFlags.NoComments); statements.push(statement); } } @@ -388,7 +389,7 @@ namespace ts { // Set emitFlags on the name of the importEqualsDeclaration // This is so the printer will not substitute the identifier - setNodeEmitFlags(node.name, NodeEmitFlags.NoSubstitution); + setEmitFlags(node.name, EmitFlags.NoSubstitution); const statements: Statement[] = []; if (moduleKind !== ModuleKind.AMD) { if (hasModifier(node, ModifierFlags.Export)) { @@ -598,7 +599,7 @@ namespace ts { } else { statements.push( - createExportStatement(node.name, setNodeEmitFlags(getSynthesizedClone(node.name), NodeEmitFlags.LocalName), /*location*/ node) + createExportStatement(node.name, setEmitFlags(getSynthesizedClone(node.name), EmitFlags.LocalName), /*location*/ node) ); } } @@ -813,7 +814,7 @@ namespace ts { )], /*location*/ node ); - setNodeEmitFlags(transformedStatement, NodeEmitFlags.NoComments); + setEmitFlags(transformedStatement, EmitFlags.NoComments); statements.push(transformedStatement); } @@ -821,14 +822,14 @@ namespace ts { return node.name ? getSynthesizedClone(node.name) : getGeneratedNameForNode(node); } - function onEmitNode(node: Node, emit: (node: Node) => void): void { + function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void { if (node.kind === SyntaxKind.SourceFile) { bindingNameExportSpecifiersMap = bindingNameExportSpecifiersForFileMap[getOriginalNodeId(node)]; - previousOnEmitNode(node, emit); + previousOnEmitNode(emitContext, node, emitCallback); bindingNameExportSpecifiersMap = undefined; } else { - previousOnEmitNode(node, emit); + previousOnEmitNode(emitContext, node, emitCallback); } } @@ -839,9 +840,9 @@ namespace ts { * @param isExpression A value indicating whether the node is to be used in an expression * position. */ - function onSubstituteNode(node: Node, isExpression: boolean) { - node = previousOnSubstituteNode(node, isExpression); - if (isExpression) { + function onSubstituteNode(emitContext: EmitContext, node: Node) { + node = previousOnSubstituteNode(emitContext, node); + if (emitContext === EmitContext.Expression) { return substituteExpression(node); } else if (isShorthandPropertyAssignment(node)) { @@ -890,7 +891,7 @@ namespace ts { // If the left-hand-side of the binaryExpression is an identifier and its is export through export Specifier if (isIdentifier(left) && isAssignmentOperator(node.operatorToken.kind)) { if (bindingNameExportSpecifiersMap && hasProperty(bindingNameExportSpecifiersMap, left.text)) { - setNodeEmitFlags(node, NodeEmitFlags.NoSubstitution); + setEmitFlags(node, EmitFlags.NoSubstitution); let nestedExportAssignment: BinaryExpression; for (const specifier of bindingNameExportSpecifiersMap[left.text]) { nestedExportAssignment = nestedExportAssignment ? @@ -910,7 +911,7 @@ namespace ts { const operand = node.operand; if (isIdentifier(operand) && bindingNameExportSpecifiersForFileMap) { if (bindingNameExportSpecifiersMap && hasProperty(bindingNameExportSpecifiersMap, operand.text)) { - setNodeEmitFlags(node, NodeEmitFlags.NoSubstitution); + setEmitFlags(node, EmitFlags.NoSubstitution); let transformedUnaryExpression: BinaryExpression; if (node.kind === SyntaxKind.PostfixUnaryExpression) { transformedUnaryExpression = createBinary( @@ -920,7 +921,7 @@ namespace ts { /*location*/ node ); // We have to set no substitution flag here to prevent visit the binary expression and substitute it again as we will preform all necessary substitution in here - setNodeEmitFlags(transformedUnaryExpression, NodeEmitFlags.NoSubstitution); + setEmitFlags(transformedUnaryExpression, EmitFlags.NoSubstitution); } let nestedExportAssignment: BinaryExpression; for (const specifier of bindingNameExportSpecifiersMap[operand.text]) { @@ -935,9 +936,9 @@ namespace ts { } function trySubstituteExportedName(node: Identifier) { - const emitFlags = getNodeEmitFlags(node); - if ((emitFlags & NodeEmitFlags.LocalName) === 0) { - const container = resolver.getReferencedExportContainer(node, (emitFlags & NodeEmitFlags.ExportName) !== 0); + const emitFlags = getEmitFlags(node); + if ((emitFlags & EmitFlags.LocalName) === 0) { + const container = resolver.getReferencedExportContainer(node, (emitFlags & EmitFlags.ExportName) !== 0); if (container) { if (container.kind === SyntaxKind.SourceFile) { return createPropertyAccess( @@ -953,7 +954,7 @@ namespace ts { } function trySubstituteImportedName(node: Identifier): Expression { - if ((getNodeEmitFlags(node) & NodeEmitFlags.LocalName) === 0) { + if ((getEmitFlags(node) & EmitFlags.LocalName) === 0) { const declaration = resolver.getReferencedImportDeclaration(node); if (declaration) { if (isImportClause(declaration)) { @@ -1077,7 +1078,7 @@ namespace ts { if (includeNonAmdDependencies && importAliasName) { // Set emitFlags on the name of the classDeclaration // This is so that when printer will not substitute the identifier - setNodeEmitFlags(importAliasName, NodeEmitFlags.NoSubstitution); + setEmitFlags(importAliasName, EmitFlags.NoSubstitution); aliasedModuleNames.push(externalModuleName); importAliasNames.push(createParameter(importAliasName)); } diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index 5e3b028122775..4a137c89a9336 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -10,8 +10,6 @@ namespace ts { } const { - getNodeEmitFlags, - setNodeEmitFlags, startLexicalEnvironment, endLexicalEnvironment, hoistVariableDeclaration, @@ -50,6 +48,10 @@ namespace ts { return transformSourceFile; function transformSourceFile(node: SourceFile) { + if (isDeclarationFile(node)) { + return node; + } + if (isExternalModule(node) || compilerOptions.isolatedModules) { currentSourceFile = node; currentNode = node; @@ -116,9 +118,9 @@ namespace ts { createParameter(contextObjectForFile) ], /*type*/ undefined, - setNodeEmitFlags( + setEmitFlags( createBlock(statements, /*location*/ undefined, /*multiLine*/ true), - NodeEmitFlags.EmitEmitHelpers + EmitFlags.EmitEmitHelpers ) ); @@ -135,7 +137,7 @@ namespace ts { : [dependencies, body] ) ) - ], /*nodeEmitFlags*/ ~NodeEmitFlags.EmitEmitHelpers & getNodeEmitFlags(node)); + ], /*nodeEmitFlags*/ ~EmitFlags.EmitEmitHelpers & getEmitFlags(node)); } /** @@ -984,14 +986,14 @@ namespace ts { // Substitutions // - function onEmitNode(node: Node, emit: (node: Node) => void): void { + function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void { if (node.kind === SyntaxKind.SourceFile) { exportFunctionForFile = exportFunctionForFileMap[getOriginalNodeId(node)]; - previousOnEmitNode(node, emit); + previousOnEmitNode(emitContext, node, emitCallback); exportFunctionForFile = undefined; } else { - previousOnEmitNode(node, emit); + previousOnEmitNode(emitContext, node, emitCallback); } } @@ -1002,9 +1004,9 @@ namespace ts { * @param isExpression A value indicating whether the node is to be used in an expression * position. */ - function onSubstituteNode(node: Node, isExpression: boolean) { - node = previousOnSubstituteNode(node, isExpression); - if (isExpression) { + function onSubstituteNode(emitContext: EmitContext, node: Node) { + node = previousOnSubstituteNode(emitContext, node); + if (emitContext === EmitContext.Expression) { return substituteExpression(node); } @@ -1053,7 +1055,7 @@ namespace ts { } function substituteAssignmentExpression(node: BinaryExpression): Expression { - setNodeEmitFlags(node, NodeEmitFlags.NoSubstitution); + setEmitFlags(node, EmitFlags.NoSubstitution); const left = node.left; switch (left.kind) { @@ -1176,7 +1178,7 @@ namespace ts { const exportDeclaration = resolver.getReferencedExportContainer(operand); if (exportDeclaration) { const expr = createPrefix(node.operator, operand, node); - setNodeEmitFlags(expr, NodeEmitFlags.NoSubstitution); + setEmitFlags(expr, EmitFlags.NoSubstitution); const call = createExportExpression(operand, expr); if (node.kind === SyntaxKind.PrefixUnaryExpression) { return call; @@ -1241,7 +1243,7 @@ namespace ts { ]), m, createBlock([ - setNodeEmitFlags( + setEmitFlags( createIf( condition, createStatement( @@ -1251,7 +1253,7 @@ namespace ts { ) ) ), - NodeEmitFlags.SingleLine + EmitFlags.SingleLine ) ]) ), @@ -1393,10 +1395,10 @@ namespace ts { hoistBindingElement(node, /*isExported*/ false); } - function updateSourceFile(node: SourceFile, statements: Statement[], nodeEmitFlags: NodeEmitFlags) { + function updateSourceFile(node: SourceFile, statements: Statement[], nodeEmitFlags: EmitFlags) { const updated = getMutableClone(node); updated.statements = createNodeArray(statements, node.statements); - setNodeEmitFlags(updated, nodeEmitFlags); + setEmitFlags(updated, nodeEmitFlags); return updated; } } diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 1d01e51f6d8d7..054b1e4608bfc 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -24,10 +24,6 @@ namespace ts { export function transformTypeScript(context: TransformationContext) { const { - getNodeEmitFlags, - setNodeEmitFlags, - setCommentRange, - setSourceMapRange, startLexicalEnvironment, endLexicalEnvironment, hoistVariableDeclaration, @@ -46,6 +42,10 @@ namespace ts { context.onEmitNode = onEmitNode; context.onSubstituteNode = onSubstituteNode; + // Enable substitution for property/element access to emit const enum values. + context.enableSubstitution(SyntaxKind.PropertyAccessExpression); + context.enableSubstitution(SyntaxKind.ElementAccessExpression); + // These variables contain state that changes as we descend into the tree. let currentSourceFile: SourceFile; let currentNamespace: ModuleDeclaration; @@ -85,6 +85,10 @@ namespace ts { * @param node A SourceFile node. */ function transformSourceFile(node: SourceFile) { + if (isDeclarationFile(node)) { + return node; + } + return visitNode(node, visitor, isSourceFile); } @@ -449,7 +453,7 @@ namespace ts { node = visitEachChild(node, visitor, context); } - setNodeEmitFlags(node, NodeEmitFlags.EmitEmitHelpers | node.emitFlags); + setEmitFlags(node, EmitFlags.EmitEmitHelpers | getEmitFlags(node)); return node; } @@ -521,7 +525,7 @@ namespace ts { // To better align with the old emitter, we should not emit a trailing source map // entry if the class has static properties. if (staticProperties.length > 0) { - setNodeEmitFlags(classDeclaration, NodeEmitFlags.NoTrailingSourceMap | getNodeEmitFlags(classDeclaration)); + setEmitFlags(classDeclaration, EmitFlags.NoTrailingSourceMap | getEmitFlags(classDeclaration)); } statements.push(classDeclaration); @@ -776,7 +780,7 @@ namespace ts { // To preserve the behavior of the old emitter, we explicitly indent // the body of a class with static initializers. - setNodeEmitFlags(classExpression, NodeEmitFlags.Indented | getNodeEmitFlags(classExpression)); + setEmitFlags(classExpression, EmitFlags.Indented | getEmitFlags(classExpression)); expressions.push(startOnNewLine(createAssignment(temp, classExpression))); addRange(expressions, generateInitializedPropertyExpressions(node, staticProperties, temp)); expressions.push(startOnNewLine(temp)); @@ -1009,10 +1013,10 @@ namespace ts { Debug.assert(isIdentifier(node.name)); const name = node.name as Identifier; const propertyName = getMutableClone(name); - setNodeEmitFlags(propertyName, NodeEmitFlags.NoComments | NodeEmitFlags.NoSourceMap); + setEmitFlags(propertyName, EmitFlags.NoComments | EmitFlags.NoSourceMap); const localName = getMutableClone(name); - setNodeEmitFlags(localName, NodeEmitFlags.NoComments); + setEmitFlags(localName, EmitFlags.NoComments); return startOnNewLine( createStatement( @@ -1418,7 +1422,7 @@ namespace ts { moveRangePastDecorators(member) ); - setNodeEmitFlags(helper, NodeEmitFlags.NoComments); + setEmitFlags(helper, EmitFlags.NoComments); return helper; } @@ -1467,7 +1471,7 @@ namespace ts { ); const result = createAssignment(getDeclarationName(node), expression, moveRangePastDecorators(node)); - setNodeEmitFlags(result, NodeEmitFlags.NoComments); + setEmitFlags(result, EmitFlags.NoComments); return result; } // Emit the call to __decorate. Given the class: @@ -1491,7 +1495,7 @@ namespace ts { moveRangePastDecorators(node) ); - setNodeEmitFlags(result, NodeEmitFlags.NoComments); + setEmitFlags(result, EmitFlags.NoComments); return result; } } @@ -1521,7 +1525,7 @@ namespace ts { transformDecorator(decorator), parameterOffset, /*location*/ decorator.expression); - setNodeEmitFlags(helper, NodeEmitFlags.NoComments); + setEmitFlags(helper, EmitFlags.NoComments); expressions.push(helper); } } @@ -2324,11 +2328,11 @@ namespace ts { if (languageVersion >= ScriptTarget.ES6) { if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.AsyncMethodWithSuperBinding) { enableSubstitutionForAsyncMethodsWithSuper(); - setNodeEmitFlags(block, NodeEmitFlags.EmitAdvancedSuperHelper); + setEmitFlags(block, EmitFlags.EmitAdvancedSuperHelper); } else if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.AsyncMethodWithSuper) { enableSubstitutionForAsyncMethodsWithSuper(); - setNodeEmitFlags(block, NodeEmitFlags.EmitSuperHelper); + setEmitFlags(block, EmitFlags.EmitSuperHelper); } } @@ -2375,7 +2379,7 @@ namespace ts { setOriginalNode(parameter, node); setCommentRange(parameter, node); setSourceMapRange(parameter, moveRangePastModifiers(node)); - setNodeEmitFlags(parameter.name, NodeEmitFlags.NoTrailingSourceMap); + setEmitFlags(parameter.name, EmitFlags.NoTrailingSourceMap); return parameter; } @@ -2534,7 +2538,7 @@ namespace ts { // We request to be advised when the printer is about to print this node. This allows // us to set up the correct state for later substitutions. - let emitFlags = NodeEmitFlags.AdviseOnEmitNode; + let emitFlags = EmitFlags.AdviseOnEmitNode; // If needed, we should emit a variable declaration for the enum. If we emit // a leading variable declaration, we should not emit leading comments for the @@ -2544,7 +2548,7 @@ namespace ts { // We should still emit the comments if we are emitting a system module. if (moduleKind !== ModuleKind.System || currentScope !== currentSourceFile) { - emitFlags |= NodeEmitFlags.NoLeadingComments; + emitFlags |= EmitFlags.NoLeadingComments; } } @@ -2584,7 +2588,7 @@ namespace ts { ); setOriginalNode(enumStatement, node); - setNodeEmitFlags(enumStatement, emitFlags); + setEmitFlags(enumStatement, emitFlags); statements.push(enumStatement); if (isNamespaceExport(node)) { @@ -2736,7 +2740,7 @@ namespace ts { // })(m1 || (m1 = {})); // trailing comment module // setCommentRange(statement, node); - setNodeEmitFlags(statement, NodeEmitFlags.NoTrailingComments); + setEmitFlags(statement, EmitFlags.NoTrailingComments); statements.push(statement); } @@ -2759,7 +2763,7 @@ namespace ts { // We request to be advised when the printer is about to print this node. This allows // us to set up the correct state for later substitutions. - let emitFlags = NodeEmitFlags.AdviseOnEmitNode; + let emitFlags = EmitFlags.AdviseOnEmitNode; // If needed, we should emit a variable declaration for the module. If we emit // a leading variable declaration, we should not emit leading comments for the @@ -2768,7 +2772,7 @@ namespace ts { addVarForEnumOrModuleDeclaration(statements, node); // We should still emit the comments if we are emitting a system module. if (moduleKind !== ModuleKind.System || currentScope !== currentSourceFile) { - emitFlags |= NodeEmitFlags.NoLeadingComments; + emitFlags |= EmitFlags.NoLeadingComments; } } @@ -2820,7 +2824,7 @@ namespace ts { ); setOriginalNode(moduleStatement, node); - setNodeEmitFlags(moduleStatement, emitFlags); + setEmitFlags(moduleStatement, emitFlags); statements.push(moduleStatement); return statements; } @@ -2895,7 +2899,7 @@ namespace ts { // })(hello || (hello = {})); // We only want to emit comment on the namespace which contains block body itself, not the containing namespaces. if (body.kind !== SyntaxKind.ModuleBlock) { - setNodeEmitFlags(block, block.emitFlags | NodeEmitFlags.NoComments); + setEmitFlags(block, getEmitFlags(block) | EmitFlags.NoComments); } return block; } @@ -2936,7 +2940,7 @@ namespace ts { } const moduleReference = createExpressionFromEntityName(node.moduleReference); - setNodeEmitFlags(moduleReference, NodeEmitFlags.NoComments | NodeEmitFlags.NoNestedComments); + setEmitFlags(moduleReference, EmitFlags.NoComments | EmitFlags.NoNestedComments); if (isNamedExternalModuleExport(node) || !isNamespaceExport(node)) { // export var ${name} = ${moduleReference}; @@ -3048,15 +3052,15 @@ namespace ts { function getNamespaceMemberName(name: Identifier, allowComments?: boolean, allowSourceMaps?: boolean): Expression { const qualifiedName = createPropertyAccess(currentNamespaceContainerName, getSynthesizedClone(name), /*location*/ name); - let emitFlags: NodeEmitFlags; + let emitFlags: EmitFlags; if (!allowComments) { - emitFlags |= NodeEmitFlags.NoComments; + emitFlags |= EmitFlags.NoComments; } if (!allowSourceMaps) { - emitFlags |= NodeEmitFlags.NoSourceMap; + emitFlags |= EmitFlags.NoSourceMap; } if (emitFlags) { - setNodeEmitFlags(qualifiedName, emitFlags); + setEmitFlags(qualifiedName, emitFlags); } return qualifiedName; } @@ -3093,7 +3097,7 @@ namespace ts { * @param allowComments A value indicating whether comments may be emitted for the name. */ function getLocalName(node: DeclarationStatement | ClassExpression, noSourceMaps?: boolean, allowComments?: boolean) { - return getDeclarationName(node, allowComments, !noSourceMaps, NodeEmitFlags.LocalName); + return getDeclarationName(node, allowComments, !noSourceMaps, EmitFlags.LocalName); } /** @@ -3111,7 +3115,7 @@ namespace ts { return getNamespaceMemberName(getDeclarationName(node), allowComments, !noSourceMaps); } - return getDeclarationName(node, allowComments, !noSourceMaps, NodeEmitFlags.ExportName); + return getDeclarationName(node, allowComments, !noSourceMaps, EmitFlags.ExportName); } /** @@ -3122,20 +3126,20 @@ namespace ts { * @param allowSourceMaps A value indicating whether source maps may be emitted for the name. * @param emitFlags Additional NodeEmitFlags to specify for the name. */ - function getDeclarationName(node: DeclarationStatement | ClassExpression, allowComments?: boolean, allowSourceMaps?: boolean, emitFlags?: NodeEmitFlags) { + function getDeclarationName(node: DeclarationStatement | ClassExpression, allowComments?: boolean, allowSourceMaps?: boolean, emitFlags?: EmitFlags) { if (node.name) { const name = getMutableClone(node.name); - emitFlags |= getNodeEmitFlags(node.name); + emitFlags |= getEmitFlags(node.name); if (!allowSourceMaps) { - emitFlags |= NodeEmitFlags.NoSourceMap; + emitFlags |= EmitFlags.NoSourceMap; } if (!allowComments) { - emitFlags |= NodeEmitFlags.NoComments; + emitFlags |= EmitFlags.NoComments; } if (emitFlags) { - setNodeEmitFlags(name, emitFlags); + setEmitFlags(name, emitFlags); } return name; @@ -3231,7 +3235,7 @@ namespace ts { * @param node The node to emit. * @param emit A callback used to emit the node in the printer. */ - function onEmitNode(node: Node, emit: (node: Node) => void): void { + function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void { const savedApplicableSubstitutions = applicableSubstitutions; const savedCurrentSuperContainer = currentSuperContainer; // If we need to support substitutions for `super` in an async method, @@ -3248,7 +3252,7 @@ namespace ts { applicableSubstitutions |= TypeScriptSubstitutionFlags.NonQualifiedEnumMembers; } - previousOnEmitNode(node, emit); + previousOnEmitNode(emitContext, node, emitCallback); applicableSubstitutions = savedApplicableSubstitutions; currentSuperContainer = savedCurrentSuperContainer; @@ -3261,9 +3265,9 @@ namespace ts { * @param isExpression A value indicating whether the node is to be used in an expression * position. */ - function onSubstituteNode(node: Node, isExpression: boolean) { - node = previousOnSubstituteNode(node, isExpression); - if (isExpression) { + function onSubstituteNode(emitContext: EmitContext, node: Node) { + node = previousOnSubstituteNode(emitContext, node); + if (emitContext === EmitContext.Expression) { return substituteExpression(node); } else if (isShorthandPropertyAssignment(node)) { @@ -3294,17 +3298,15 @@ namespace ts { switch (node.kind) { case SyntaxKind.Identifier: return substituteExpressionIdentifier(node); - } - - if (enabledSubstitutions & TypeScriptSubstitutionFlags.AsyncMethodsWithSuper) { - switch (node.kind) { - case SyntaxKind.CallExpression: + case SyntaxKind.PropertyAccessExpression: + return substitutePropertyAccessExpression(node); + case SyntaxKind.ElementAccessExpression: + return substituteElementAccessExpression(node); + case SyntaxKind.CallExpression: + if (enabledSubstitutions & TypeScriptSubstitutionFlags.AsyncMethodsWithSuper) { return substituteCallExpression(node); - case SyntaxKind.PropertyAccessExpression: - return substitutePropertyAccessExpression(node); - case SyntaxKind.ElementAccessExpression: - return substituteElementAccessExpression(node); - } + } + break; } return node; @@ -3342,7 +3344,7 @@ namespace ts { function trySubstituteNamespaceExportedName(node: Identifier): Expression { // If this is explicitly a local name, do not substitute. - if (enabledSubstitutions & applicableSubstitutions && (getNodeEmitFlags(node) & NodeEmitFlags.LocalName) === 0) { + if (enabledSubstitutions & applicableSubstitutions && (getEmitFlags(node) & EmitFlags.LocalName) === 0) { // If we are nested within a namespace declaration, we may need to qualifiy // an identifier that is exported from a merged namespace. const container = resolver.getReferencedExportContainer(node, /*prefixLocals*/ false); @@ -3381,7 +3383,7 @@ namespace ts { } function substitutePropertyAccessExpression(node: PropertyAccessExpression) { - if (node.expression.kind === SyntaxKind.SuperKeyword) { + if (enabledSubstitutions & TypeScriptSubstitutionFlags.AsyncMethodsWithSuper && node.expression.kind === SyntaxKind.SuperKeyword) { const flags = getSuperContainerAsyncMethodFlags(); if (flags) { return createSuperAccessInAsyncMethod( @@ -3392,11 +3394,11 @@ namespace ts { } } - return node; + return substituteConstantValue(node); } function substituteElementAccessExpression(node: ElementAccessExpression) { - if (node.expression.kind === SyntaxKind.SuperKeyword) { + if (enabledSubstitutions & TypeScriptSubstitutionFlags.AsyncMethodsWithSuper && node.expression.kind === SyntaxKind.SuperKeyword) { const flags = getSuperContainerAsyncMethodFlags(); if (flags) { return createSuperAccessInAsyncMethod( @@ -3407,9 +3409,39 @@ namespace ts { } } + return substituteConstantValue(node); + } + + function substituteConstantValue(node: PropertyAccessExpression | ElementAccessExpression): LeftHandSideExpression { + const constantValue = tryGetConstEnumValue(node); + if (constantValue !== undefined) { + const substitute = createLiteral(constantValue); + setSourceMapRange(substitute, node); + setCommentRange(substitute, node); + if (!compilerOptions.removeComments) { + const propertyName = isPropertyAccessExpression(node) + ? declarationNameToString(node.name) + : getTextOfNode(node.argumentExpression); + substitute.trailingComment = ` ${propertyName} `; + } + + setConstantValue(node, constantValue); + return substitute; + } + return node; } + function tryGetConstEnumValue(node: Node): number { + if (compilerOptions.isolatedModules) { + return undefined; + } + + return isPropertyAccessExpression(node) || isElementAccessExpression(node) + ? resolver.getConstantValue(node) + : undefined; + } + function createSuperAccessInAsyncMethod(argumentExpression: Expression, flags: NodeCheckFlags, location: TextRange): LeftHandSideExpression { if (flags & NodeCheckFlags.AsyncMethodWithSuperBinding) { return createPropertyAccess( diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 7629f12423115..f6d791769925e 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -494,10 +494,7 @@ namespace ts { /* @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 */ flowNode?: FlowNode; // Associated FlowNode (initialized by binding) - /* @internal */ transformId?: number; // Associates transient transformation properties with a specific transformation (initialized by transformation). - /* @internal */ emitFlags?: NodeEmitFlags; // Transient emit flags for a synthesized node (initialized by transformation). - /* @internal */ sourceMapRange?: TextRange; // Transient custom sourcemap range for a synthesized node (initialized by transformation). - /* @internal */ commentRange?: TextRange; // Transient custom comment range for a synthesized node (initialized by transformation). + /* @internal */ emitNode?: EmitNode; // Associated EmitNode (initialized by transforms) } export interface NodeArray extends Array, TextRange { @@ -3175,7 +3172,17 @@ namespace ts { } /* @internal */ - export const enum NodeEmitFlags { + export interface EmitNode { + flags?: EmitFlags; + commentRange?: TextRange; + sourceMapRange?: TextRange; + tokenSourceMapRanges?: Map; + annotatedNodes?: Node[]; // Tracks Parse-tree nodes with EmitNodes for eventual cleanup. + constantValue?: number; + } + + /* @internal */ + export const enum EmitFlags { EmitEmitHelpers = 1 << 0, // Any emit helpers should be written to this node. EmitExportStar = 1 << 1, // The export * helper should be written to this node. EmitSuperHelper = 1 << 2, // Emit the basic _super helper for async methods. @@ -3205,6 +3212,14 @@ namespace ts { CustomPrologue = 1 << 23, // Treat the statement as if it were a prologue directive (NOTE: Prologue directives are *not* transformed). } + /* @internal */ + export const enum EmitContext { + SourceFile, // Emitting a SourceFile + Expression, // Emitting an Expression + IdentifierName, // Emitting an IdentifierName + Unspecified, // Emitting an otherwise unspecified node + } + /** Additional context provided to `visitEachChild` */ /* @internal */ export interface LexicalEnvironment { diff --git a/tests/baselines/reference/constEnumToStringWithComments.js b/tests/baselines/reference/constEnumToStringWithComments.js index c01e75cb923fc..f0fbc8663859e 100644 --- a/tests/baselines/reference/constEnumToStringWithComments.js +++ b/tests/baselines/reference/constEnumToStringWithComments.js @@ -23,15 +23,15 @@ let c1 = Foo["C"].toString(); //// [constEnumToStringWithComments.js] -var x0 = 100 /* X */..toString(); -var x1 = 100 /* "X" */..toString(); +var x0 = 100 /* X */.toString(); +var x1 = 100 /* "X" */.toString(); var y0 = 0.5 /* Y */.toString(); var y1 = 0.5 /* "Y" */.toString(); -var z0 = 2 /* Z */..toString(); -var z1 = 2 /* "Z" */..toString(); -var a0 = -1 /* A */..toString(); -var a1 = -1 /* "A" */..toString(); +var z0 = 2 /* Z */.toString(); +var z1 = 2 /* "Z" */.toString(); +var a0 = -1 /* A */.toString(); +var a1 = -1 /* "A" */.toString(); var b0 = -1.5 /* B */.toString(); var b1 = -1.5 /* "B" */.toString(); -var c0 = -1 /* C */..toString(); -var c1 = -1 /* "C" */..toString(); +var c0 = -1 /* C */.toString(); +var c1 = -1 /* "C" */.toString();