Reduce stack depth from transforms

This commit is contained in:
Ron Buckton
2018-02-08 03:40:12 -08:00
parent b02003652b
commit 96fd7c6f16
12 changed files with 263 additions and 132 deletions
+62 -17
View File
@@ -105,7 +105,9 @@ namespace ts {
hasGlobalName: resolver.hasGlobalName,
// transform hooks
onEmitNode: transform.emitNodeWithNotification,
onEmitNode: transform.useEmitNodeWithNotification ? transform.emitNodeWithNotification : undefined,
onBeforeEmitNode: transform.useEmitNodeWithNotification ? undefined : transform.beforeEmitNode,
onAfterEmitNode: transform.useEmitNodeWithNotification ? undefined : transform.afterEmitNode,
substituteNode: transform.substituteNode,
// sourcemap hooks
@@ -260,11 +262,15 @@ namespace ts {
}
const enum PipelinePhase {
BeforeEmit,
LeadingComments,
LeadingSourceMap,
Emit,
TrailingSourceMap,
TrailingComments
TrailingComments,
AfterEmit,
Start = BeforeEmit
}
export function createPrinter(printerOptions: PrinterOptions = {}, handlers: PrintHandlers = {}): Printer {
@@ -275,6 +281,8 @@ namespace ts {
onEmitSourceMapOfToken,
onEmitSourceMapOfPosition,
onEmitNode,
onBeforeEmitNode,
onAfterEmitNode,
onEmitHelpers,
onSetSourceFile,
substituteNode,
@@ -284,6 +292,8 @@ namespace ts {
onAfterEmitToken
} = handlers;
Debug.assert(!(onEmitNode && (onBeforeEmitNode || onAfterEmitNode)), "Do not provide 'onEmitNode' of either of 'onBeforeEmitNode' or 'onAfterEmitNode' are provided.");
const newLine = getNewLineCharacter(printerOptions);
const comments = createCommentWriter(printerOptions, onEmitSourceMapOfPosition);
const {
@@ -428,7 +438,13 @@ namespace ts {
setSourceFile(sourceFile);
}
pipelineEmitWithNotification(hint, node);
// NOTE: this has been manually inlined to reduce overall stack depth
if (onEmitNode) {
onEmitNode(hint, node, pipelineEmit);
}
else {
pipelineEmitWorker(hint, node, PipelinePhase.Start);
}
}
function setSourceFile(sourceFile: SourceFile) {
@@ -456,33 +472,49 @@ namespace ts {
}
function emit(node: Node | undefined) {
pipelineEmitWithNotification(EmitHint.Unspecified, node);
// NOTE: this has been manually inlined to reduce overall stack depth
if (onEmitNode) {
onEmitNode(EmitHint.Unspecified, node, pipelineEmit);
}
else {
pipelineEmitWorker(EmitHint.Unspecified, node, PipelinePhase.Start);
}
}
function emitIdentifierName(node: Identifier | undefined) {
pipelineEmitWithNotification(EmitHint.IdentifierName, node);
// NOTE: this has been manually inlined to reduce overall stack depth
if (onEmitNode) {
onEmitNode(EmitHint.IdentifierName, node, pipelineEmit);
}
else {
pipelineEmitWorker(EmitHint.IdentifierName, node, PipelinePhase.Start);
}
}
function emitExpression(node: Expression | undefined) {
pipelineEmitWithNotification(EmitHint.Expression, node);
}
function pipelineEmitWithNotification(hint: EmitHint, node: Node) {
// NOTE: this has been manually inlined to reduce overall stack depth
if (onEmitNode) {
onEmitNode(hint, node, pipelineEmitWithComments);
onEmitNode(EmitHint.Expression, node, pipelineEmit);
}
else {
pipelineEmitWithComments(hint, node);
pipelineEmitWorker(EmitHint.Expression, node, PipelinePhase.Start);
}
}
function pipelineEmitWithComments(hint: EmitHint, node: Node) {
pipelineEmit(hint, node, PipelinePhase.LeadingComments);
function pipelineEmit(hint: EmitHint, node: Node) {
pipelineEmitWorker(hint, node, PipelinePhase.Start);
}
function pipelineEmit(hint: EmitHint, node: Node | undefined, start: PipelinePhase) {
function pipelineEmitWorker(hint: EmitHint, node: Node | undefined, phase: PipelinePhase) {
if (!node) return;
switch (start) {
switch (phase) {
case PipelinePhase.BeforeEmit:
if (onBeforeEmitNode) {
onBeforeEmitNode(hint, node);
}
// falls through
case PipelinePhase.LeadingComments:
node = trySubstituteNode(hint, node);
if (hint !== EmitHint.SourceFile) {
@@ -514,7 +546,12 @@ namespace ts {
emitTrailingCommentsOfNode(node);
}
break;
// falls through
case PipelinePhase.AfterEmit:
if (onAfterEmitNode) {
onAfterEmitNode(hint, node);
}
}
}
@@ -1260,7 +1297,15 @@ namespace ts {
}
writePunctuation("[");
pipelineEmitWithNotification(EmitHint.MappedTypeParameter, node.typeParameter);
// NOTE: this has been manually inlined to reduce overall stack depth
if (onEmitNode) {
onEmitNode(EmitHint.MappedTypeParameter, node.typeParameter, pipelineEmit);
}
else {
pipelineEmitWorker(EmitHint.MappedTypeParameter, node.typeParameter, PipelinePhase.Start);
}
writePunctuation("]");
emit(node.questionToken);
-24
View File
@@ -37,15 +37,6 @@ namespace ts {
emitLeadingSourceMapOfNode(node: Node): void;
emitTrailingSourceMapOfNode(node: Node): void;
/**
* Emits a node with possible leading and trailing source maps.
*
* @param hint The current emit context
* @param node The node to emit.
* @param emitCallback The callback used to emit the node.
*/
emitNodeWithSourceMap(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void;
/**
* Emits a token of a node node with possible leading and trailing source maps.
*
@@ -108,7 +99,6 @@ namespace ts {
emitPos,
emitLeadingSourceMapOfNode,
emitTrailingSourceMapOfNode,
emitNodeWithSourceMap,
emitTokenWithSourceMap,
getText,
getSourceMappingURL,
@@ -317,20 +307,6 @@ namespace ts {
}
}
/**
* Emits a node with possible leading and trailing source maps.
*
* @param hint A hint as to the intended usage of the node.
* @param node The node to emit.
* @param emitCallback The callback used to emit the node.
*/
function emitNodeWithSourceMap(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) {
if (!node) return;
emitLeadingSourceMapOfNode(node);
emitCallback(hint, node);
emitTrailingSourceMapOfNode(node);
}
function emitLeadingSourceMapOfNode(node: Node) {
if (!node) return;
+67 -6
View File
@@ -82,6 +82,13 @@ namespace ts {
return transformers;
}
const defaultOnSubstituteNode: TransformationContext["onSubstituteNode"] = (_, node) => node;
const defaultOnEmitNode: TransformationContext["onEmitNode"] = (hint, node, callback) => callback(hint, node);
// NOTE: not using `noop` here to avoid deoptimizations due to inlining and argument/parameter
// count mismatches. Deoptimization for this would be severely negative as these functions are
// used in a hot path.
const defaultOnBeforeOrAfterEmitNode: TransformationContext["onBeforeEmitNode"] = (_hint, _node) => { /*empty*/ };
/**
* Transforms an array of SourceFiles by passing them through each transformer.
*
@@ -101,8 +108,10 @@ namespace ts {
let lexicalEnvironmentStackOffset = 0;
let lexicalEnvironmentSuspended = false;
let emitHelpers: EmitHelper[];
let onSubstituteNode: TransformationContext["onSubstituteNode"] = (_, node) => node;
let onEmitNode: TransformationContext["onEmitNode"] = (hint, node, callback) => callback(hint, node);
let onSubstituteNode: TransformationContext["onSubstituteNode"] = defaultOnSubstituteNode;
let onEmitNode: TransformationContext["onEmitNode"];
let onBeforeEmitNode: TransformationContext["onBeforeEmitNode"];
let onAfterEmitNode: TransformationContext["onBeforeEmitNode"];
let state = TransformationState.Uninitialized;
// The transformation context is provided to each transformer as part of transformer
@@ -129,12 +138,32 @@ namespace ts {
Debug.assert(value !== undefined, "Value must not be 'undefined'");
onSubstituteNode = value;
},
get onEmitNode() { return onEmitNode; },
get onEmitNode() {
// for backwards compatibility, we push any current before/after events into the current callback.
if (onBeforeEmitNode || onAfterEmitNode) {
onEmitNode = wrapNotification(onEmitNode || defaultOnEmitNode, onBeforeEmitNode, onAfterEmitNode);
onBeforeEmitNode = undefined;
onAfterEmitNode = undefined;
}
return onEmitNode || defaultOnEmitNode;
},
set onEmitNode(value) {
Debug.assert(state < TransformationState.Initialized, "Cannot modify transformation hooks after initialization has completed.");
Debug.assert(value !== undefined, "Value must not be 'undefined'");
onEmitNode = value;
}
},
get onBeforeEmitNode() { return onBeforeEmitNode || defaultOnBeforeOrAfterEmitNode; },
set onBeforeEmitNode(value) {
Debug.assert(state < TransformationState.Initialized, "Cannot modify transformation hooks after initialization has completed.");
Debug.assert(value !== undefined, "Value must not be 'undefined'");
onBeforeEmitNode = value;
},
get onAfterEmitNode() { return onAfterEmitNode || defaultOnBeforeOrAfterEmitNode; },
set onAfterEmitNode(value) {
Debug.assert(state < TransformationState.Initialized, "Cannot modify transformation hooks after initialization has completed.");
Debug.assert(value !== undefined, "Value must not be 'undefined'");
onAfterEmitNode = value;
},
};
// Ensure the parse tree is clean before applying transformations
@@ -159,11 +188,21 @@ namespace ts {
performance.mark("afterTransform");
performance.measure("transformTime", "beforeTransform", "afterTransform");
// for backwards compatibility, we push any current before/after events into the current callback.
if (onEmitNode && (onBeforeEmitNode || onAfterEmitNode)) {
onEmitNode = wrapNotification(onEmitNode, onBeforeEmitNode, onAfterEmitNode);
onBeforeEmitNode = undefined;
onAfterEmitNode = undefined;
}
return {
transformed,
substituteNode,
useEmitNodeWithNotification: onEmitNode !== undefined,
emitNodeWithNotification,
dispose
beforeEmitNode: onBeforeEmitNode ? beforeEmitNode : undefined,
afterEmitNode: onAfterEmitNode ? afterEmitNode : undefined,
dispose,
};
function transformRoot(node: T) {
@@ -215,6 +254,14 @@ namespace ts {
|| (getEmitFlags(node) & EmitFlags.AdviseOnEmitNode) !== 0;
}
function wrapNotification(onEmitNode: TransformationContext["onEmitNode"], onBeforeEmitNode: TransformationContext["onBeforeEmitNode"], onAfterEmitNode: TransformationContext["onAfterEmitNode"]): TransformationContext["onEmitNode"] {
return (hint, node, emitCallback) => {
if (onBeforeEmitNode) onBeforeEmitNode(hint, node);
onEmitNode(hint, node, emitCallback);
if (onAfterEmitNode) onAfterEmitNode(hint, node);
};
}
/**
* Emits a node with possible emit notification.
*
@@ -226,7 +273,7 @@ namespace ts {
Debug.assert(state < TransformationState.Disposed, "Cannot invoke TransformationResult callbacks after the result is disposed.");
if (node) {
if (isEmitNotificationEnabled(node)) {
onEmitNode(hint, node, emitCallback);
if (onEmitNode) onEmitNode(hint, node, emitCallback);
}
else {
emitCallback(hint, node);
@@ -234,6 +281,20 @@ namespace ts {
}
}
function beforeEmitNode(hint: EmitHint, node: Node) {
Debug.assert(state < TransformationState.Disposed, "Cannot invoke TransformationResult callbacks after the result is disposed.");
if (node && onBeforeEmitNode && isEmitNotificationEnabled(node)) {
onBeforeEmitNode(hint, node);
}
}
function afterEmitNode(hint: EmitHint, node: Node) {
Debug.assert(state < TransformationState.Disposed, "Cannot invoke TransformationResult callbacks after the result is disposed.");
if (node && onAfterEmitNode && isEmitNotificationEnabled(node)) {
onAfterEmitNode(hint, node);
}
}
/**
* Records a hoisted variable declaration for the provided name within a lexical environment.
*/
+17 -7
View File
@@ -272,12 +272,15 @@ namespace ts {
const compilerOptions = context.getCompilerOptions();
const resolver = context.getEmitResolver();
const previousOnSubstituteNode = context.onSubstituteNode;
const previousOnEmitNode = context.onEmitNode;
context.onEmitNode = onEmitNode;
const previousOnBeforeEmitNode = context.onBeforeEmitNode;
const previousOnAfterEmitNode = context.onAfterEmitNode;
context.onSubstituteNode = onSubstituteNode;
context.onBeforeEmitNode = onBeforeEmitNode;
context.onAfterEmitNode = onAfterEmitNode;
let currentSourceFile: SourceFile;
let currentText: string;
const hierarchyFactsStack: HierarchyFacts[] = [];
let hierarchyFacts: HierarchyFacts;
let taggedTemplateStringDeclarations: VariableDeclaration[];
@@ -3840,9 +3843,8 @@ namespace ts {
*
* @param hint A hint as to the intended usage of the node.
* @param node The node to be printed.
* @param emitCallback The callback used to emit the node.
*/
function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) {
function onBeforeEmitNode(hint: EmitHint, node: Node) {
if (enabledSubstitutions & ES2015SubstitutionFlags.CapturedThis && isFunctionLike(node)) {
// If we are tracking a captured `this`, keep track of the enclosing function.
const ancestorFacts = enterSubtree(
@@ -3850,11 +3852,19 @@ namespace ts {
getEmitFlags(node) & EmitFlags.CapturesThis
? HierarchyFacts.FunctionIncludes | HierarchyFacts.CapturesThis
: HierarchyFacts.FunctionIncludes);
previousOnEmitNode(hint, node, emitCallback);
hierarchyFactsStack.push(ancestorFacts);
}
previousOnBeforeEmitNode(hint, node);
}
function onAfterEmitNode(hint: EmitHint, node: Node) {
previousOnAfterEmitNode(hint, node);
if (enabledSubstitutions & ES2015SubstitutionFlags.CapturedThis && isFunctionLike(node)) {
const ancestorFacts = hierarchyFactsStack.pop();
exitSubtree(ancestorFacts, HierarchyFacts.None, HierarchyFacts.None);
return;
}
previousOnEmitNode(hint, node, emitCallback);
}
/**
+19 -15
View File
@@ -32,15 +32,18 @@ namespace ts {
* just-in-time substitution for `super` expressions inside of async methods.
*/
let enclosingSuperContainerFlags: NodeCheckFlags = 0;
const enclosingSuperContainerFlagsStack: NodeCheckFlags[] = [];
let enclosingFunctionParameterNames: UnderscoreEscapedMap<true>;
// Save the previous transformation hooks.
const previousOnEmitNode = context.onEmitNode;
const previousOnBeforeEmitNode = context.onBeforeEmitNode;
const previousOnAfterEmitNode = context.onAfterEmitNode;
const previousOnSubstituteNode = context.onSubstituteNode;
// Set new transformation hooks.
context.onEmitNode = onEmitNode;
context.onBeforeEmitNode = onBeforeEmitNode;
context.onAfterEmitNode = onAfterEmitNode;
context.onSubstituteNode = onSubstituteNode;
return transformSourceFile;
@@ -503,22 +506,23 @@ namespace ts {
*
* @param hint A hint as to the intended usage of the node.
* @param node The node to emit.
* @param emit A callback used to emit the node in the printer.
*/
function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void {
// If we need to support substitutions for `super` in an async method,
// we should track it here.
function onBeforeEmitNode(hint: EmitHint, node: Node) {
if (enabledSubstitutions & ES2017SubstitutionFlags.AsyncMethodsWithSuper && isSuperContainer(node)) {
const superContainerFlags = resolver.getNodeCheckFlags(node) & (NodeCheckFlags.AsyncMethodWithSuper | NodeCheckFlags.AsyncMethodWithSuperBinding);
if (superContainerFlags !== enclosingSuperContainerFlags) {
const savedEnclosingSuperContainerFlags = enclosingSuperContainerFlags;
enclosingSuperContainerFlags = superContainerFlags;
previousOnEmitNode(hint, node, emitCallback);
enclosingSuperContainerFlags = savedEnclosingSuperContainerFlags;
return;
}
enclosingSuperContainerFlagsStack.push(enclosingSuperContainerFlags);
enclosingSuperContainerFlags = resolver.getNodeCheckFlags(node) & (NodeCheckFlags.AsyncMethodWithSuper | NodeCheckFlags.AsyncMethodWithSuperBinding);
}
previousOnBeforeEmitNode(hint, node);
}
function onAfterEmitNode(hint: EmitHint, node: Node) {
previousOnAfterEmitNode(hint, node);
if (enabledSubstitutions & ES2017SubstitutionFlags.AsyncMethodsWithSuper && isSuperContainer(node)) {
enclosingSuperContainerFlags = enclosingSuperContainerFlagsStack.pop();
}
previousOnEmitNode(hint, node, emitCallback);
}
/**
+5 -6
View File
@@ -12,11 +12,11 @@ namespace ts {
const compilerOptions = context.getCompilerOptions();
// enable emit notification only if using --jsx preserve or react-native
let previousOnEmitNode: (hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) => void;
let previousOnBeforeEmitNode: TransformationContext["onBeforeEmitNode"];
let noSubstitution: boolean[];
if (compilerOptions.jsx === JsxEmit.Preserve || compilerOptions.jsx === JsxEmit.ReactNative) {
previousOnEmitNode = context.onEmitNode;
context.onEmitNode = onEmitNode;
previousOnBeforeEmitNode = context.onBeforeEmitNode;
context.onBeforeEmitNode = onBeforeEmitNode;
context.enableEmitNotification(SyntaxKind.JsxOpeningElement);
context.enableEmitNotification(SyntaxKind.JsxClosingElement);
context.enableEmitNotification(SyntaxKind.JsxSelfClosingElement);
@@ -43,9 +43,8 @@ namespace ts {
*
* @param hint A hint as to the intended usage of the node.
* @param node The node to emit.
* @param emitCallback A callback used to emit the node.
*/
function onEmitNode(hint: EmitHint, node: Node, emitCallback: (emitContext: EmitHint, node: Node) => void) {
function onBeforeEmitNode(hint: EmitHint, node: Node) {
switch (node.kind) {
case SyntaxKind.JsxOpeningElement:
case SyntaxKind.JsxClosingElement:
@@ -55,7 +54,7 @@ namespace ts {
break;
}
previousOnEmitNode(hint, node, emitCallback);
previousOnBeforeEmitNode(hint, node);
}
/**
+17 -12
View File
@@ -20,8 +20,10 @@ namespace ts {
const compilerOptions = context.getCompilerOptions();
const languageVersion = getEmitScriptTarget(compilerOptions);
const previousOnEmitNode = context.onEmitNode;
context.onEmitNode = onEmitNode;
const previousOnBeforeEmitNode = context.onBeforeEmitNode;
const previousOnAfterEmitNode = context.onAfterEmitNode;
context.onBeforeEmitNode = onBeforeEmitNode;
context.onAfterEmitNode = onAfterEmitNode;
const previousOnSubstituteNode = context.onSubstituteNode;
context.onSubstituteNode = onSubstituteNode;
@@ -29,6 +31,7 @@ namespace ts {
let enabledSubstitutions: ESNextSubstitutionFlags;
let enclosingFunctionFlags: FunctionFlags;
let enclosingSuperContainerFlags: NodeCheckFlags = 0;
const enclosingSuperContainerFlagsStack: NodeCheckFlags[] = [];
return transformSourceFile;
@@ -744,21 +747,23 @@ namespace ts {
* @param node The node to be printed.
* @param emitCallback The callback used to emit the node.
*/
function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) {
function onBeforeEmitNode(hint: EmitHint, node: Node) {
// If we need to support substitutions for `super` in an async method,
// we should track it here.
if (enabledSubstitutions & ESNextSubstitutionFlags.AsyncMethodsWithSuper && isSuperContainer(node)) {
const superContainerFlags = resolver.getNodeCheckFlags(node) & (NodeCheckFlags.AsyncMethodWithSuper | NodeCheckFlags.AsyncMethodWithSuperBinding);
if (superContainerFlags !== enclosingSuperContainerFlags) {
const savedEnclosingSuperContainerFlags = enclosingSuperContainerFlags;
enclosingSuperContainerFlags = superContainerFlags;
previousOnEmitNode(hint, node, emitCallback);
enclosingSuperContainerFlags = savedEnclosingSuperContainerFlags;
return;
}
enclosingSuperContainerFlagsStack.push(enclosingSuperContainerFlags);
enclosingSuperContainerFlags = resolver.getNodeCheckFlags(node) & (NodeCheckFlags.AsyncMethodWithSuper | NodeCheckFlags.AsyncMethodWithSuperBinding);
}
previousOnEmitNode(hint, node, emitCallback);
previousOnBeforeEmitNode(hint, node);
}
function onAfterEmitNode(hint: EmitHint, node: Node) {
previousOnAfterEmitNode(hint, node);
if (enabledSubstitutions & ESNextSubstitutionFlags.AsyncMethodsWithSuper && isSuperContainer(node)) {
enclosingSuperContainerFlags = enclosingSuperContainerFlagsStack.pop();
}
}
/**
+12 -8
View File
@@ -5,9 +5,11 @@
namespace ts {
export function transformES2015Module(context: TransformationContext) {
const compilerOptions = context.getCompilerOptions();
const previousOnEmitNode = context.onEmitNode;
const previousOnBeforeEmitNode = context.onBeforeEmitNode;
const previousOnAfterEmitNode = context.onAfterEmitNode;
const previousOnSubstituteNode = context.onSubstituteNode;
context.onEmitNode = onEmitNode;
context.onBeforeEmitNode = onBeforeEmitNode;
context.onAfterEmitNode = onAfterEmitNode;
context.onSubstituteNode = onSubstituteNode;
context.enableEmitNotification(SyntaxKind.SourceFile);
context.enableSubstitution(SyntaxKind.Identifier);
@@ -73,16 +75,18 @@ namespace ts {
*
* @param hint A hint as to the intended usage of the node.
* @param node The node to emit.
* @param emit A callback used to emit the node in the printer.
*/
function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void {
function onBeforeEmitNode(hint: EmitHint, node: Node): void {
if (isSourceFile(node)) {
currentSourceFile = node;
previousOnEmitNode(hint, node, emitCallback);
currentSourceFile = undefined;
}
else {
previousOnEmitNode(hint, node, emitCallback);
previousOnBeforeEmitNode(hint, node);
}
function onAfterEmitNode(hint: EmitHint, node: Node): void {
previousOnAfterEmitNode(hint, node);
if (isSourceFile(node)) {
currentSourceFile = undefined;
}
}
+14 -10
View File
@@ -30,10 +30,12 @@ namespace ts {
const host = context.getEmitHost();
const languageVersion = getEmitScriptTarget(compilerOptions);
const moduleKind = getEmitModuleKind(compilerOptions);
const previousOnBeforeEmitNode = context.onBeforeEmitNode;
const previousOnAfterEmitNode = context.onAfterEmitNode;
const previousOnSubstituteNode = context.onSubstituteNode;
const previousOnEmitNode = context.onEmitNode;
context.onBeforeEmitNode = onBeforeEmitNode;
context.onAfterEmitNode = onAfterEmitNode;
context.onSubstituteNode = onSubstituteNode;
context.onEmitNode = onEmitNode;
context.enableSubstitution(SyntaxKind.Identifier); // Substitutes expression identifiers with imported/exported symbols.
context.enableSubstitution(SyntaxKind.BinaryExpression); // Substitutes assignments to exported symbols.
context.enableSubstitution(SyntaxKind.PrefixUnaryExpression); // Substitutes updates to exported symbols.
@@ -1455,23 +1457,25 @@ namespace ts {
*
* @param hint A hint as to the intended usage of the node.
* @param node The node to emit.
* @param emit A callback used to emit the node in the printer.
*/
function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void {
if (node.kind === SyntaxKind.SourceFile) {
currentSourceFile = <SourceFile>node;
function onBeforeEmitNode(hint: EmitHint, node: Node): void {
if (isSourceFile(node)) {
currentSourceFile = node;
currentModuleInfo = moduleInfoMap[getOriginalNodeId(currentSourceFile)];
noSubstitution = [];
}
previousOnEmitNode(hint, node, emitCallback);
previousOnBeforeEmitNode(hint, node);
}
function onAfterEmitNode(hint: EmitHint, node: Node): void {
previousOnAfterEmitNode(hint, node);
if (isSourceFile(node)) {
currentSourceFile = undefined;
currentModuleInfo = undefined;
noSubstitution = undefined;
}
else {
previousOnEmitNode(hint, node, emitCallback);
}
}
//
+13 -12
View File
@@ -19,10 +19,12 @@ namespace ts {
const compilerOptions = context.getCompilerOptions();
const resolver = context.getEmitResolver();
const host = context.getEmitHost();
const previousOnBeforeEmitNode = context.onBeforeEmitNode;
const previousOnAfterEmitNode = context.onAfterEmitNode;
const previousOnSubstituteNode = context.onSubstituteNode;
const previousOnEmitNode = context.onEmitNode;
context.onBeforeEmitNode = onBeforeEmitNode;
context.onAfterEmitNode = onAfterEmitNode;
context.onSubstituteNode = onSubstituteNode;
context.onEmitNode = onEmitNode;
context.enableSubstitution(SyntaxKind.Identifier); // Substitutes expression identifiers for imported symbols.
context.enableSubstitution(SyntaxKind.ShorthandPropertyAssignment); // Substitutes expression identifiers for imported symbols
context.enableSubstitution(SyntaxKind.BinaryExpression); // Substitutes assignments to exported symbols.
@@ -1581,30 +1583,29 @@ namespace ts {
*
* @param hint A hint as to the intended usage of the node.
* @param node The node to emit.
* @param emitCallback A callback used to emit the node in the printer.
*/
function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void {
if (node.kind === SyntaxKind.SourceFile) {
function onBeforeEmitNode(hint: EmitHint, node: Node): void {
if (isSourceFile(node)) {
const id = getOriginalNodeId(node);
currentSourceFile = <SourceFile>node;
currentSourceFile = node;
moduleInfo = moduleInfoMap[id];
exportFunction = exportFunctionsMap[id];
noSubstitution = noSubstitutionMap[id];
if (noSubstitution) {
delete noSubstitutionMap[id];
}
}
previousOnBeforeEmitNode(hint, node);
}
previousOnEmitNode(hint, node, emitCallback);
function onAfterEmitNode(hint: EmitHint, node: Node) {
previousOnAfterEmitNode(hint, node);
if (isSourceFile(node)) {
currentSourceFile = undefined;
moduleInfo = undefined;
exportFunction = undefined;
noSubstitution = undefined;
}
else {
previousOnEmitNode(hint, node, emitCallback);
}
}
//
+29 -15
View File
@@ -50,11 +50,13 @@ namespace ts {
const moduleKind = getEmitModuleKind(compilerOptions);
// Save the previous transformation hooks.
const previousOnEmitNode = context.onEmitNode;
const previousOnBeforeEmitNode = context.onBeforeEmitNode;
const previousOnAfterEmitNode = context.onAfterEmitNode;
const previousOnSubstituteNode = context.onSubstituteNode;
// Set new transformation hooks.
context.onEmitNode = onEmitNode;
context.onBeforeEmitNode = onBeforeEmitNode;
context.onAfterEmitNode = onAfterEmitNode;
context.onSubstituteNode = onSubstituteNode;
// Enable substitution for property/element access to emit const enum values.
@@ -85,6 +87,7 @@ namespace ts {
* just-in-time substitution while printing an expression identifier.
*/
let applicableSubstitutions: TypeScriptSubstitutionFlags;
const applicableSubstitutionsStack: TypeScriptSubstitutionFlags[] = [];
/**
* Tracks what computed name expressions originating from elided names must be inlined
@@ -3389,28 +3392,39 @@ namespace ts {
*
* @param hint A hint as to the intended usage of the node.
* @param node The node to emit.
* @param emit A callback used to emit the node in the printer.
*/
function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void {
const savedApplicableSubstitutions = applicableSubstitutions;
const savedCurrentSourceFile = currentSourceFile;
function onBeforeEmitNode(hint: EmitHint, node: Node) {
if (isSourceFile(node)) {
currentSourceFile = node;
}
if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports && isTransformedModuleDeclaration(node)) {
applicableSubstitutions |= TypeScriptSubstitutionFlags.NamespaceExports;
const requiresNamespaceExports = enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports && isTransformedModuleDeclaration(node);
const requiresNonQualifiedEnumMembers = enabledSubstitutions & TypeScriptSubstitutionFlags.NonQualifiedEnumMembers && isTransformedEnumDeclaration(node);
if (requiresNamespaceExports || requiresNonQualifiedEnumMembers) {
applicableSubstitutionsStack.push(applicableSubstitutions);
if (requiresNamespaceExports) {
applicableSubstitutions |= TypeScriptSubstitutionFlags.NamespaceExports;
}
if (requiresNonQualifiedEnumMembers) {
applicableSubstitutions |= TypeScriptSubstitutionFlags.NonQualifiedEnumMembers;
}
}
if (enabledSubstitutions & TypeScriptSubstitutionFlags.NonQualifiedEnumMembers && isTransformedEnumDeclaration(node)) {
applicableSubstitutions |= TypeScriptSubstitutionFlags.NonQualifiedEnumMembers;
previousOnBeforeEmitNode(hint, node);
}
function onAfterEmitNode(hint: EmitHint, node: Node) {
previousOnAfterEmitNode(hint, node);
if (isSourceFile(node)) {
currentSourceFile = undefined;
}
previousOnEmitNode(hint, node, emitCallback);
applicableSubstitutions = savedApplicableSubstitutions;
currentSourceFile = savedCurrentSourceFile;
if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports && isTransformedModuleDeclaration(node) ||
enabledSubstitutions & TypeScriptSubstitutionFlags.NonQualifiedEnumMembers && isTransformedEnumDeclaration(node)) {
applicableSubstitutions = applicableSubstitutionsStack.pop();
}
}
/**
+8
View File
@@ -4817,6 +4817,8 @@ namespace ts {
* before returning the `NodeTransformer` callback.
*/
onEmitNode: (hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) => void;
/*@internal*/ onBeforeEmitNode?: (hint: EmitHint, node: Node) => void;
/*@internal*/ onAfterEmitNode?: (hint: EmitHint, node: Node) => void;
}
export interface TransformationResult<T extends Node> {
@@ -4847,6 +4849,10 @@ namespace ts {
* Clean up EmitNode entries on any parse-tree nodes.
*/
dispose(): void;
/*@internal*/ useEmitNodeWithNotification: boolean;
/*@internal*/ beforeEmitNode?(hint: EmitHint, node: Node): void;
/*@internal*/ afterEmitNode?(hint: EmitHint, node: Node): void;
}
/**
@@ -4942,6 +4948,8 @@ namespace ts {
* ```
*/
substituteNode?(hint: EmitHint, node: Node): Node;
/*@internal*/ onBeforeEmitNode?: (hint: EmitHint, node: Node) => void;
/*@internal*/ onAfterEmitNode?: (hint: EmitHint, node: Node) => void;
/*@internal*/ onEmitLeadingSourceMapOfNode?: (node: Node) => void;
/*@internal*/ onEmitTrailingSourceMapOfNode?: (node: Node) => void;
/*@internal*/ onEmitSourceMapOfToken?: (node: Node, token: SyntaxKind, writer: (s: string) => void, pos: number, emitCallback: (token: SyntaxKind, writer: (s: string) => void, pos: number) => number) => number;