Skip to content

Commit

Permalink
Resolutions cache stays for lifetime..
Browse files Browse the repository at this point in the history
But when do we gc Resolution caches esp for non relative names etc?
Also TODO:: handle change in module reosolution options
Postpone stopping watching resolutions as they can still be shared from different file
  • Loading branch information
sheetalkamat committed Jun 26, 2024
1 parent 76f2d6a commit a797369
Show file tree
Hide file tree
Showing 8 changed files with 107 additions and 110 deletions.
69 changes: 55 additions & 14 deletions src/compiler/moduleNameResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import {
hasProperty,
hasTrailingDirectorySeparator,
hostGetCanonicalFileName,
identity,
inferredTypesContainingFile,
isArray,
isDeclarationFileName,
Expand Down Expand Up @@ -1074,6 +1075,7 @@ function createPerDirectoryResolutionCache<T>(
getCanonicalFileName: GetCanonicalFileName,
options: CompilerOptions | undefined,
optionsToRedirectsKey: Map<CompilerOptions, RedirectsCacheKey>,
getValidResolution: (resolution: T | undefined) => T | undefined,
): PerDirectoryResolutionCache<T> {
const directoryToModuleNameMap = createCacheWithRedirects<Path, ModeAwareCache<T>>(options, optionsToRedirectsKey);
return {
Expand All @@ -1099,7 +1101,7 @@ function createPerDirectoryResolutionCache<T>(

function getFromDirectoryCache(name: string, mode: ResolutionMode, directoryName: string, redirectedReference: ResolvedProjectReference | undefined) {
const path = toPath(directoryName, currentDirectory, getCanonicalFileName);
return directoryToModuleNameMap.getMapOfCacheRedirects(redirectedReference)?.get(path)?.get(name, mode);
return getValidResolution(directoryToModuleNameMap.getMapOfCacheRedirects(redirectedReference)?.get(path)?.get(name, mode));
}
}

Expand Down Expand Up @@ -1163,6 +1165,7 @@ function createNonRelativeNameResolutionCache<T>(
options: CompilerOptions | undefined,
getResolvedFileName: (result: T) => string | undefined,
optionsToRedirectsKey: Map<CompilerOptions, RedirectsCacheKey>,
getValidResolution: (resolution: T | undefined) => T | undefined,
): NonRelativeNameResolutionCache<T> {
const moduleNameToDirectoryMap = createCacheWithRedirects<ModeAwareCacheKey, PerNonRelativeNameCache<T>>(options, optionsToRedirectsKey);
return {
Expand All @@ -1182,12 +1185,19 @@ function createNonRelativeNameResolutionCache<T>(

function getFromNonRelativeNameCache(nonRelativeModuleName: string, mode: ResolutionMode, directoryName: string, redirectedReference?: ResolvedProjectReference): T | undefined {
Debug.assert(!isExternalModuleNameRelative(nonRelativeModuleName));
return moduleNameToDirectoryMap.getMapOfCacheRedirects(redirectedReference)?.get(createModeAwareCacheKey(nonRelativeModuleName, mode))?.get(directoryName);
return moduleNameToDirectoryMap.getMapOfCacheRedirects(redirectedReference)?.get(
createModeAwareCacheKey(nonRelativeModuleName, mode),
)?.get(directoryName);
}

function getOrCreateCacheForNonRelativeName(nonRelativeModuleName: string, mode: ResolutionMode, redirectedReference?: ResolvedProjectReference): PerNonRelativeNameCache<T> {
Debug.assert(!isExternalModuleNameRelative(nonRelativeModuleName));
return getOrCreateCache(moduleNameToDirectoryMap, redirectedReference, createModeAwareCacheKey(nonRelativeModuleName, mode), createPerModuleNameCache);
return getOrCreateCache(
moduleNameToDirectoryMap,
redirectedReference,
createModeAwareCacheKey(nonRelativeModuleName, mode),
createPerModuleNameCache,
);
}

function createPerModuleNameCache(): PerNonRelativeNameCache<T> {
Expand All @@ -1196,7 +1206,11 @@ function createNonRelativeNameResolutionCache<T>(
return { get, set };

function get(directory: string): T | undefined {
return directoryPathMap.get(toPath(directory, currentDirectory, getCanonicalFileName));
return getByPath(toPath(directory, currentDirectory, getCanonicalFileName));
}

function getByPath(directoryPath: Path): T | undefined {
return getValidResolution(directoryPathMap.get(directoryPath));
}

/**
Expand All @@ -1213,32 +1227,45 @@ function createNonRelativeNameResolutionCache<T>(
function set(directory: string, result: T): void {
const path = toPath(directory, currentDirectory, getCanonicalFileName);
// if entry is already in cache do nothing
if (directoryPathMap.has(path)) {
if (getByPath(path)) {
return;
}

const existing = directoryPathMap.get(path);
// Remove invalidated result from parent
if (existing) {
const existingCommonPrefix = getCommonPrefix(path, existing);
withCommonPrefix(path, existingCommonPrefix, parent => directoryPathMap.delete(parent));
}

directoryPathMap.set(path, result);

const resolvedFileName = getResolvedFileName(result);
// find common prefix between directory and resolved file name
// this common prefix should be the shortest path that has the same resolution
// directory: /a/b/c/d/e
// resolvedFileName: /a/b/foo.d.ts
// commonPrefix: /a/b
// for failed lookups cache the result for every directory up to root
const commonPrefix = resolvedFileName && getCommonPrefix(path, resolvedFileName);
const commonPrefix = getCommonPrefix(path, result);
withCommonPrefix(path, commonPrefix, parent => directoryPathMap.set(parent, result));
}

function withCommonPrefix(path: Path, commonPrefix: Path | undefined, action: (parent: Path) => void) {
let current = path;
while (current !== commonPrefix) {
const parent = getDirectoryPath(current);
if (parent === current || directoryPathMap.has(parent)) {
if (parent === current || getByPath(parent)) {
break;
}
directoryPathMap.set(parent, result);
action(parent);
current = parent;
}
}

function getCommonPrefix(directory: Path, resolution: string) {
const resolutionDirectory = toPath(getDirectoryPath(resolution), currentDirectory, getCanonicalFileName);
function getCommonPrefix(directory: Path, resolution: T) {
const resolvedFileName = getResolvedFileName(resolution);
if (!resolvedFileName) return undefined;
const resolutionDirectory = toPath(getDirectoryPath(resolvedFileName), currentDirectory, getCanonicalFileName);

// find first position where directory and resolution differs
let i = 0;
Expand All @@ -1257,7 +1284,7 @@ function createNonRelativeNameResolutionCache<T>(
if (sep === -1) {
return undefined;
}
return directory.substr(0, Math.max(sep, rootLength));
return directory.substr(0, Math.max(sep, rootLength)) as Path;
}
}
}
Expand All @@ -1274,20 +1301,24 @@ function createModuleOrTypeReferenceResolutionCache<T>(
packageJsonInfoCache: PackageJsonInfoCache | undefined,
getResolvedFileName: (result: T) => string | undefined,
optionsToRedirectsKey: Map<CompilerOptions, RedirectsCacheKey> | undefined,
getValidResolution: ((resolution: T | undefined) => T | undefined) | undefined,
): ModuleOrTypeReferenceResolutionCache<T> {
optionsToRedirectsKey ??= new Map();
getValidResolution ??= identity;
const perDirectoryResolutionCache = createPerDirectoryResolutionCache<T>(
currentDirectory,
getCanonicalFileName,
options,
optionsToRedirectsKey,
getValidResolution,
);
const nonRelativeNameResolutionCache = createNonRelativeNameResolutionCache(
currentDirectory,
getCanonicalFileName,
options,
getResolvedFileName,
optionsToRedirectsKey,
getValidResolution,
);
packageJsonInfoCache ??= createPackageJsonInfoCache(currentDirectory, getCanonicalFileName);

Expand Down Expand Up @@ -1330,14 +1361,18 @@ export function createModuleResolutionCache(
getCanonicalFileName: (s: string) => string,
options?: CompilerOptions,
packageJsonInfoCache?: PackageJsonInfoCache,
optionsToRedirectsKey?: Map<CompilerOptions, RedirectsCacheKey>, // eslint-disable-line @typescript-eslint/unified-signatures
optionsToRedirectsKey?: Map<CompilerOptions, RedirectsCacheKey>,
getValidResolution?: ( // eslint-disable-line @typescript-eslint/unified-signatures
resolution: ResolvedModuleWithFailedLookupLocations | undefined,
) => ResolvedModuleWithFailedLookupLocations | undefined,
): ModuleResolutionCache;
export function createModuleResolutionCache(
currentDirectory: string,
getCanonicalFileName: (s: string) => string,
options?: CompilerOptions,
packageJsonInfoCache?: PackageJsonInfoCache,
optionsToRedirectsKey?: Map<CompilerOptions, RedirectsCacheKey>,
getValidResolution?: (resolution: ResolvedModuleWithFailedLookupLocations | undefined) => ResolvedModuleWithFailedLookupLocations | undefined,
): ModuleResolutionCache {
const result = createModuleOrTypeReferenceResolutionCache(
currentDirectory,
Expand All @@ -1346,6 +1381,7 @@ export function createModuleResolutionCache(
packageJsonInfoCache,
getOriginalOrResolvedModuleFileName,
optionsToRedirectsKey,
getValidResolution,
) as ModuleResolutionCache;
result.getOrCreateCacheForModuleName = (nonRelativeName, mode, redirectedReference) => result.getOrCreateCacheForNonRelativeName(nonRelativeName, mode, redirectedReference);
return result;
Expand All @@ -1363,14 +1399,18 @@ export function createTypeReferenceDirectiveResolutionCache(
getCanonicalFileName: (s: string) => string,
options?: CompilerOptions,
packageJsonInfoCache?: PackageJsonInfoCache,
optionsToRedirectsKey?: Map<CompilerOptions, RedirectsCacheKey>, // eslint-disable-line @typescript-eslint/unified-signatures
optionsToRedirectsKey?: Map<CompilerOptions, RedirectsCacheKey>,
getValidResolution?: ( // eslint-disable-line @typescript-eslint/unified-signatures
resolution: ResolvedTypeReferenceDirectiveWithFailedLookupLocations | undefined,
) => ResolvedTypeReferenceDirectiveWithFailedLookupLocations | undefined,
): TypeReferenceDirectiveResolutionCache;
export function createTypeReferenceDirectiveResolutionCache(
currentDirectory: string,
getCanonicalFileName: (s: string) => string,
options?: CompilerOptions,
packageJsonInfoCache?: PackageJsonInfoCache,
optionsToRedirectsKey?: Map<CompilerOptions, RedirectsCacheKey>,
getValidResolution?: (resolution: ResolvedTypeReferenceDirectiveWithFailedLookupLocations | undefined) => ResolvedTypeReferenceDirectiveWithFailedLookupLocations | undefined,
): TypeReferenceDirectiveResolutionCache {
return createModuleOrTypeReferenceResolutionCache(
currentDirectory,
Expand All @@ -1379,6 +1419,7 @@ export function createTypeReferenceDirectiveResolutionCache(
packageJsonInfoCache,
getOriginalOrResolvedTypeReferenceFileName,
optionsToRedirectsKey,
getValidResolution,
);
}

Expand Down
3 changes: 1 addition & 2 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,6 @@ import {
removeSuffix,
resolutionExtensionIsTSOrJson,
ResolutionMode,
ResolutionWithFailedLookupLocations,
resolveConfigFileProjectName,
ResolvedConfigFileName,
ResolvedModuleFull,
Expand Down Expand Up @@ -4179,7 +4178,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
resolveModuleNamesReusingOldState(moduleNames, file);
Debug.assert(resolutions.length === moduleNames.length);
const optionsForFile = redirectedReference?.commandLine.options || options;
const resolutionsInFile = createModeAwareCache<ResolutionWithFailedLookupLocations>();
const resolutionsInFile = createModeAwareCache<ResolvedModuleWithFailedLookupLocations>();
(resolvedModules ??= new Map()).set(file.path, resolutionsInFile);
for (let index = 0; index < moduleNames.length; index++) {
const resolution = resolutions[index].resolvedModule;
Expand Down
Loading

0 comments on commit a797369

Please sign in to comment.