diff --git a/internal/checker/checker.go b/internal/checker/checker.go index 51f0027493..2c8bb69be8 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -30572,7 +30572,7 @@ func (c *Checker) GetTypeAtLocation(node *ast.Node) *Type { return c.getTypeOfNode(node) } -func (c *Checker) GetEmitResolver(file *ast.SourceFile) *emitResolver { +func (c *Checker) GetEmitResolver() *emitResolver { c.emitResolverOnce.Do(func() { c.emitResolver = &emitResolver{checker: c} }) diff --git a/internal/checker/nodebuilderimpl.go b/internal/checker/nodebuilderimpl.go index 72a4cd1f98..015d42f55b 100644 --- a/internal/checker/nodebuilderimpl.go +++ b/internal/checker/nodebuilderimpl.go @@ -1450,7 +1450,7 @@ func (b *nodeBuilderImpl) symbolToParameterDeclaration(parameterSymbol *ast.Symb } name := b.parameterToParameterDeclarationName(parameterSymbol, parameterDeclaration) // TODO: isOptionalParameter on emit resolver here is silly - hoist to checker and reexpose on emit resolver? - isOptional := parameterDeclaration != nil && b.ch.GetEmitResolver(nil).isOptionalParameter(parameterDeclaration) || parameterSymbol.CheckFlags&ast.CheckFlagsOptionalParameter != 0 + isOptional := parameterDeclaration != nil && b.ch.GetEmitResolver().isOptionalParameter(parameterDeclaration) || parameterSymbol.CheckFlags&ast.CheckFlagsOptionalParameter != 0 var questionToken *ast.Node if isOptional { questionToken = b.f.NewToken(ast.KindQuestionToken) @@ -1887,7 +1887,7 @@ func (b *nodeBuilderImpl) serializeTypeForDeclaration(declaration *ast.Declarati } } // !!! TODO: JSDoc, getEmitResolver call is unfortunate layering for the helper - hoist it into checker - addUndefinedForParameter := declaration != nil && (ast.IsParameter(declaration) /*|| ast.IsJSDocParameterTag(declaration)*/) && b.ch.GetEmitResolver(nil).requiresAddingImplicitUndefined(declaration, symbol, b.ctx.enclosingDeclaration) + addUndefinedForParameter := declaration != nil && (ast.IsParameter(declaration) /*|| ast.IsJSDocParameterTag(declaration)*/) && b.ch.GetEmitResolver().requiresAddingImplicitUndefined(declaration, symbol, b.ctx.enclosingDeclaration) if addUndefinedForParameter { t = b.ch.getOptionalType(t, false) } diff --git a/internal/checker/symbolaccessibility.go b/internal/checker/symbolaccessibility.go index c34d1d3bd2..cd47e3c337 100644 --- a/internal/checker/symbolaccessibility.go +++ b/internal/checker/symbolaccessibility.go @@ -38,7 +38,7 @@ func (ch *Checker) IsAnySymbolAccessible(symbols []*ast.Symbol, enclosingDeclara if len(accessibleSymbolChain) > 0 { hadAccessibleChain = symbol // TODO: going through emit resolver here is weird. Relayer these APIs. - hasAccessibleDeclarations := ch.GetEmitResolver(nil).hasVisibleDeclarations(accessibleSymbolChain[0], shouldComputeAliasesToMakeVisible) + hasAccessibleDeclarations := ch.GetEmitResolver().hasVisibleDeclarations(accessibleSymbolChain[0], shouldComputeAliasesToMakeVisible) if hasAccessibleDeclarations != nil { return hasAccessibleDeclarations } diff --git a/internal/compiler/emitHost.go b/internal/compiler/emitHost.go index 9161e1f35f..d1db60a4d9 100644 --- a/internal/compiler/emitHost.go +++ b/internal/compiler/emitHost.go @@ -32,14 +32,22 @@ type EmitHost interface { GetCurrentDirectory() string CommonSourceDirectory() string IsEmitBlocked(file string) bool - GetEmitResolver(file *ast.SourceFile) printer.EmitResolver } var _ EmitHost = (*emitHost)(nil) // NOTE: emitHost operations must be thread-safe type emitHost struct { - program *Program + program *Program + emitResolver printer.EmitResolver +} + +func newEmitHost(ctx context.Context, program *Program, file *ast.SourceFile) (*emitHost, func()) { + checker, done := program.GetTypeCheckerForFile(ctx, file) + return &emitHost{ + program: program, + emitResolver: checker.GetEmitResolver(), + }, done } func (host *emitHost) GetModeForUsageLocation(file ast.HasFileName, moduleSpecifier *ast.StringLiteralLike) core.ResolutionMode { @@ -83,7 +91,7 @@ func (host *emitHost) GetRedirectTargets(path tspath.Path) []string { } func (host *emitHost) GetEffectiveDeclarationFlags(node *ast.Node, flags ast.ModifierFlags) ast.ModifierFlags { - return host.GetEmitResolver(ast.GetSourceFileOfNode(node)).GetEffectiveDeclarationFlags(node, flags) + return host.GetEmitResolver().GetEffectiveDeclarationFlags(node, flags) } func (host *emitHost) GetOutputPathsFor(file *ast.SourceFile, forceDtsPaths bool) declarations.OutputPaths { @@ -92,7 +100,7 @@ func (host *emitHost) GetOutputPathsFor(file *ast.SourceFile, forceDtsPaths bool } func (host *emitHost) GetResolutionModeOverride(node *ast.Node) core.ResolutionMode { - return host.GetEmitResolver(ast.GetSourceFileOfNode(node)).GetResolutionModeOverride(node) + return host.GetEmitResolver().GetResolutionModeOverride(node) } func (host *emitHost) GetSourceFileFromReference(origin *ast.SourceFile, ref *ast.FileReference) *ast.SourceFile { @@ -116,13 +124,8 @@ func (host *emitHost) WriteFile(fileName string, text string, writeByteOrderMark return host.program.Host().FS().WriteFile(fileName, text, writeByteOrderMark) } -func (host *emitHost) GetEmitResolver(file *ast.SourceFile) printer.EmitResolver { - // The context and done function don't matter in tsc, currently the only caller of this function. - // But if this ever gets used by LSP code, we'll need to thread the context properly and pass the - // done function to the caller to ensure resources are cleaned up at the end of the request. - checker, done := host.program.GetTypeCheckerForFile(context.TODO(), file) - defer done() - return checker.GetEmitResolver(file) +func (host *emitHost) GetEmitResolver() printer.EmitResolver { + return host.emitResolver } func (host *emitHost) IsSourceFileFromExternalLibrary(file *ast.SourceFile) bool { diff --git a/internal/compiler/emitter.go b/internal/compiler/emitter.go index 9cd30237ba..b031647c26 100644 --- a/internal/compiler/emitter.go +++ b/internal/compiler/emitter.go @@ -49,9 +49,8 @@ func (e *emitter) emit() { e.emitBuildInfo(e.paths.BuildInfoPath()) } -func (e *emitter) getDeclarationTransformers(emitContext *printer.EmitContext, sourceFile *ast.SourceFile, declarationFilePath string, declarationMapPath string) []*declarations.DeclarationTransformer { - emitResolver := e.host.GetEmitResolver(sourceFile) - transform := declarations.NewDeclarationTransformer(e.host, emitResolver, emitContext, e.host.Options(), declarationFilePath, declarationMapPath) +func (e *emitter) getDeclarationTransformers(emitContext *printer.EmitContext, declarationFilePath string, declarationMapPath string) []*declarations.DeclarationTransformer { + transform := declarations.NewDeclarationTransformer(e.host, emitContext, e.host.Options(), declarationFilePath, declarationMapPath) return []*declarations.DeclarationTransformer{transform} } @@ -86,7 +85,7 @@ func getScriptTransformers(emitContext *printer.EmitContext, host printer.EmitHo var emitResolver printer.EmitResolver var referenceResolver binder.ReferenceResolver if importElisionEnabled || options.GetJSXTransformEnabled() { - emitResolver = host.GetEmitResolver(sourceFile) + emitResolver = host.GetEmitResolver() emitResolver.MarkLinkedReferencesRecursively(sourceFile) referenceResolver = emitResolver } else { @@ -179,7 +178,7 @@ func (e *emitter) emitDeclarationFile(sourceFile *ast.SourceFile, declarationFil var diags []*ast.Diagnostic emitContext, putEmitContext := printer.GetEmitContext() defer putEmitContext() - for _, transformer := range e.getDeclarationTransformers(emitContext, sourceFile, declarationFilePath, declarationMapPath) { + for _, transformer := range e.getDeclarationTransformers(emitContext, declarationFilePath, declarationMapPath) { sourceFile = transformer.TransformSourceFile(sourceFile) diags = append(diags, transformer.GetDiagnostics()...) } @@ -378,6 +377,7 @@ type SourceFileMayBeEmittedHost interface { IsSourceFileFromExternalLibrary(file *ast.SourceFile) bool GetCurrentDirectory() string UseCaseSensitiveFileNames() bool + SourceFiles() []*ast.SourceFile } func sourceFileMayBeEmitted(sourceFile *ast.SourceFile, host SourceFileMayBeEmittedHost, forceDtsEmit bool) bool { @@ -435,7 +435,7 @@ func sourceFileMayBeEmitted(sourceFile *ast.SourceFile, host SourceFileMayBeEmit return true } -func getSourceFilesToEmit(host printer.EmitHost, targetSourceFile *ast.SourceFile, forceDtsEmit bool) []*ast.SourceFile { +func getSourceFilesToEmit(host SourceFileMayBeEmittedHost, targetSourceFile *ast.SourceFile, forceDtsEmit bool) []*ast.SourceFile { // !!! outFile not yet implemented, may be deprecated var sourceFiles []*ast.SourceFile if targetSourceFile != nil { @@ -452,13 +452,13 @@ func isSourceFileNotJson(file *ast.SourceFile) bool { return !ast.IsJsonSourceFile(file) } -func getDeclarationDiagnostics(host EmitHost, resolver printer.EmitResolver, file *ast.SourceFile) []*ast.Diagnostic { +func getDeclarationDiagnostics(host EmitHost, file *ast.SourceFile) []*ast.Diagnostic { fullFiles := core.Filter(getSourceFilesToEmit(host, file, false), isSourceFileNotJson) if !core.Some(fullFiles, func(f *ast.SourceFile) bool { return f == file }) { return []*ast.Diagnostic{} } options := host.Options() - transform := declarations.NewDeclarationTransformer(host, resolver, nil, options, "", "") + transform := declarations.NewDeclarationTransformer(host, nil, options, "", "") transform.TransformSourceFile(file) return transform.GetDiagnostics() } diff --git a/internal/compiler/program.go b/internal/compiler/program.go index 7dbd620a6e..fa2ac6e908 100644 --- a/internal/compiler/program.go +++ b/internal/compiler/program.go @@ -449,7 +449,7 @@ func (p *Program) getSemanticDiagnosticsForFile(ctx context.Context, sourceFile return filtered } -func (p *Program) getDeclarationDiagnosticsForFile(_ctx context.Context, sourceFile *ast.SourceFile) []*ast.Diagnostic { +func (p *Program) getDeclarationDiagnosticsForFile(ctx context.Context, sourceFile *ast.SourceFile) []*ast.Diagnostic { if sourceFile.IsDeclarationFile { return []*ast.Diagnostic{} } @@ -458,8 +458,9 @@ func (p *Program) getDeclarationDiagnosticsForFile(_ctx context.Context, sourceF return cached } - host := &emitHost{program: p} - diagnostics := getDeclarationDiagnostics(host, host.GetEmitResolver(sourceFile), sourceFile) + host, done := newEmitHost(ctx, p, sourceFile) + defer done() + diagnostics := getDeclarationDiagnostics(host, sourceFile) diagnostics, _ = p.declarationDiagnosticCache.LoadOrStore(sourceFile, diagnostics) return diagnostics } @@ -651,9 +652,8 @@ func (p *Program) CommonSourceDirectory() string { p.Options(), func() []string { var files []string - host := &emitHost{program: p} for _, file := range p.files { - if sourceFileMayBeEmitted(file, host, false /*forceDtsEmit*/) { + if sourceFileMayBeEmitted(file, p, false /*forceDtsEmit*/) { files = append(files, file.FileName()) } } @@ -688,20 +688,17 @@ func (p *Program) Emit(options EmitOptions) *EmitResult { // !!! performance measurement p.BindSourceFiles() - host := &emitHost{program: p} - writerPool := &sync.Pool{ New: func() any { - return printer.NewTextWriter(host.Options().NewLine.GetNewLineCharacter()) + return printer.NewTextWriter(p.Options().NewLine.GetNewLineCharacter()) }, } wg := core.NewWorkGroup(p.singleThreaded()) var emitters []*emitter - sourceFiles := getSourceFilesToEmit(host, options.TargetSourceFile, options.forceDtsEmit) + sourceFiles := getSourceFilesToEmit(p, options.TargetSourceFile, options.forceDtsEmit) for _, sourceFile := range sourceFiles { emitter := &emitter{ - host: host, emittedFilesList: nil, sourceMapDataList: nil, writer: nil, @@ -709,6 +706,10 @@ func (p *Program) Emit(options EmitOptions) *EmitResult { } emitters = append(emitters, emitter) wg.Queue(func() { + host, done := newEmitHost(context.TODO(), p, sourceFile) + defer done() + emitter.host = host + // take an unused writer writer := writerPool.Get().(printer.EmitTextWriter) writer.Clear() diff --git a/internal/printer/emithost.go b/internal/printer/emithost.go index f0a6af576c..121ac712fc 100644 --- a/internal/printer/emithost.go +++ b/internal/printer/emithost.go @@ -25,7 +25,7 @@ type EmitHost interface { IsEmitBlocked(file string) bool WriteFile(fileName string, text string, writeByteOrderMark bool, relatedSourceFiles []*ast.SourceFile, data *WriteFileData) error GetEmitModuleFormatOfFile(file ast.HasFileName) core.ModuleKind - GetEmitResolver(file *ast.SourceFile) EmitResolver + GetEmitResolver() EmitResolver GetOutputAndProjectReference(path tspath.Path) *tsoptions.OutputDtsAndProjectReference IsSourceFileFromExternalLibrary(file *ast.SourceFile) bool } diff --git a/internal/transformers/declarations/transform.go b/internal/transformers/declarations/transform.go index e9d1f39b94..639e06cbed 100644 --- a/internal/transformers/declarations/transform.go +++ b/internal/transformers/declarations/transform.go @@ -35,6 +35,7 @@ type DeclarationEmitHost interface { GetOutputPathsFor(file *ast.SourceFile, forceDtsPaths bool) OutputPaths GetResolutionModeOverride(node *ast.Node) core.ResolutionMode GetEffectiveDeclarationFlags(node *ast.Node, flags ast.ModifierFlags) ast.ModifierFlags + GetEmitResolver() printer.EmitResolver } type DeclarationTransformer struct { @@ -60,7 +61,8 @@ type DeclarationTransformer struct { rawLibReferenceDirectives []*ast.FileReference } -func NewDeclarationTransformer(host DeclarationEmitHost, resolver printer.EmitResolver, context *printer.EmitContext, compilerOptions *core.CompilerOptions, declarationFilePath string, declarationMapPath string) *DeclarationTransformer { +func NewDeclarationTransformer(host DeclarationEmitHost, context *printer.EmitContext, compilerOptions *core.CompilerOptions, declarationFilePath string, declarationMapPath string) *DeclarationTransformer { + resolver := host.GetEmitResolver() state := &SymbolTrackerSharedState{isolatedDeclarations: compilerOptions.IsolatedDeclarations.IsTrue(), resolver: resolver} tracker := NewSymbolTracker(host, resolver, state) // TODO: Use new host GetOutputPathsFor method instead of passing in entrypoint paths (which will also better support bundled emit) diff --git a/internal/transformers/tstransforms/importelision_test.go b/internal/transformers/tstransforms/importelision_test.go index fe20f006cd..f7d8b9c043 100644 --- a/internal/transformers/tstransforms/importelision_test.go +++ b/internal/transformers/tstransforms/importelision_test.go @@ -241,7 +241,7 @@ func TestImportElision(t *testing.T) { }, }) - emitResolver := c.GetEmitResolver(file) + emitResolver := c.GetEmitResolver() emitResolver.MarkLinkedReferencesRecursively(file) emitContext := printer.NewEmitContext()