From a295aa8fd1934b43e8f26e41da2550656e615209 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Thu, 9 Feb 2017 13:10:17 -0800 Subject: [PATCH] Reduce stack depth due to substitution --- src/compiler/emitter.ts | 26 +++++++++++--------------- src/compiler/transformer.ts | 13 ++++--------- src/compiler/transformers/es2015.ts | 4 ++-- src/compiler/transformers/ts.ts | 2 -- src/compiler/types.ts | 16 ++++++---------- src/compiler/visitor.ts | 6 +++--- src/harness/unittests/transform.ts | 2 +- 7 files changed, 27 insertions(+), 42 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 4b3fb5dac9a..46a34fd9a18 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -37,7 +37,7 @@ namespace ts { // transform hooks onEmitNode: transform.emitNodeWithNotification, - onSubstituteNode: transform.emitNodeWithSubstitution, + substituteNode: transform.substituteNode, // sourcemap hooks onEmitSourceMapOfNode: sourceMap.emitNodeWithSourceMap, @@ -198,7 +198,7 @@ namespace ts { onEmitNode, onEmitHelpers, onSetSourceFile, - onSubstituteNode, + substituteNode, } = handlers; const newLine = getNewLineCharacter(printerOptions); @@ -327,8 +327,8 @@ namespace ts { setWriter(/*output*/ undefined); } - function emit(node: Node, hint = EmitHint.Unspecified) { - pipelineEmitWithNotification(hint, node); + function emit(node: Node) { + pipelineEmitWithNotification(EmitHint.Unspecified, node); } function emitIdentifierName(node: Identifier) { @@ -349,6 +349,7 @@ namespace ts { } function pipelineEmitWithComments(hint: EmitHint, node: Node) { + node = trySubstituteNode(hint, node); if (emitNodeWithComments && hint !== EmitHint.SourceFile) { emitNodeWithComments(hint, node, pipelineEmitWithSourceMap); } @@ -359,16 +360,7 @@ namespace ts { function pipelineEmitWithSourceMap(hint: EmitHint, node: Node) { if (onEmitSourceMapOfNode && hint !== EmitHint.SourceFile && hint !== EmitHint.IdentifierName) { - onEmitSourceMapOfNode(hint, node, pipelineEmitWithSubstitution); - } - else { - pipelineEmitWithSubstitution(hint, node); - } - } - - function pipelineEmitWithSubstitution(hint: EmitHint, node: Node) { - if (onSubstituteNode) { - onSubstituteNode(hint, node, pipelineEmitWithHint); + onEmitSourceMapOfNode(hint, node, pipelineEmitWithHint); } else { pipelineEmitWithHint(hint, node); @@ -634,7 +626,7 @@ namespace ts { // If the node is an expression, try to emit it as an expression with // substitution. if (isExpression(node)) { - return pipelineEmitWithSubstitution(EmitHint.Expression, node); + return pipelineEmitExpression(trySubstituteNode(EmitHint.Expression, node)); } } @@ -731,6 +723,10 @@ namespace ts { } } + function trySubstituteNode(hint: EmitHint, node: Node) { + return node && substituteNode && substituteNode(hint, node) || node; + } + function emitBodyIndirect(node: Node, elements: NodeArray, emitCallback: (node: Node) => void): void { if (emitBodyWithDetachedComments) { emitBodyWithDetachedComments(node, elements, emitCallback); diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index 097a84b2cc9..b9a75015f41 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -159,7 +159,7 @@ namespace ts { return { transformed, - emitNodeWithSubstitution, + substituteNode, emitNodeWithNotification, dispose }; @@ -191,14 +191,9 @@ namespace ts { * @param node The node to emit. * @param emitCallback The callback used to emit the node or its substitute. */ - function emitNodeWithSubstitution(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) { - Debug.assert(state < TransformationState.Disposed, "Cannot invoke TransformationResult callbacks after the result is disposed."); - if (node) { - if (isSubstitutionEnabled(node)) { - node = onSubstituteNode(hint, node) || node; - } - emitCallback(hint, node); - } + function substituteNode(hint: EmitHint, node: Node) { + Debug.assert(state < TransformationState.Disposed, "Cannot substitute a node after the result is disposed."); + return node && isSubstitutionEnabled(node) && onSubstituteNode(hint, node) || node; } /** diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index e0fd435a90d..25429ca783c 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -3549,7 +3549,7 @@ namespace ts { if (enabledSubstitutions & ES2015SubstitutionFlags.BlockScopedBindings) { const original = getParseTreeNode(node, isIdentifier); if (original && isNameOfDeclarationWithCollidingName(original)) { - return getGeneratedNameForNode(original); + return setTextRange(getGeneratedNameForNode(original), node); } } @@ -3602,7 +3602,7 @@ namespace ts { if (enabledSubstitutions & ES2015SubstitutionFlags.BlockScopedBindings) { const declaration = resolver.getReferencedDeclarationWithCollidingName(node); if (declaration) { - return getGeneratedNameForNode(declaration.name); + return setTextRange(getGeneratedNameForNode(declaration.name), node); } } diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index c28d74f8527..5aebc645ab7 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -3322,8 +3322,6 @@ namespace ts { : getTextOfNode(node.argumentExpression); addSyntheticTrailingComment(substitute, SyntaxKind.MultiLineCommentTrivia, ` ${propertyName} `); - // wrap the substituted node so that it emits its own comments. - return createPartiallyEmittedExpression(substitute); } return substitute; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 7cdace0bd7f..064e5330e9e 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3901,13 +3901,12 @@ diagnostics?: Diagnostic[]; /** - * Emits the substitute for a node, if one is available; otherwise, emits the node. + * Gets a substitute for a node, if one is available; otherwise, returns the original node. * * @param hint A hint as to the intended usage of the node. * @param node The node to substitute. - * @param emitCallback A callback used to emit the node or its substitute. */ - emitNodeWithSubstitution(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void; + substituteNode(hint: EmitHint, node: Node): Node; /** * Emits a node with possible notification. @@ -3998,23 +3997,20 @@ /** * A hook used by the Printer to perform just-in-time substitution of a node. This is * primarily used by node transformations that need to substitute one node for another, - * such as replacing `myExportedVar` with `exports.myExportedVar`. A compatible - * implementation **must** invoke `emitCallback` eith the provided `hint` and either - * the provided `node`, or its substitute. + * such as replacing `myExportedVar` with `exports.myExportedVar`. * @param hint A hint indicating the intended purpose of the node. * @param node The node to emit. - * @param emitCallback A callback that, when invoked, will emit the node. * @example * ```ts * var printer = createPrinter(printerOptions, { - * onSubstituteNode(hint, node, emitCallback) { + * substituteNode(hint, node) { * // perform substitution if necessary... - * emitCallback(hint, node); + * return node; * } * }); * ``` */ - onSubstituteNode?(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void; + substituteNode?(hint: EmitHint, node: Node): Node; /*@internal*/ onEmitSourceMapOfNode?: (hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) => void; /*@internal*/ onEmitSourceMapOfToken?: (node: Node, token: SyntaxKind, pos: number, emitCallback: (token: SyntaxKind, pos: number) => number) => number; /*@internal*/ onEmitSourceMapOfPosition?: (pos: number) => void; diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index bc6afacff66..9d3ab2ea203 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -709,7 +709,7 @@ namespace ts { return node ? f(initial, node) : initial; } - function reduceNodeArray(nodes: Node[], f: (memo: T, nodes: Node[]) => T, initial: T) { + function reduceNodeArray(nodes: NodeArray, f: (memo: T, nodes: NodeArray) => T, initial: T) { return nodes ? f(initial, nodes) : initial; } @@ -721,12 +721,12 @@ namespace ts { * @param initial The initial value to supply to the reduction. * @param f The callback function */ - export function reduceEachChild(node: Node, initial: T, cbNode: (memo: T, node: Node) => T, cbNodeArray?: (memo: T, nodes: Node[]) => T): T { + export function reduceEachChild(node: Node, initial: T, cbNode: (memo: T, node: Node) => T, cbNodeArray?: (memo: T, nodes: NodeArray) => T): T { if (node === undefined) { return initial; } - const reduceNodes: (nodes: Node[], f: (memo: T, node: Node | Node[]) => T, initial: T) => T = cbNodeArray ? reduceNodeArray : reduceLeft; + const reduceNodes: (nodes: NodeArray, f: (memo: T, node: Node | NodeArray) => T, initial: T) => T = cbNodeArray ? reduceNodeArray : reduceLeft; const cbNodes = cbNodeArray || cbNode; const kind = node.kind; diff --git a/src/harness/unittests/transform.ts b/src/harness/unittests/transform.ts index 2f010028d17..ae2caf4f23b 100644 --- a/src/harness/unittests/transform.ts +++ b/src/harness/unittests/transform.ts @@ -9,7 +9,7 @@ namespace ts { const transformed = transform(createSourceFile("source.ts", source, ScriptTarget.ES2015), transformers); const printer = createPrinter({ newLine: NewLineKind.CarriageReturnLineFeed }, { onEmitNode: transformed.emitNodeWithNotification, - onSubstituteNode: transformed.emitNodeWithSubstitution + substituteNode: transformed.substituteNode }); const result = printer.printBundle(createBundle(transformed.transformed)); transformed.dispose();