Skip to content

Commit ec320de

Browse files
Merge pull request #28282 from ajafff/backport-perf-regression
Fix performance regression when reusing old state (#28028)
2 parents 84d1de7 + da7f68d commit ec320de

File tree

1 file changed

+20
-27
lines changed

1 file changed

+20
-27
lines changed

src/compiler/program.ts

+20-27
Original file line numberDiff line numberDiff line change
@@ -574,7 +574,7 @@ namespace ts {
574574
let diagnosticsProducingTypeChecker: TypeChecker;
575575
let noDiagnosticsTypeChecker: TypeChecker;
576576
let classifiableNames: UnderscoreEscapedMap<true>;
577-
let modifiedFilePaths: Path[] | undefined;
577+
const ambientModuleNameToUnmodifiedFileName = createMap<string>();
578578

579579
const cachedSemanticDiagnosticsForFile: DiagnosticCache<Diagnostic> = {};
580580
const cachedDeclarationDiagnosticsForFile: DiagnosticCache<DiagnosticWithLocation> = {};
@@ -850,21 +850,14 @@ namespace ts {
850850
return classifiableNames;
851851
}
852852

853-
interface OldProgramState {
854-
program: Program | undefined;
855-
oldSourceFile: SourceFile | undefined;
856-
/** The collection of paths modified *since* the old program. */
857-
modifiedFilePaths: Path[] | undefined;
858-
}
859-
860-
function resolveModuleNamesReusingOldState(moduleNames: string[], containingFile: string, file: SourceFile, oldProgramState: OldProgramState) {
853+
function resolveModuleNamesReusingOldState(moduleNames: string[], containingFile: string, file: SourceFile) {
861854
if (structuralIsReused === StructureIsReused.Not && !file.ambientModuleNames.length) {
862855
// If the old program state does not permit reusing resolutions and `file` does not contain locally defined ambient modules,
863856
// the best we can do is fallback to the default logic.
864857
return resolveModuleNamesWorker(moduleNames, containingFile);
865858
}
866859

867-
const oldSourceFile = oldProgramState.program && oldProgramState.program.getSourceFile(containingFile);
860+
const oldSourceFile = oldProgram && oldProgram.getSourceFile(containingFile);
868861
if (oldSourceFile !== file && file.resolvedModules) {
869862
// `file` was created for the new program.
870863
//
@@ -928,7 +921,7 @@ namespace ts {
928921
}
929922
}
930923
else {
931-
resolvesToAmbientModuleInNonModifiedFile = moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName, oldProgramState);
924+
resolvesToAmbientModuleInNonModifiedFile = moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName);
932925
}
933926

934927
if (resolvesToAmbientModuleInNonModifiedFile) {
@@ -971,12 +964,9 @@ namespace ts {
971964

972965
// If we change our policy of rechecking failed lookups on each program create,
973966
// we should adjust the value returned here.
974-
function moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName: string, oldProgramState: OldProgramState): boolean {
975-
if (!oldProgramState.program) {
976-
return false;
977-
}
978-
const resolutionToFile = getResolvedModule(oldProgramState.oldSourceFile!, moduleName); // TODO: GH#18217
979-
const resolvedFile = resolutionToFile && oldProgramState.program.getSourceFile(resolutionToFile.resolvedFileName);
967+
function moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName: string): boolean {
968+
const resolutionToFile = getResolvedModule(oldSourceFile!, moduleName);
969+
const resolvedFile = resolutionToFile && oldProgram!.getSourceFile(resolutionToFile.resolvedFileName);
980970
if (resolutionToFile && resolvedFile && !resolvedFile.externalModuleIndicator) {
981971
// In the old program, we resolved to an ambient module that was in the same
982972
// place as we expected to find an actual module file.
@@ -986,16 +976,14 @@ namespace ts {
986976
}
987977

988978
// at least one of declarations should come from non-modified source file
989-
const firstUnmodifiedFile = oldProgramState.program.getSourceFiles().find(
990-
f => !contains(oldProgramState.modifiedFilePaths, f.path) && contains(f.ambientModuleNames, moduleName)
991-
);
979+
const unmodifiedFile = ambientModuleNameToUnmodifiedFileName.get(moduleName);
992980

993-
if (!firstUnmodifiedFile) {
981+
if (!unmodifiedFile) {
994982
return false;
995983
}
996984

997985
if (isTraceEnabled(options, host)) {
998-
trace(host, Diagnostics.Module_0_was_resolved_as_ambient_module_declared_in_1_since_this_file_was_not_modified, moduleName, firstUnmodifiedFile.fileName);
986+
trace(host, Diagnostics.Module_0_was_resolved_as_ambient_module_declared_in_1_since_this_file_was_not_modified, moduleName, unmodifiedFile);
999987
}
1000988
return true;
1001989
}
@@ -1188,14 +1176,20 @@ namespace ts {
11881176
return oldProgram.structureIsReused;
11891177
}
11901178

1191-
modifiedFilePaths = modifiedSourceFiles.map(f => f.newFile.path);
1179+
const modifiedFiles = modifiedSourceFiles.map(f => f.oldFile);
1180+
for (const oldFile of oldSourceFiles) {
1181+
if (!contains(modifiedFiles, oldFile)) {
1182+
for (const moduleName of oldFile.ambientModuleNames) {
1183+
ambientModuleNameToUnmodifiedFileName.set(moduleName, oldFile.fileName);
1184+
}
1185+
}
1186+
}
11921187
// try to verify results of module resolution
11931188
for (const { oldFile: oldSourceFile, newFile: newSourceFile } of modifiedSourceFiles) {
11941189
const newSourceFilePath = getNormalizedAbsolutePath(newSourceFile.originalFileName, currentDirectory);
11951190
if (resolveModuleNamesWorker) {
11961191
const moduleNames = getModuleNames(newSourceFile);
1197-
const oldProgramState: OldProgramState = { program: oldProgram, oldSourceFile, modifiedFilePaths };
1198-
const resolutions = resolveModuleNamesReusingOldState(moduleNames, newSourceFilePath, newSourceFile, oldProgramState);
1192+
const resolutions = resolveModuleNamesReusingOldState(moduleNames, newSourceFilePath, newSourceFile);
11991193
// ensure that module resolution results are still correct
12001194
const resolutionsChanged = hasChangesInResolutions(moduleNames, resolutions, oldSourceFile.resolvedModules, moduleResolutionIsEqualTo);
12011195
if (resolutionsChanged) {
@@ -2308,8 +2302,7 @@ namespace ts {
23082302
if (file.imports.length || file.moduleAugmentations.length) {
23092303
// Because global augmentation doesn't have string literal name, we can check for global augmentation as such.
23102304
const moduleNames = getModuleNames(file);
2311-
const oldProgramState: OldProgramState = { program: oldProgram, oldSourceFile: oldProgram && oldProgram.getSourceFile(file.fileName), modifiedFilePaths };
2312-
const resolutions = resolveModuleNamesReusingOldState(moduleNames, getNormalizedAbsolutePath(file.originalFileName, currentDirectory), file, oldProgramState);
2305+
const resolutions = resolveModuleNamesReusingOldState(moduleNames, getNormalizedAbsolutePath(file.originalFileName, currentDirectory), file);
23132306
Debug.assert(resolutions.length === moduleNames.length);
23142307
for (let i = 0; i < moduleNames.length; i++) {
23152308
const resolution = resolutions[i];

0 commit comments

Comments
 (0)