Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ import {
createGetCanonicalFileName,
createGetSymbolWalker,
createModeAwareCacheKey,
createPrinter,
createOrReusePrinter,
createPropertyNameNodeForIdentifierOrLiteral,
createSymbolTable,
createTextWriter,
Expand Down Expand Up @@ -6123,7 +6123,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
function symbolToStringWorker(writer: EmitTextWriter) {
const entity = builder(symbol, meaning!, enclosingDeclaration, nodeFlags)!; // TODO: GH#18217
// add neverAsciiEscape for GH#39027
const printer = enclosingDeclaration?.kind === SyntaxKind.SourceFile ? createPrinter({ removeComments: true, neverAsciiEscape: true }) : createPrinter({ removeComments: true });
const printer = enclosingDeclaration?.kind === SyntaxKind.SourceFile ? createOrReusePrinter({ removeComments: true, neverAsciiEscape: true }) : createOrReusePrinter({ removeComments: true });
const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration);
printer.writeNode(EmitHint.Unspecified, entity, /*sourceFile*/ sourceFile, writer);
return writer;
Expand All @@ -6142,7 +6142,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
sigOutput = kind === SignatureKind.Construct ? SyntaxKind.ConstructSignature : SyntaxKind.CallSignature;
}
const sig = nodeBuilder.signatureToSignatureDeclaration(signature, sigOutput, enclosingDeclaration, toNodeBuilderFlags(flags) | NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.WriteTypeParametersInQualifiedName);
const printer = createPrinter({ removeComments: true, omitTrailingSemicolon: true });
const printer = createOrReusePrinter({ removeComments: true, omitTrailingSemicolon: true });
const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration);
printer.writeNode(EmitHint.Unspecified, sig!, /*sourceFile*/ sourceFile, getTrailingSemicolonDeferringWriter(writer)); // TODO: GH#18217
return writer;
Expand All @@ -6156,7 +6156,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// The unresolved type gets a synthesized comment on `any` to hint to users that it's not a plain `any`.
// Otherwise, we always strip comments out.
const options = { removeComments: type !== unresolvedType };
const printer = createPrinter(options);
const printer = createOrReusePrinter(options);
const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration);
printer.writeNode(EmitHint.Unspecified, typeNode, /*sourceFile*/ sourceFile, writer);
const result = writer.getText();
Expand Down Expand Up @@ -9636,7 +9636,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
typePredicate.kind === TypePredicateKind.Identifier || typePredicate.kind === TypePredicateKind.AssertsIdentifier ? factory.createIdentifier(typePredicate.parameterName) : factory.createThisTypeNode(),
typePredicate.type && nodeBuilder.typeToTypeNode(typePredicate.type, enclosingDeclaration, toNodeBuilderFlags(flags) | NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.WriteTypeParametersInQualifiedName)! // TODO: GH#18217
);
const printer = createPrinter({ removeComments: true });
const printer = createOrReusePrinter({ removeComments: true });
const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration);
printer.writeNode(EmitHint.Unspecified, predicate, /*sourceFile*/ sourceFile, writer);
return writer;
Expand Down
117 changes: 77 additions & 40 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1343,19 +1343,44 @@ const enum PipelinePhase {
Emit,
}

const printerCache = new Map<string, Printer>();

// If updating this, make sure the cache key is updated too.
/** @internal */
export type ReusablePrinterOptions = Pick<PrinterOptions, "removeComments" | "neverAsciiEscape" | "omitTrailingSemicolon" | "module" | "target" | "newLine" | "preserveSourceNewlines" | "terminateUnterminatedLiterals">;

/** @internal */
export function createOrReusePrinter(o: ReusablePrinterOptions = {}) {
const key = `${keyBool(o.removeComments)}|${keyBool(o.neverAsciiEscape)}|${keyBool(o.omitTrailingSemicolon)}|${keyNum(o.module)}|${keyNum(o.target)}|${keyNum(o.newLine)}|${keyBool(o.preserveSourceNewlines)}|${keyBool(o.terminateUnterminatedLiterals)}`;
let printer = printerCache.get(key);
if (!printer) {
printerCache.set(key, printer = createPrinter(o));
}
return printer;

function keyBool(value: boolean | undefined) {
return value === undefined ? "u" : value ? 1 : 0;
}

function keyNum(value: number | undefined) {
return value === undefined ? "u" : `${value}`;
}
}

