Skip to content

Commit 539b9a6

Browse files
authored
Merge pull request #28028 from ajafff/optimize-resolve-reusing-old-state
Fix performance regression when reusing old state
2 parents 05716a7 + 63a8cb6 commit 539b9a6

File tree

1 file changed

+20
-27
lines changed

1 file changed

+20
-27
lines changed

src/compiler/program.ts

Lines changed: 20 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -594,7 +594,7 @@ namespace ts {
594594
let diagnosticsProducingTypeChecker: TypeChecker;
595595
let noDiagnosticsTypeChecker: TypeChecker;
596596
let classifiableNames: UnderscoreEscapedMap<true>;
597-
let modifiedFilePaths: Path[] | undefined;
597+
const ambientModuleNameToUnmodifiedFileName = createMap<string>();
598598

599599
const cachedSemanticDiagnosticsForFile: DiagnosticCache<Diagnostic> = {};
600600
const cachedDeclarationDiagnosticsForFile: DiagnosticCache<DiagnosticWithLocation> = {};
@@ -880,21 +880,14 @@ namespace ts {
880880
return classifiableNames;
881881
}
882882

883-
interface OldProgramState {
884-
program: Program | undefined;
885-
oldSourceFile: SourceFile | undefined;
886-
/** The collection of paths modified *since* the old program. */
887-
modifiedFilePaths: Path[] | undefined;
888-
}
889-
890-
function resolveModuleNamesReusingOldState(moduleNames: string[], containingFile: string, file: SourceFile, oldProgramState: OldProgramState) {
883+
function resolveModuleNamesReusingOldState(moduleNames: string[], containingFile: string, file: SourceFile) {
891884
if (structuralIsReused === StructureIsReused.Not && !file.ambientModuleNames.length) {
892885
// If the old program state does not permit reusing resolutions and `file` does not contain locally defined ambient modules,
893886
// the best we can do is fallback to the default logic.
894887
return resolveModuleNamesWorker(moduleNames, containingFile, /*reusedNames*/ undefined, getResolvedProjectReferenceToRedirect(file.originalFileName));
895888
}
896889

897-
const oldSourceFile = oldProgramState.program && oldProgramState.program.getSourceFile(containingFile);
890+
const oldSourceFile = oldProgram && oldProgram.getSourceFile(containingFile);
898891
if (oldSourceFile !== file && file.resolvedModules) {
899892
// `file` was created for the new program.
900893
//
@@ -958,7 +951,7 @@ namespace ts {
958951
}
959952
}
960953
else {
961-
resolvesToAmbientModuleInNonModifiedFile = moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName, oldProgramState);
954+
resolvesToAmbientModuleInNonModifiedFile = moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName);
962955
}
963956

964957
if (resolvesToAmbientModuleInNonModifiedFile) {
@@ -1001,12 +994,9 @@ namespace ts {
1001994

1002995
// If we change our policy of rechecking failed lookups on each program create,
1003996
// we should adjust the value returned here.
1004-
function moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName: string, oldProgramState: OldProgramState): boolean {
1005-
if (!oldProgramState.program) {
1006-
return false;
1007-
}
1008-
const resolutionToFile = getResolvedModule(oldProgramState.oldSourceFile!, moduleName); // TODO: GH#18217
1009-
const resolvedFile = resolutionToFile && oldProgramState.program.getSourceFile(resolutionToFile.resolvedFileName);
997+
function moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName: string): boolean {
998+
const resolutionToFile = getResolvedModule(oldSourceFile!, moduleName);
999+
const resolvedFile = resolutionToFile && oldProgram!.getSourceFile(resolutionToFile.resolvedFileName);
10101000
if (resolutionToFile && resolvedFile && !resolvedFile.externalModuleIndicator) {
10111001
// In the old program, we resolved to an ambient module that was in the same
10121002
// place as we expected to find an actual module file.
@@ -1016,16 +1006,14 @@ namespace ts {
10161006
}
10171007

10181008
// at least one of declarations should come from non-modified source file
1019-
const firstUnmodifiedFile = oldProgramState.program.getSourceFiles().find(
1020-
f => !contains(oldProgramState.modifiedFilePaths, f.path) && contains(f.ambientModuleNames, moduleName)
1021-
);
1009+
const unmodifiedFile = ambientModuleNameToUnmodifiedFileName.get(moduleName);
10221010

1023-
if (!firstUnmodifiedFile) {
1011+
if (!unmodifiedFile) {
10241012
return false;
10251013
}
10261014

10271015
if (isTraceEnabled(options, host)) {
1028-
trace(host, Diagnostics.Module_0_was_resolved_as_ambient_module_declared_in_1_since_this_file_was_not_modified, moduleName, firstUnmodifiedFile.fileName);
1016+
trace(host, Diagnostics.Module_0_was_resolved_as_ambient_module_declared_in_1_since_this_file_was_not_modified, moduleName, unmodifiedFile);
10291017
}
10301018
return true;
10311019
}
@@ -1213,14 +1201,20 @@ namespace ts {
12131201
return oldProgram.structureIsReused;
12141202
}
12151203

1216-
modifiedFilePaths = modifiedSourceFiles.map(f => f.newFile.path);
1204+
const modifiedFiles = modifiedSourceFiles.map(f => f.oldFile);
1205+
for (const oldFile of oldSourceFiles) {
1206+
if (!contains(modifiedFiles, oldFile)) {
1207+
for (const moduleName of oldFile.ambientModuleNames) {
1208+
ambientModuleNameToUnmodifiedFileName.set(moduleName, oldFile.fileName);
1209+
}
1210+
}
1211+
}
12171212
// try to verify results of module resolution
12181213
for (const { oldFile: oldSourceFile, newFile: newSourceFile } of modifiedSourceFiles) {
12191214
const newSourceFilePath = getNormalizedAbsolutePath(newSourceFile.originalFileName, currentDirectory);
12201215
if (resolveModuleNamesWorker) {
12211216
const moduleNames = getModuleNames(newSourceFile);
1222-
const oldProgramState: OldProgramState = { program: oldProgram, oldSourceFile, modifiedFilePaths };
1223-
const resolutions = resolveModuleNamesReusingOldState(moduleNames, newSourceFilePath, newSourceFile, oldProgramState);
1217+
const resolutions = resolveModuleNamesReusingOldState(moduleNames, newSourceFilePath, newSourceFile);
12241218
// ensure that module resolution results are still correct
12251219
const resolutionsChanged = hasChangesInResolutions(moduleNames, resolutions, oldSourceFile.resolvedModules, moduleResolutionIsEqualTo);
12261220
if (resolutionsChanged) {
@@ -2432,8 +2426,7 @@ namespace ts {
24322426
if (file.imports.length || file.moduleAugmentations.length) {
24332427
// Because global augmentation doesn't have string literal name, we can check for global augmentation as such.
24342428
const moduleNames = getModuleNames(file);
2435-
const oldProgramState: OldProgramState = { program: oldProgram, oldSourceFile: oldProgram && oldProgram.getSourceFile(file.fileName), modifiedFilePaths };
2436-
const resolutions = resolveModuleNamesReusingOldState(moduleNames, getNormalizedAbsolutePath(file.originalFileName, currentDirectory), file, oldProgramState);
2429+
const resolutions = resolveModuleNamesReusingOldState(moduleNames, getNormalizedAbsolutePath(file.originalFileName, currentDirectory), file);
24372430
Debug.assert(resolutions.length === moduleNames.length);
24382431
for (let i = 0; i < moduleNames.length; i++) {
24392432
const resolution = resolutions[i];

0 commit comments

Comments
 (0)