Refactored emit pipeline.

This commit is contained in:
Ron Buckton
2016-09-26 15:21:03 -07:00
parent c1ee534974
commit bfb8933a93
11 changed files with 326 additions and 322 deletions
+15 -20
View File
@@ -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,9 +34,9 @@ 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;
}
@@ -46,10 +46,12 @@ namespace ts {
if ((pos < 0 && end < 0) || (pos === end)) {
// Both pos and end are synthesized, so just emit the node without comments.
if (emitFlags & EmitFlags.NoNestedComments) {
disableCommentsAndEmit(node, emitCallback);
disabled = true;
emitCallback(emitContext, node);
disabled = false;
}
else {
emitCallback(node);
emitCallback(emitContext, node);
}
}
else {
@@ -91,10 +93,12 @@ namespace ts {
}
if (emitFlags & EmitFlags.NoNestedComments) {
disableCommentsAndEmit(node, emitCallback);
disabled = true;
emitCallback(emitContext, node);
disabled = false;
}
else {
emitCallback(node);
emitCallback(emitContext, node);
}
if (extendedDiagnostics) {
@@ -137,8 +141,10 @@ namespace ts {
performance.measure("commentTime", "preEmitBodyWithDetachedComments");
}
if (emitFlags & EmitFlags.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;
}
+115 -104
View File
@@ -229,33 +229,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 {
transformed,
emitNodeWithSubstitution,
emitNodeWithNotification
} = transformed;
performance.mark("beforePrint");
} = transformFiles(resolver, host, sourceFiles, transformers);
performance.measure("transformTime", "beforeTransform");
// Emit each output file
forEachTransformedEmitFile(host, transformed.getSourceFiles(), emitFile);
// Clean up after transformation
transformed.dispose();
performance.mark("beforePrint");
forEachTransformedEmitFile(host, transformed, emitFile);
performance.measure("printTime", "beforePrint");
// Clean up emit nodes on parse tree
for (const sourceFile of sourceFiles) {
disposeEmitNodes(sourceFile);
}
return {
emitSkipped,
diagnostics: emitterDiagnostics.getDiagnostics(),
@@ -346,111 +340,137 @@ const _super = (function (geti, seti) {
currentFileIdentifiers = node.identifiers;
sourceMap.setSourceFile(node);
comments.setSourceFile(node);
emitNodeWithNotification(node, emitWithSubstitution);
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);
}
/**
* 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.
*/
function emitWithSourceMap(node: Node) {
emitNodeWithSourceMap(node, emitWithSubstitution);
}
/**
* Emits a node with possible substitution.
*
* NOTE: Do not call this method directly. It is part of the emit pipeline
* and should only be called indirectly from emitWithSourceMap or
* emitIdentifierNameWithComments.
*/
function emitWithSubstitution(node: Node) {
emitNodeWithSubstitution(node, /*isExpression*/ false, emitWorker);
pipelineEmitWithNotification(EmitContext.Unspecified, node);
}
/**
* Emits an IdentifierName.
*/
function emitIdentifierName(node: Identifier) {
if (node) {
emitNodeWithNotification(node, emitIdentifierNameWithComments);
}
}
/**
* Emits an IdentifierName with possible comments.
*
* NOTE: Do not call this method directly. It is part of the emit pipeline
* and should only be called indirectly from emitIdentifierName.
*/
function emitIdentifierNameWithComments(node: Identifier) {
emitNodeWithComments(node, emitWithSubstitution);
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 possible 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, emitExpressionWithSubstitution);
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 an expression with possible substitution.
* Emits a node with source maps.
*
* NOTE: Do not call this method directly. It is part of the emitExpression pipeline
* and should only be called indirectly from emitExpressionWithSourceMap or
* from emitWorker.
* NOTE: Do not call this method directly. It is part of the emit pipeline
* and should only be called indirectly from pipelineEmitWithComments.
*/
function emitExpressionWithSubstitution(node: Expression) {
emitNodeWithSubstitution(node, /*isExpression*/ true, emitExpressionWorker);
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;
}
emitNodeWithSourceMap(emitContext, node, pipelineEmitWithSubstitution);
}
/**
* Emits a node with possible substitution.
*
* 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 pickign a more specific context).
*/
function pipelineEmitWithSubstitution(emitContext: EmitContext, node: Node) {
emitNodeWithSubstitution(emitContext, node, pipelineEmitForContext);
}
/**
* Emits a node.
*
* NOTE: Do not call this method directly. It is part of the emit pipeline
* and should only be called indirectly from emitNodeWithSubstitution.
* and should only be called indirectly from pipelineEmitWithSubstitution.
*/
function emitWorker(node: Node): void {
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);
}
}
/**
* Emits a node in the SourceFile EmitContext.
*
* NOTE: Do not call this method directly. It is part of the emit pipeline
* and should only be called indirectly from pipelineEmitForContext.
*/
function pipelineEmitInSourceFileContext(node: Node): void {
const kind = node.kind;
switch (kind) {
// Top-level nodes
case SyntaxKind.SourceFile:
return emitSourceFile(<SourceFile>node);
}
}
/**
* Emits a node in the IdentifierName EmitContext.
*
* NOTE: Do not call this method directly. It is part of the emit pipeline
* and should only be called indirectly from pipelineEmitForContext.
*/
function pipelineEmitInIdentifierNameContext(node: Node): void {
const kind = node.kind;
switch (kind) {
// Identifiers
case SyntaxKind.Identifier:
return emitIdentifier(<Identifier>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
@@ -486,7 +506,8 @@ const _super = (function (geti, seti) {
case SyntaxKind.StringKeyword:
case SyntaxKind.SymbolKeyword:
case SyntaxKind.GlobalKeyword:
return emitTokenNode(node);
writeTokenText(kind);
return;
// Parse tree nodes
@@ -691,27 +712,24 @@ const _super = (function (geti, seti) {
case SyntaxKind.EnumMember:
return emitEnumMember(<EnumMember>node);
// Top-level nodes
case SyntaxKind.SourceFile:
return emitSourceFile(<SourceFile>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 emitExpressionWithSubstitution(node);
return pipelineEmitWithSubstitution(EmitContext.Expression, node);
}
}
/**
* Emits an expression.
* Emits a node in the Expression EmitContext.
*
* NOTE: Do not call this method directly. It is part of the emitExpression pipeline
* and should only be called indirectly from emitExpressionWithNotification.
* NOTE: Do not call this method directly. It is part of the emit pipeline
* and should only be called indirectly from pipelineEmitForContext.
*/
function emitExpressionWorker(node: Node) {
function pipelineEmitInExpressionContext(node: Node): void {
const kind = node.kind;
switch (kind) {
// Literals
@@ -733,7 +751,8 @@ const _super = (function (geti, seti) {
case SyntaxKind.SuperKeyword:
case SyntaxKind.TrueKeyword:
case SyntaxKind.ThisKeyword:
return emitTokenNode(node);
writeTokenText(kind);
return;
// Expressions
case SyntaxKind.ArrayLiteralExpression:
@@ -2444,15 +2463,7 @@ const _super = (function (geti, seti) {
function writeTokenText(token: SyntaxKind, pos?: number) {
const tokenString = tokenToString(token);
write(tokenString);
return positionIsSynthesized(pos) ? -1 : pos + tokenString.length;
}
function emitTokenNode(node: Node) {
emitNodeWithSourceMap(node, emitTokenNodeWorker);
}
function emitTokenNodeWorker(node: Node) {
writeTokenText(node.kind);
return pos < 0 ? pos : pos + tokenString.length;
}
function increaseIndentIf(value: boolean, valueToWriteWhenNotIndenting?: string) {
+134 -1
View File
@@ -2665,6 +2665,139 @@ namespace ts {
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<T extends Node>(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<T extends Node>(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<T extends Node>(node: T, token: SyntaxKind, range: TextRange) {
const emitNode = getOrCreateEmitNode(node);
const tokenSourceMapRanges = emitNode.tokenSourceMapRanges || (emitNode.tokenSourceMapRanges = createMap<TextRange>());
tokenSourceMapRanges[token] = range;
return node;
}
/**
* Sets a custom text range to use when emitting comments.
*/
export function setCommentRange<T extends Node>(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];
}
export function setTextRange<T extends TextRange>(node: T, location: TextRange): T {
if (location) {
node.pos = location.pos;
@@ -2706,7 +2839,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 /// <amd-module name="<new name>" />
+6 -5
View File
@@ -38,10 +38,11 @@ namespace ts {
/**
* Emits a node with possible leading and trailing source maps.
*
* @param emitContext The current emit context
* @param node The node to emit.
* @param emitCallback The callback used to emit the node.
*/
emitNodeWithSourceMap(node: Node, emitCallback: (node: Node) => void): void;
emitNodeWithSourceMap(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void;
/**
* Emits a token of a node node with possible leading and trailing source maps.
@@ -313,9 +314,9 @@ namespace ts {
* @param node The node to emit.
* @param emitCallback The callback used to emit the node.
*/
function emitNodeWithSourceMap(node: Node, emitCallback: (node: Node) => void) {
function emitNodeWithSourceMap(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) {
if (disabled) {
return emitCallback(node);
return emitCallback(emitContext, node);
}
if (node) {
@@ -331,11 +332,11 @@ namespace ts {
if (emitFlags & EmitFlags.NoNestedSourceMaps) {
disabled = true;
emitCallback(node);
emitCallback(emitContext, node);
disabled = false;
}
else {
emitCallback(node);
emitCallback(emitContext, node);
}
if (node.kind !== SyntaxKind.NotEmittedStatement
+23 -167
View File
@@ -28,29 +28,25 @@ namespace ts {
/**
* Gets the transformed source files.
*/
getSourceFiles(): SourceFile[];
transformed: SourceFile[];
/**
* 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.
*/
emitNodeWithSubstitution(node: Node, isExpression: boolean, emitCallback: (node: Node) => void): void;
emitNodeWithSubstitution(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void;
/**
* 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.
*/
emitNodeWithNotification(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 {
@@ -82,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
@@ -100,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 */
@@ -129,139 +125,6 @@ namespace ts {
return transformers;
}
/**
* 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<T extends Node>(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<T extends Node>(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<T extends Node>(node: T, token: SyntaxKind, range: TextRange) {
const emitNode = getOrCreateEmitNode(node);
const tokenSourceMapRanges = emitNode.tokenSourceMapRanges || (emitNode.tokenSourceMapRanges = createMap<TextRange>());
tokenSourceMapRanges[token] = range;
return node;
}
/**
* Sets a custom text range to use when emitting comments.
*/
export function setCommentRange<T extends Node>(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];
}
/**
* Transforms an array of SourceFiles by passing them through each transformer.
*
@@ -290,10 +153,10 @@ namespace ts {
hoistFunctionDeclaration,
startLexicalEnvironment,
endLexicalEnvironment,
onSubstituteNode: (node, isExpression) => node,
onSubstituteNode: (emitContext, node) => node,
enableSubstitution,
isSubstitutionEnabled,
onEmitNode: (node, emitCallback) => emitCallback(node),
onEmitNode: (node, emitContext, emitCallback) => emitCallback(node, emitContext),
enableEmitNotification,
isEmitNotificationEnabled
};
@@ -308,20 +171,9 @@ namespace ts {
lexicalEnvironmentDisabled = true;
return {
getSourceFiles: () => transformed,
transformed,
emitNodeWithSubstitution,
emitNodeWithNotification,
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 sourceFile of sourceFiles) {
disposeEmitNodes(sourceFile);
}
}
emitNodeWithNotification
};
/**
@@ -355,21 +207,21 @@ namespace ts {
/**
* Emits a node with possible substitution.
*
* @param emitContext The current emit context.
* @param node The node to emit.
* @param isExpression Whether the node represents an expression.
* @param emitCallback The callback used to emit the node or its substitute.
*/
function emitNodeWithSubstitution(node: Node, isExpression: boolean, emitCallback: (node: Node) => void) {
function emitNodeWithSubstitution(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) {
if (node) {
if (isSubstitutionEnabled(node)) {
const substitute = context.onSubstituteNode(node, isExpression);
const substitute = context.onSubstituteNode(emitContext, node);
if (substitute && substitute !== node) {
emitCallback(substitute);
emitCallback(emitContext, substitute);
return;
}
}
emitCallback(node);
emitCallback(emitContext, node);
}
}
@@ -391,14 +243,18 @@ namespace ts {
/**
* Emits a node with possible emit notification.
*
* @param emitContext The current emit context.
* @param node The node to emit.
* @param emitCallback The callback used to emit the node.
*/
function emitNodeWithNotification(node: Node, emitCallback: (node: Node) => void) {
function emitNodeWithNotification(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void) {
if (node) {
if (isEmitNotificationEnabled(node)) {
context.onEmitNode(node, emitCallback);
context.onEmitNode(emitContext, node, emitCallback);
}
else {
emitCallback(node);
emitCallback(emitContext, node);
}
}
}
+5 -5
View File
@@ -2852,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 savedUseCapturedThis = useCapturedThis;
if (enabledSubstitutions & ES6SubstitutionFlags.CapturedThis && isFunctionLike(node)) {
@@ -2861,7 +2861,7 @@ namespace ts {
useCapturedThis = (getEmitFlags(node) & EmitFlags.CapturesThis) !== 0;
}
previousOnEmitNode(node, emit);
previousOnEmitNode(emitContext, node, emitCallback);
useCapturedThis = savedUseCapturedThis;
}
@@ -2902,10 +2902,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);
}
+3 -3
View File
@@ -1883,9 +1883,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(<Expression>node);
}
return node;
+6 -6
View File
@@ -822,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);
}
}
@@ -840,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(<Expression>node);
}
else if (isShorthandPropertyAssignment(node)) {
+6 -6
View File
@@ -986,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);
}
}
@@ -1004,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(<Expression>node);
}
+5 -5
View File
@@ -3231,7 +3231,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 +3248,7 @@ namespace ts {
applicableSubstitutions |= TypeScriptSubstitutionFlags.NonQualifiedEnumMembers;
}
previousOnEmitNode(node, emit);
previousOnEmitNode(emitContext, node, emitCallback);
applicableSubstitutions = savedApplicableSubstitutions;
currentSuperContainer = savedCurrentSuperContainer;
@@ -3261,9 +3261,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(<Expression>node);
}
else if (isShorthandPropertyAssignment(node)) {
+8
View File
@@ -3189,6 +3189,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 {