Skip to content

Commit 11866b4

Browse files
committed
Handle effective type roots and type ref resolution sharing cache
// TODO: update incremental tests to actually verify the cache
1 parent 3e4da7f commit 11866b4

31 files changed

+754
-1702
lines changed

src/compiler/moduleNameResolver.ts

Lines changed: 257 additions & 59 deletions
Large diffs are not rendered by default.

src/compiler/program.ts

Lines changed: 95 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import {
5050
createSymlinkCache,
5151
createTypeChecker,
5252
createTypeReferenceDirectiveResolutionCache,
53+
createTypeRootsCacheKey,
5354
CustomTransformers,
5455
Debug,
5556
DeclarationWithTypeParameterChildren,
@@ -117,6 +118,7 @@ import {
117118
getDeclarationDiagnostics as ts_getDeclarationDiagnostics,
118119
getDefaultLibFileName,
119120
getDirectoryPath,
121+
getEffectiveTypeRoots as ts_getEffectiveTypeRoots,
120122
getEmitDeclarations,
121123
getEmitModuleKind,
122124
getEmitModuleResolutionKind,
@@ -320,6 +322,7 @@ import {
320322
TypeChecker,
321323
typeDirectiveIsEqualTo,
322324
TypeReferenceDirectiveResolutionCache,
325+
TypeRootsCacheKeyOrSpecifiedTypeRoots,
323326
unprefixedNodeCoreModules,
324327
VariableDeclaration,
325328
VariableStatement,
@@ -1812,6 +1815,9 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
18121815
let projectReferenceRedirects: Map<Path, ResolvedProjectReference | false> | undefined;
18131816
let mapFromFileToProjectReferenceRedirects: Map<Path, Path> | undefined;
18141817
let mapFromToProjectReferenceRedirectSource: Map<Path, SourceOfProjectReferenceRedirect> | undefined;
1818+
let typeRootsCacheKeys: Map<Path | undefined, TypeRootsCacheKeyOrSpecifiedTypeRoots> | undefined;
1819+
let processingTypeRootsCacheKeys: Map<Path | undefined, TypeRootsCacheKeyOrSpecifiedTypeRoots> | undefined;
1820+
let effectiveRoots: Map<Path | undefined, readonly string[] | false> | undefined;
18151821

18161822
const useSourceOfProjectReferenceRedirect = !!host.useSourceOfProjectReferenceRedirect?.() &&
18171823
!options.disableSourceOfProjectReferenceRedirect;
@@ -1825,6 +1831,9 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
18251831
forEachResolvedProjectReference,
18261832
});
18271833
const readFile = host.readFile.bind(host) as typeof host.readFile;
1834+
const hostGetEffectiveTypeRoots = host.getEffectiveTypeRoots;
1835+
const hostGetTypeRootsCacheKey = host.getTypeRootsCacheKey;
1836+
host.getEffectiveTypeRoots = getEffectiveTypeRoots;
18281837

18291838
tracing?.push(tracing.Phase.Program, "shouldProgramCreateNewSourceFiles", { hasOldProgram: !!oldProgram });
18301839
const shouldCreateNewSourceFile = shouldProgramCreateNewSourceFiles(oldProgram, options);
@@ -1883,6 +1892,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
18831892
const resolutions = resolveTypeReferenceDirectiveNamesReusingOldState(
18841893
automaticTypeDirectiveNames,
18851894
getAutomaticTypeDirectiveContainingFile(options, currentDirectory),
1895+
getTypeRootsCacheKey,
18861896
);
18871897
for (let i = 0; i < automaticTypeDirectiveNames.length; i++) {
18881898
// under node16/nodenext module resolution, load `types`/ata include names as cjs resolution results by passing an `undefined` mode
@@ -1971,6 +1981,10 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
19711981
resolvedLibProcessing = undefined;
19721982
resolvedModulesProcessing = undefined;
19731983
resolvedTypeReferenceDirectiveNamesProcessing = undefined;
1984+
effectiveRoots = undefined;
1985+
processingTypeRootsCacheKeys = undefined;
1986+
host.getEffectiveTypeRoots = hostGetEffectiveTypeRoots;
1987+
host.getTypeRootsCacheKey = hostGetTypeRootsCacheKey;
19741988

19751989
const program: Program = {
19761990
getRootFileNames: () => rootNames,
@@ -2054,6 +2068,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
20542068
structureIsReused,
20552069
writeFile,
20562070
getGlobalTypingsCacheLocation: maybeBind(host, host.getGlobalTypingsCacheLocation),
2071+
getTypeRootsCacheKeys: () => typeRootsCacheKeys,
20572072
};
20582073

20592074
onProgramCreateComplete();
@@ -2251,6 +2266,51 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
22512266
return result;
22522267
}
22532268

2269+
function getEffectiveTypeRoots(options: CompilerOptions, redirect: ResolvedProjectReference | undefined) {
2270+
let result = effectiveRoots?.get(redirect?.sourceFile.path);
2271+
if (result === undefined) {
2272+
(effectiveRoots ??= new Map()).set(
2273+
redirect?.sourceFile.path,
2274+
result = (
2275+
hostGetEffectiveTypeRoots ?
2276+
hostGetEffectiveTypeRoots(options, redirect) :
2277+
ts_getEffectiveTypeRoots(options, host)
2278+
) ?? false,
2279+
);
2280+
}
2281+
return result || undefined;
2282+
}
2283+
2284+
function getTypeRootsCacheKey(options: CompilerOptions, redirect: ResolvedProjectReference | undefined): TypeRootsCacheKeyOrSpecifiedTypeRoots {
2285+
let result = typeRootsCacheKeys?.get(redirect?.sourceFile.path);
2286+
if (result === undefined) {
2287+
(typeRootsCacheKeys ??= new Map()).set(
2288+
redirect?.sourceFile.path,
2289+
result = processingTypeRootsCacheKeys?.get(redirect?.sourceFile.path) ??
2290+
getTypeRootsCacheKeyWorker(options, redirect) ??
2291+
false,
2292+
);
2293+
}
2294+
return result;
2295+
}
2296+
2297+
function getProcessingTypeRootsCacheKey(options: CompilerOptions, redirect: ResolvedProjectReference | undefined): TypeRootsCacheKeyOrSpecifiedTypeRoots {
2298+
let result = processingTypeRootsCacheKeys?.get(redirect?.sourceFile.path);
2299+
if (result === undefined) {
2300+
(processingTypeRootsCacheKeys ??= new Map()).set(
2301+
redirect?.sourceFile.path,
2302+
result = getTypeRootsCacheKeyWorker(options, redirect) ?? false,
2303+
);
2304+
}
2305+
return result;
2306+
}
2307+
2308+
function getTypeRootsCacheKeyWorker(options: CompilerOptions, redirect: ResolvedProjectReference | undefined): TypeRootsCacheKeyOrSpecifiedTypeRoots {
2309+
return hostGetTypeRootsCacheKey ?
2310+
hostGetTypeRootsCacheKey(options, redirect) :
2311+
createTypeRootsCacheKey(options, redirect, host);
2312+
}
2313+
22542314
function getRedirectReferenceForResolution(file: SourceFile) {
22552315
const redirect = getResolvedProjectReferenceToRedirect(file.originalFileName);
22562316
if (redirect || !isDeclarationFileName(file.originalFileName)) return redirect;
@@ -2346,9 +2406,21 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
23462406
});
23472407
}
23482408

