Skip to content

Commit bd97e12

Browse files
authored
Multifaceted approach to performantly enabling fileExists outside of the synchronize step in the emit host (#25107)
* Multifaceted approach to performantly enabling fileExists outside of the synchronize step in the emit host * make cache undefinable and handle correctly * Remove unneeded cast * Readd assert * More useful failure messager
1 parent c441451 commit bd97e12

File tree

3 files changed

+29
-8
lines changed

3 files changed

+29
-8
lines changed

src/compiler/program.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1239,7 +1239,14 @@ namespace ts {
12391239
(fileName, data, writeByteOrderMark, onError, sourceFiles) => host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles)),
12401240
isEmitBlocked,
12411241
readFile: f => host.readFile(f),
1242-
fileExists: f => host.fileExists(f),
1242+
fileExists: f => {
1243+
// Use local caches
1244+
const path = toPath(f);
1245+
if (getSourceFileByPath(path)) return true;
1246+
if (contains(missingFilePaths, path)) return false;
1247+
// Before falling back to the host
1248+
return host.fileExists(f);
1249+
},
12431250
...(host.directoryExists ? { directoryExists: f => host.directoryExists!(f) } : {}),
12441251
};
12451252
}

src/services/services.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1171,13 +1171,13 @@ namespace ts {
11711171
}
11721172

11731173
// Get a fresh cache of the host information
1174-
let hostCache = new HostCache(host, getCanonicalFileName);
1174+
let hostCache: HostCache | undefined = new HostCache(host, getCanonicalFileName);
11751175
const rootFileNames = hostCache.getRootFileNames();
11761176

11771177
const hasInvalidatedResolution: HasInvalidatedResolution = host.hasInvalidatedResolution || returnFalse;
11781178

11791179
// If the program is already up-to-date, we can reuse it
1180-
if (isProgramUptoDate(program, rootFileNames, hostCache.compilationSettings(), path => hostCache.getVersion(path), fileExists, hasInvalidatedResolution, !!host.hasChangedAutomaticTypeDirectiveNames)) {
1180+
if (isProgramUptoDate(program, rootFileNames, hostCache.compilationSettings(), path => hostCache!.getVersion(path), fileExists, hasInvalidatedResolution, !!host.hasChangedAutomaticTypeDirectiveNames)) {
11811181
return;
11821182
}
11831183

@@ -1204,7 +1204,7 @@ namespace ts {
12041204
readFile(fileName) {
12051205
// stub missing host functionality
12061206
const path = toPath(fileName, currentDirectory, getCanonicalFileName);
1207-
const entry = hostCache.getEntryByPath(path);
1207+
const entry = hostCache && hostCache.getEntryByPath(path);
12081208
if (entry) {
12091209
return isString(entry) ? undefined : getSnapshotText(entry.scriptSnapshot);
12101210
}
@@ -1246,7 +1246,7 @@ namespace ts {
12461246

12471247
// hostCache is captured in the closure for 'getOrCreateSourceFile' but it should not be used past this point.
12481248
// It needs to be cleared to allow all collected snapshots to be released
1249-
hostCache = undefined!;
1249+
hostCache = undefined;
12501250

12511251
// We reset this cache on structure invalidation so we don't hold on to outdated files for long; however we can't use the `compilerHost` above,
12521252
// Because it only functions until `hostCache` is cleared, while we'll potentially need the functionality to lazily read sourcemap files during
@@ -1260,7 +1260,7 @@ namespace ts {
12601260

12611261
function fileExists(fileName: string): boolean {
12621262
const path = toPath(fileName, currentDirectory, getCanonicalFileName);
1263-
const entry = hostCache.getEntryByPath(path);
1263+
const entry = hostCache && hostCache.getEntryByPath(path);
12641264
return entry ?
12651265
!isString(entry) :
12661266
(!!host.fileExists && host.fileExists(fileName));
@@ -1278,11 +1278,11 @@ namespace ts {
12781278
}
12791279

12801280
function getOrCreateSourceFileByPath(fileName: string, path: Path, _languageVersion: ScriptTarget, _onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile | undefined {
1281-
Debug.assert(hostCache !== undefined);
1281+
Debug.assert(hostCache !== undefined, "getOrCreateSourceFileByPath called after typical CompilerHost lifetime, check the callstack something with a reference to an old host.");
12821282
// The program is asking for this file, check first if the host can locate it.
12831283
// If the host can not locate the file, then it does not exist. return undefined
12841284
// to the program to allow reporting of errors for missing files.
1285-
const hostFileInformation = hostCache.getOrCreateEntryByPath(fileName, path);
1285+
const hostFileInformation = hostCache && hostCache.getOrCreateEntryByPath(fileName, path);
12861286
if (!hostFileInformation) {
12871287
return undefined;
12881288
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// @declaration: true
4+
// @Filename: node_modules/foo/index.d.ts
5+
////export function f(): I;
6+
////export interface I {
7+
//// x: number;
8+
////}
9+
// @Filename: a.ts
10+
////import { f } from "foo";
11+
////export const x = f();
12+
13+
goTo.file(1);
14+
verify.getSemanticDiagnostics([]);

0 commit comments

Comments
 (0)