export function createPrinter(printerOptions: PrinterOptions = {}, handlers: PrintHandlers = {}): Printer {
const {
hasGlobalName,
onEmitNode = noEmitNotification,
isEmitNotificationEnabled,
substituteNode = noEmitSubstitution,
onBeforeEmitNode,
onAfterEmitNode,
onBeforeEmitNodeArray,
onAfterEmitNodeArray,
onBeforeEmitToken,
onAfterEmitToken
} = handlers;
let theHandlers!: PrintHandlers & Required<Pick<PrintHandlers, "onEmitNode" | "substituteNode">>;

function setHandlers(newHandlers?: PrintHandlers) {
if (newHandlers) {
theHandlers = {
onEmitNode: noEmitNotification,
substituteNode: noEmitSubstitution,
...newHandlers,
};
}
}

setHandlers(handlers);

const extendedDiagnostics = !!printerOptions.extendedDiagnostics;
const newLine = getNewLineCharacter(printerOptions);
Expand Down Expand Up @@ -1475,25 +1500,31 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
/**
* If `sourceFile` is `undefined`, `node` must be a synthesized `TypeNode`.
*/
function writeNode(hint: EmitHint, node: TypeNode, sourceFile: undefined, output: EmitTextWriter): void;
function writeNode(hint: EmitHint, node: Node, sourceFile: SourceFile, output: EmitTextWriter): void;
function writeNode(hint: EmitHint, node: Node, sourceFile: SourceFile | undefined, output: EmitTextWriter) {
function writeNode(hint: EmitHint, node: TypeNode, sourceFile: undefined, output: EmitTextWriter, handlers?: PrintHandlers): void;
function writeNode(hint: EmitHint, node: Node, sourceFile: SourceFile, output: EmitTextWriter, handlers?: PrintHandlers): void;
function writeNode(hint: EmitHint, node: Node, sourceFile: SourceFile | undefined, output: EmitTextWriter, handlers?: PrintHandlers) {
const previousWriter = writer;
const previousHandlers = theHandlers;
setHandlers(handlers);
setWriter(output, /*_sourceMapGenerator*/ undefined);
print(hint, node, sourceFile);
reset();
writer = previousWriter;
theHandlers = previousHandlers;
}

function writeList<T extends Node>(format: ListFormat, nodes: NodeArray<T>, sourceFile: SourceFile | undefined, output: EmitTextWriter) {
function writeList<T extends Node>(format: ListFormat, nodes: NodeArray<T>, sourceFile: SourceFile | undefined, output: EmitTextWriter, handlers?: PrintHandlers) {
const previousWriter = writer;
const previousHandlers = theHandlers;
setHandlers(handlers);
setWriter(output, /*_sourceMapGenerator*/ undefined);
if (sourceFile) {
setSourceFile(sourceFile);
}
emitList(/*parentNode*/ undefined, nodes, format);
reset();
writer = previousWriter;
theHandlers = previousHandlers;
}

function getTextPosWithWriteLine() {
Expand Down Expand Up @@ -1542,9 +1573,11 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
return false;
}

function writeBundle(bundle: Bundle, output: EmitTextWriter, sourceMapGenerator: SourceMapGenerator | undefined) {
function writeBundle(bundle: Bundle, output: EmitTextWriter, sourceMapGenerator: SourceMapGenerator | undefined, handlers?: PrintHandlers) {
isOwnFileEmit = false;
const previousWriter = writer;
const previousHandlers = theHandlers;
setHandlers(handlers);
setWriter(output, sourceMapGenerator);
emitShebangIfNeeded(bundle);
emitPrologueDirectivesIfNeeded(bundle);
Expand Down Expand Up @@ -1599,6 +1632,7 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri

reset();
writer = previousWriter;
theHandlers = previousHandlers;
}

function writeUnparsedSource(unparsed: UnparsedSource, output: EmitTextWriter) {
Expand All @@ -1609,15 +1643,18 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
writer = previousWriter;
}

function writeFile(sourceFile: SourceFile, output: EmitTextWriter, sourceMapGenerator: SourceMapGenerator | undefined) {
function writeFile(sourceFile: SourceFile, output: EmitTextWriter, sourceMapGenerator: SourceMapGenerator | undefined, handlers?: PrintHandlers) {
isOwnFileEmit = true;
const previousWriter = writer;
const previousHandlers = theHandlers;
setHandlers(handlers);
setWriter(output, sourceMapGenerator);
emitShebangIfNeeded(sourceFile);
emitPrologueDirectivesIfNeeded(sourceFile);
print(EmitHint.SourceFile, sourceFile, sourceFile);
reset();
writer = previousWriter;
theHandlers = previousHandlers;
}

function beginPrint() {
Expand Down Expand Up @@ -1741,12 +1778,12 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
function getPipelinePhase(phase: PipelinePhase, emitHint: EmitHint, node: Node) {
switch (phase) {
case PipelinePhase.Notification:
if (onEmitNode !== noEmitNotification && (!isEmitNotificationEnabled || isEmitNotificationEnabled(node))) {
if (theHandlers.onEmitNode !== noEmitNotification && (!theHandlers.isEmitNotificationEnabled || theHandlers.isEmitNotificationEnabled(node))) {
return pipelineEmitWithNotification;
}
// falls through
case PipelinePhase.Substitution:
if (substituteNode !== noEmitSubstitution && (lastSubstitution = substituteNode(emitHint, node) || node) !== node) {
if (theHandlers.substituteNode !== noEmitSubstitution && (lastSubstitution = theHandlers.substituteNode(emitHint, node) || node) !== node) {
if (currentParenthesizerRule) {
lastSubstitution = currentParenthesizerRule(lastSubstitution);
}
Expand Down Expand Up @@ -1776,11 +1813,11 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri

function pipelineEmitWithNotification(hint: EmitHint, node: Node) {
const pipelinePhase = getNextPipelinePhase(PipelinePhase.Notification, hint, node);
onEmitNode(hint, node, pipelinePhase);
theHandlers.onEmitNode(hint, node, pipelinePhase);
}

function pipelineEmitWithHint(hint: EmitHint, node: Node): void {
onBeforeEmitNode?.(node);
theHandlers.onBeforeEmitNode?.(node);
if (preserveSourceNewlines) {
const savedPreserveSourceNewlines = preserveSourceNewlines;
beforeEmitNode(node);
Expand All @@ -1790,7 +1827,7 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
else {
pipelineEmitWithHintWorker(hint, node);
}
onAfterEmitNode?.(node);
theHandlers.onAfterEmitNode?.(node);
// clear the parenthesizer rule as we ascend
currentParenthesizerRule = undefined;
}
Expand Down Expand Up @@ -2161,8 +2198,8 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
}
if (isExpression(node)) {
hint = EmitHint.Expression;
if (substituteNode !== noEmitSubstitution) {
const substitute = substituteNode(hint, node) || node;
if (theHandlers.substituteNode !== noEmitSubstitution) {
const substitute = theHandlers.substituteNode(hint, node) || node;
if (substitute !== node) {
node = substitute;
if (currentParenthesizerRule) {
Expand Down Expand Up @@ -3203,7 +3240,7 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
state.declarationListContainerEndStack[state.stackIndex] = declarationListContainerEnd;
const emitComments = state.shouldEmitCommentsStack[state.stackIndex] = shouldEmitComments(node);
const emitSourceMaps = state.shouldEmitSourceMapsStack[state.stackIndex] = shouldEmitSourceMaps(node);
onBeforeEmitNode?.(node);
theHandlers.onBeforeEmitNode?.(node);
if (emitComments) emitCommentsBeforeNode(node);
if (emitSourceMaps) emitSourceMapsBeforeNode(node);
beforeEmitNode(node);
Expand Down Expand Up @@ -3255,7 +3292,7 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
afterEmitNode(savedPreserveSourceNewlines);
if (shouldEmitSourceMaps) emitSourceMapsAfterNode(node);
if (shouldEmitComments) emitCommentsAfterNode(node, savedContainerPos, savedContainerEnd, savedDeclarationListContainerEnd);
onAfterEmitNode?.(node);
theHandlers.onAfterEmitNode?.(node);
state.stackIndex--;
}
}
Expand Down Expand Up @@ -3756,7 +3793,7 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
}

function emitBlockFunctionBody(body: Block) {
onBeforeEmitNode?.(body);
theHandlers.onBeforeEmitNode?.(body);
writeSpace();
writePunctuation("{");
increaseIndent();
Expand All @@ -3769,7 +3806,7 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri

decreaseIndent();
writeToken(SyntaxKind.CloseBraceToken, body.statements.end, writePunctuation, body);
onAfterEmitNode?.(body);
theHandlers.onAfterEmitNode?.(body);
}

function emitBlockFunctionBodyOnSingleLine(body: Block) {
Expand Down Expand Up @@ -4737,7 +4774,7 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
return node.pos;
}

onBeforeEmitNodeArray?.(modifiers);
theHandlers.onBeforeEmitNodeArray?.(modifiers);

// partition modifiers into contiguous chunks of `Modifier` or `Decorator`
let lastMode: "modifiers" | "decorators" | undefined;
Expand Down Expand Up @@ -4779,7 +4816,7 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
pos++;
}

onAfterEmitNodeArray?.(modifiers);
theHandlers.onAfterEmitNodeArray?.(modifiers);

if (lastModifier && !positionIsSynthesized(lastModifier.end)) {
return lastModifier.end;
Expand Down Expand Up @@ -4954,8 +4991,8 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri

const isEmpty = children === undefined || start >= children.length || count === 0;
if (isEmpty && format & ListFormat.OptionalIfEmpty) {
onBeforeEmitNodeArray?.(children);
onAfterEmitNodeArray?.(children);
theHandlers.onBeforeEmitNodeArray?.(children);
theHandlers.onAfterEmitNodeArray?.(children);
return;
}

Expand All @@ -4966,7 +5003,7 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
}
}

onBeforeEmitNodeArray?.(children);
theHandlers.onBeforeEmitNodeArray?.(children);

if (isEmpty) {
// Write a line terminator if the parent node was multi-line
Expand All @@ -4981,7 +5018,7 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
emitNodeListItems(emit, parentNode, children, format, parenthesizerRule, start, count, children.hasTrailingComma, children);
}

onAfterEmitNodeArray?.(children);
theHandlers.onAfterEmitNodeArray?.(children);

if (format & ListFormat.BracketsMask) {
if (isEmpty && children) {
Expand Down Expand Up @@ -5207,12 +5244,12 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
}

function writeTokenNode(node: Node, writer: (s: string) => void) {
if (onBeforeEmitToken) {
onBeforeEmitToken(node);
if (theHandlers.onBeforeEmitToken) {
theHandlers.onBeforeEmitToken(node);
}
writer(tokenToString(node.kind)!);
if (onAfterEmitToken) {
onAfterEmitToken(node);
if (theHandlers.onAfterEmitToken) {
theHandlers.onAfterEmitToken(node);
}
}

Expand Down Expand Up @@ -5754,7 +5791,7 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
* when `isfileLevelUniqueName` is passed as a callback to `makeUniqueName`.
*/
function isFileLevelUniqueName(name: string, _isPrivate: boolean) {
return currentSourceFile ? ts.isFileLevelUniqueName(currentSourceFile, name, hasGlobalName) : true;
return currentSourceFile ? ts.isFileLevelUniqueName(currentSourceFile, name, theHandlers.hasGlobalName) : true;
}

/**
Expand Down
8 changes: 4 additions & 4 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9160,10 +9160,10 @@ export interface Printer {
* Prints a bundle of source files as-is, without any emit transformations.
*/
printBundle(bundle: Bundle): string;
/** @internal */ writeNode(hint: EmitHint, node: Node, sourceFile: SourceFile | undefined, writer: EmitTextWriter): void;
/** @internal */ writeList<T extends Node>(format: ListFormat, list: NodeArray<T> | undefined, sourceFile: SourceFile | undefined, writer: EmitTextWriter): void;
/** @internal */ writeFile(sourceFile: SourceFile, writer: EmitTextWriter, sourceMapGenerator: SourceMapGenerator | undefined): void;
/** @internal */ writeBundle(bundle: Bundle, writer: EmitTextWriter, sourceMapGenerator: SourceMapGenerator | undefined): void;
/** @internal */ writeNode(hint: EmitHint, node: Node, sourceFile: SourceFile | undefined, writer: EmitTextWriter, handlers?: PrintHandlers): void;
/** @internal */ writeList<T extends Node>(format: ListFormat, list: NodeArray<T> | undefined, sourceFile: SourceFile | undefined, writer: EmitTextWriter, handlers?: PrintHandlers): void;
/** @internal */ writeFile(sourceFile: SourceFile, writer: EmitTextWriter, sourceMapGenerator: SourceMapGenerator | undefined, handlers?: PrintHandlers): void;
/** @internal */ writeBundle(bundle: Bundle, writer: EmitTextWriter, sourceMapGenerator: SourceMapGenerator | undefined, handlers?: PrintHandlers): void;
/** @deprecated @internal */ bundleFileInfo?: BundleFileInfo;
}

Expand Down
Loading