2349-
function resolveTypeReferenceDirectiveNamesReusingOldState(typeDirectiveNames: readonly FileReference[], containingFile: SourceFile): readonly ResolvedTypeReferenceDirectiveWithFailedLookupLocations[];
2350-
function resolveTypeReferenceDirectiveNamesReusingOldState(typeDirectiveNames: readonly string[], containingFile: string): readonly ResolvedTypeReferenceDirectiveWithFailedLookupLocations[];
2351-
function resolveTypeReferenceDirectiveNamesReusingOldState<T extends string | FileReference>(typeDirectiveNames: readonly T[], containingFile: string | SourceFile): readonly ResolvedTypeReferenceDirectiveWithFailedLookupLocations[] {
2409+
function resolveTypeReferenceDirectiveNamesReusingOldState(
2410+
typeDirectiveNames: readonly FileReference[],
2411+
containingFile: SourceFile,
2412+
getTypeRootsCacheKey: (options: CompilerOptions, redirects: ResolvedProjectReference | undefined) => TypeRootsCacheKeyOrSpecifiedTypeRoots,
2413+
): readonly ResolvedTypeReferenceDirectiveWithFailedLookupLocations[];
2414+
function resolveTypeReferenceDirectiveNamesReusingOldState(
2415+
typeDirectiveNames: readonly string[],
2416+
containingFile: string,
2417+
getTypeRootsCacheKey: (options: CompilerOptions, redirects: ResolvedProjectReference | undefined) => TypeRootsCacheKeyOrSpecifiedTypeRoots,
2418+
): readonly ResolvedTypeReferenceDirectiveWithFailedLookupLocations[];
2419+
function resolveTypeReferenceDirectiveNamesReusingOldState<T extends string | FileReference>(
2420+
typeDirectiveNames: readonly T[],
2421+
containingFile: string | SourceFile,
2422+
getTypeRootsCacheKey: (options: CompilerOptions, redirects: ResolvedProjectReference | undefined) => TypeRootsCacheKeyOrSpecifiedTypeRoots,
2423+
): readonly ResolvedTypeReferenceDirectiveWithFailedLookupLocations[] {
23522424
const containingSourceFile = !isString(containingFile) ? containingFile : undefined;
23532425
return resolveNamesReusingOldState({
23542426
entries: typeDirectiveNames,
@@ -2367,6 +2439,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
23672439
containingSourceFile ?
23682440
containingSourceFile === oldProgram?.getSourceFile(containingSourceFile.fileName) && !hasInvalidatedResolutions(containingSourceFile.path) :
23692441
!hasInvalidatedResolutions(toPath(containingFile as string)),
2442+
getTypeRootsCacheKey,
23702443
});
23712444
}
23722445

@@ -2393,6 +2466,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
23932466
getResolved: (oldResolution: Resolution) => ResolutionWithResolvedFileName | undefined;
23942467
canReuseResolutionsInFile: () => boolean;
23952468
resolveToOwnAmbientModule?: true;
2469+
getTypeRootsCacheKey?: (options: CompilerOptions, redirects: ResolvedProjectReference | undefined) => TypeRootsCacheKeyOrSpecifiedTypeRoots;
23962470
}
23972471

23982472
function resolveNamesReusingOldState<Entry, SourceFileOrString, SourceFileOrUndefined extends SourceFile | undefined, Resolution>({
@@ -2407,6 +2481,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
24072481
getResolved,
24082482
canReuseResolutionsInFile,
24092483
resolveToOwnAmbientModule,
2484+
getTypeRootsCacheKey,
24102485
}: ResolveNamesReusingOldStateInput<Entry, SourceFileOrString, SourceFileOrUndefined, Resolution>): readonly Resolution[] {
24112486
if (!entries.length) {
24122487
onReusedResolutions?.(
@@ -2417,7 +2492,10 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
24172492
);
24182493
return emptyArray;
24192494
}
2495+
// Ensure typeRootsCacheKey is cached
2496+
getTypeRootsCacheKey?.(redirectedReference?.commandLine.options ?? options, redirectedReference);
24202497
if (structureIsReused === StructureIsReused.Not && (!resolveToOwnAmbientModule || !containingSourceFile!.ambientModuleNames.length)) {
2498+
host.getTypeRootsCacheKey = getTypeRootsCacheKey;
24212499
// If the old program state does not permit reusing resolutions and `file` does not contain locally defined ambient modules,
24222500
// the best we can do is fallback to the default logic.
24232501
return resolutionWorker(
@@ -2494,6 +2572,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
24942572
);
24952573
return result!;
24962574
}
2575+
host.getTypeRootsCacheKey = getTypeRootsCacheKey;
24972576
const resolutions = resolutionWorker(
24982577
unknownEntries,
24992578
containingFile,
@@ -2703,7 +2782,11 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
27032782
);
27042783
if (resolutionsChanged) structureIsReused = StructureIsReused.SafeModules;
27052784
const typesReferenceDirectives = newSourceFile.typeReferenceDirectives;
2706-
const typeReferenceResolutions = resolveTypeReferenceDirectiveNamesReusingOldState(typesReferenceDirectives, newSourceFile);
2785+
const typeReferenceResolutions = resolveTypeReferenceDirectiveNamesReusingOldState(
2786+
typesReferenceDirectives,
2787+
newSourceFile,
2788+
getProcessingTypeRootsCacheKey,
2789+
);
27072790
(resolvedTypeReferenceDirectiveNamesProcessing ??= new Map()).set(newSourceFile.path, typeReferenceResolutions);
27082791
// ensure that types resolutions are still correct
27092792
const typeReferenceResolutionsChanged = hasChangesInResolutions(
@@ -2778,6 +2861,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
27782861
resolvedTypeReferenceDirectiveNames = oldProgram.resolvedTypeReferenceDirectiveNames;
27792862
resolvedLibReferences = oldProgram.resolvedLibReferences;
27802863
packageMap = oldProgram.getCurrentPackagesMap();
2864+
typeRootsCacheKeys = oldProgram.getTypeRootsCacheKeys();
27812865

27822866
return StructureIsReused.Completely;
27832867
}
@@ -4057,7 +4141,13 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
40574141
}
40584142

40594143
const resolutions = resolvedTypeReferenceDirectiveNamesProcessing?.get(file.path) ||
4060-
resolveTypeReferenceDirectiveNamesReusingOldState(typeDirectives, file);
4144+
resolveTypeReferenceDirectiveNamesReusingOldState(typeDirectives, file, getTypeRootsCacheKey);
4145+
if (resolutions.length && resolvedTypeReferenceDirectiveNamesProcessing?.get(file.path)) {
4146+
// Ensure type reference key is cached from processing to actual
4147+
const redirect = getRedirectReferenceForResolution(file);
4148+
const value = processingTypeRootsCacheKeys?.get(redirect?.sourceFile.path);
4149+
if (value !== undefined) (typeRootsCacheKeys ??= new Map()).set(redirect?.sourceFile.path, value);
4150+
}
40614151
const resolutionsInFile = createModeAwareCache<ResolvedTypeReferenceDirectiveWithFailedLookupLocations>();
40624152
(resolvedTypeReferenceDirectiveNames ??= new Map()).set(file.path, resolutionsInFile);
40634153
for (let index = 0; index < typeDirectives.length; index++) {

src/compiler/resolutionCache.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ import {
7474
trace,
7575
TypeReferenceDirectiveResolutionCache,
7676
typeReferenceResolutionNameAndModeGetter,
77+
TypeRootsCacheKeyOrSpecifiedTypeRoots,
7778
WatchDirectoryFlags,
7879
} from "./_namespaces/ts.js";
7980

@@ -360,6 +361,7 @@ function resolveModuleNameUsingGlobalCache(
360361
mode,
361362
getDirectoryPath(containingFile),
362363
redirectedReference,
364+
undefined,
363365
primary.globalCacheResolution.globalResult,
364366
primary,
365367
);
@@ -658,16 +660,25 @@ export function createResolutionCache(
658660

659661
function compactCaches(newProgram: Program | undefined) {
660662
const availableOptions = new Set<CompilerOptions>();
663+
const availableTypeCacheKeys = new Map<CompilerOptions, Set<TypeRootsCacheKeyOrSpecifiedTypeRoots>>();
661664
if (newProgram) {
662665
availableOptions.add(newProgram.getCompilerOptions());
666+
const key = newProgram.getTypeRootsCacheKeys()?.get(/*key*/ undefined);
667+
if (key !== undefined) availableTypeCacheKeys.set(newProgram.getCompilerOptions(), new Set([key]));
663668
newProgram.forEachResolvedProjectReference(ref => {
664669
availableOptions.add(ref.commandLine.options);
670+
const key = newProgram.getTypeRootsCacheKeys()?.get(ref.sourceFile.path);
671+
if (key !== undefined) {
672+
const existing = availableTypeCacheKeys.get(ref.commandLine.options);
673+
if (existing) existing.add(key);
674+
else availableTypeCacheKeys.set(ref.commandLine.options, new Set([key]));
675+
}
665676
});
666677
}
667678
moduleResolutionCache.compact(availableOptions, /*skipOptionsToRedirectsKeyCleanup*/ true);
668-
typeReferenceDirectiveResolutionCache.compact(availableOptions);
679+
typeReferenceDirectiveResolutionCache.compact(availableOptions, /*skipOptionsToRedirectsKeyCleanup*/ false, availableTypeCacheKeys);
669680
libraryResolutionCache.compact();
670-
sharedCache.compactCaches(availableOptions, cache);
681+
sharedCache.compactCaches(availableOptions, availableTypeCacheKeys, cache);
671682
}
672683

673684
function gcModuleOrTypeRefCache(

0 commit comments

Comments
 (0)