Skip to content

Commit 303824e

Browse files
committed
Instead of maintaining delta of changes, maintain old state for those changes
1 parent 26e7cb6 commit 303824e

File tree

7 files changed

+1267
-172
lines changed

7 files changed

+1267
-172
lines changed

src/compiler/builder.ts

Lines changed: 37 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -29,27 +29,6 @@ namespace ts {
2929
* The map has key by source file's path that has been changed
3030
*/
3131
changedFilesSet?: Set<Path>;
32-
/**
33-
* Set of affected files being iterated
34-
*/
35-
affectedFiles?: readonly SourceFile[] | undefined;
36-
/**
37-
* Current changed file for iterating over affected files
38-
*/
39-
currentChangedFilePath?: Path | undefined;
40-
/**
41-
* Map of file signatures, with key being file path, calculated while getting current changed file's affected files
42-
* These will be committed whenever the iteration through affected files of current changed file is complete
43-
*/
44-
currentAffectedFilesSignatures?: ESMap<Path, string> | undefined;
45-
/**
46-
* Newly computed visible to outside referencedSet
47-
*/
48-
currentAffectedFilesExportedModulesMap?: BuilderState.ManyToManyPathMap | undefined;
49-
/**
50-
* True if the semantic diagnostics were copied from the old state
51-
*/
52-
semanticDiagnosticsFromOldState?: Set<Path>;
5332
/**
5433
* program corresponding to this state
5534
*/
@@ -106,10 +85,18 @@ namespace ts {
10685
* The map has key by source file's path that has been changed
10786
*/
10887
changedFilesSet: Set<Path>;
88+
/**
89+
* Set of affected files being iterated
90+
*/
91+
affectedFiles?: readonly SourceFile[] | undefined;
10992
/**
11093
* Current index to retrieve affected file from
11194
*/
11295
affectedFilesIndex: number | undefined;
96+
/**
97+
* Current changed file for iterating over affected files
98+
*/
99+
currentChangedFilePath?: Path | undefined;
113100
/**
114101
* Already seen affected files
115102
*/
@@ -118,6 +105,10 @@ namespace ts {
118105
* whether this program has cleaned semantic diagnostics cache for lib files
119106
*/
120107
cleanedDiagnosticsOfLibFiles?: boolean;
108+
/**
109+
* True if the semantic diagnostics were copied from the old state
110+
*/
111+
semanticDiagnosticsFromOldState?: Set<Path>;
121112
/**
122113
* Records if change in dts emit was detected
123114
*/
@@ -187,18 +178,8 @@ namespace ts {
187178
!outFilePath &&
188179
!compilerOptionsAffectDeclarationPath(compilerOptions, oldCompilerOptions!);
189180
if (useOldState) {
190-
// Verify the sanity of old state
191-
if (!oldState!.currentChangedFilePath) {
192-
const affectedSignatures = oldState!.currentAffectedFilesSignatures;
193-
Debug.assert(!oldState!.affectedFiles && (!affectedSignatures || !affectedSignatures.size), "Cannot reuse if only few affected files of currentChangedFile were iterated");
194-
}
195-
const changedFilesSet = oldState!.changedFilesSet;
196-
if (canCopySemanticDiagnostics) {
197-
Debug.assert(!changedFilesSet?.size || !forEachKey(changedFilesSet, path => oldState!.semanticDiagnosticsPerFile!.has(path)), "Semantic diagnostics shouldnt be available for changed files");
198-
}
199-
200181
// Copy old state's changed files set
201-
changedFilesSet?.forEach(value => state.changedFilesSet.add(value));
182+
oldState!.changedFilesSet?.forEach(value => state.changedFilesSet.add(value));
202183
if (!outFilePath && oldState!.affectedFilesPendingEmit) {
203184
state.affectedFilesPendingEmit = oldState!.affectedFilesPendingEmit.slice();
204185
state.affectedFilesPendingEmitKind = oldState!.affectedFilesPendingEmitKind && new Map(oldState!.affectedFilesPendingEmitKind);
@@ -373,10 +354,8 @@ namespace ts {
373354
state.changedFilesSet.delete(state.currentChangedFilePath!);
374355
state.currentChangedFilePath = undefined;
375356
// Commit the changes in file signature
376-
BuilderState.updateSignaturesFromCache(state, state.currentAffectedFilesSignatures!);
377-
state.currentAffectedFilesSignatures!.clear();
378-
BuilderState.updateExportedFilesMapFromCache(state, state.currentAffectedFilesExportedModulesMap);
379-
state.currentAffectedFilesExportedModulesMap?.clear();
357+
state.oldSignatures?.clear();
358+
state.oldExportedModulesMap?.clear();
380359
state.affectedFiles = undefined;
381360
}
382361

@@ -397,11 +376,7 @@ namespace ts {
397376
}
398377

399378
// Get next batch of affected files
400-
if (!state.currentAffectedFilesSignatures) state.currentAffectedFilesSignatures = new Map();
401-
if (state.exportedModulesMap) {
402-
state.currentAffectedFilesExportedModulesMap ||= BuilderState.createManyToManyPathMap();
403-
}
404-
state.affectedFiles = BuilderState.getFilesAffectedBy(state, program, nextKey.value, cancellationToken, computeHash, state.currentAffectedFilesSignatures, state.currentAffectedFilesExportedModulesMap);
379+
state.affectedFiles = BuilderState.getFilesAffectedByWithOldState(state, program, nextKey.value, cancellationToken, computeHash);
405380
state.currentChangedFilePath = nextKey.value;
406381
state.affectedFilesIndex = 0;
407382
if (!state.seenAffectedFiles) state.seenAffectedFiles = new Set();
@@ -474,14 +449,11 @@ namespace ts {
474449
state,
475450
Debug.checkDefined(state.program),
476451
affectedFile,
477-
Debug.checkDefined(state.currentAffectedFilesSignatures),
478452
cancellationToken,
479453
computeHash,
480-
state.currentAffectedFilesExportedModulesMap
481454
);
482455
return;
483456
}
484-
Debug.assert(state.hasCalledUpdateShapeSignature?.has(affectedFile.resolvedPath) || state.currentAffectedFilesSignatures?.has(affectedFile.resolvedPath), `Signature not updated for affected file: ${affectedFile.fileName}`);
485457
if (state.compilerOptions.assumeChangesOnlyAffectDirectDependencies) return;
486458
handleDtsMayChangeOfReferencingExportOfAffectedFile(state, affectedFile, cancellationToken, computeHash, host);
487459
}
@@ -512,10 +484,8 @@ namespace ts {
512484
state,
513485
program,
514486
sourceFile,
515-
Debug.checkDefined(state.currentAffectedFilesSignatures),
516487
cancellationToken,
517488
computeHash,
518-
state.currentAffectedFilesExportedModulesMap,
519489
!host.disableUseFileVersionAsSignature
520490
);
521491
// If not dts emit, nothing more to do
@@ -540,32 +510,11 @@ namespace ts {
540510
}
541511

542512
function isChangedSignature(state: BuilderProgramState, path: Path) {
543-
const newSignature = Debug.checkDefined(state.currentAffectedFilesSignatures).get(path);
544-
const oldSignature = Debug.checkDefined(state.fileInfos.get(path)).signature;
513+
const oldSignature = Debug.checkDefined(state.oldSignatures).get(path) || undefined;
514+
const newSignature = Debug.checkDefined(state.fileInfos.get(path)).signature;
545515
return newSignature !== oldSignature;
546516
}
547517

548-
function forEachKeyOfExportedModulesMap<T>(
549-
state: BuilderProgramState,
550-
filePath: Path,
551-
fn: (exportedFromPath: Path) => T | undefined,
552-
): T | undefined {
553-
// Go through exported modules from cache first
554-
let keys = state.currentAffectedFilesExportedModulesMap!.getKeys(filePath);
555-
const result = keys && forEachKey(keys, fn);
556-
if (result) return result;
557-
558-
// If exported from path is not from cache and exported modules has path, all files referencing file exported from are affected
559-
keys = state.exportedModulesMap!.getKeys(filePath);
560-
return keys && forEachKey(keys, exportedFromPath =>
561-
// If the cache had an updated value, skip
562-
!state.currentAffectedFilesExportedModulesMap!.hasKey(exportedFromPath) &&
563-
!state.currentAffectedFilesExportedModulesMap!.deletedKeys()?.has(exportedFromPath) ?
564-
fn(exportedFromPath) :
565-
undefined
566-
);
567-
}
568-
569518
function handleDtsMayChangeOfGlobalScope(
570519
state: BuilderProgramState,
571520
filePath: Path,
@@ -622,11 +571,10 @@ namespace ts {
622571
}
623572
}
624573

625-
Debug.assert(!!state.currentAffectedFilesExportedModulesMap);
626574
const seenFileAndExportsOfFile = new Set<string>();
627575
// Go through exported modules from cache first
628576
// If exported modules has path, all files referencing file exported from are affected
629-
forEachKeyOfExportedModulesMap(state, affectedFile.resolvedPath, exportedFromPath => {
577+
state.exportedModulesMap.getKeys(affectedFile.resolvedPath)?.forEach(exportedFromPath => {
630578
if (handleDtsMayChangeOfGlobalScope(state, exportedFromPath, cancellationToken, computeHash, host)) return true;
631579
const references = state.referencedMap!.getKeys(exportedFromPath);
632580
return references && forEachKey(references, filePath =>
@@ -658,10 +606,9 @@ namespace ts {
658606

659607
if (handleDtsMayChangeOfGlobalScope(state, filePath, cancellationToken, computeHash, host)) return true;
660608
handleDtsMayChangeOf(state, filePath, cancellationToken, computeHash, host);
661-
Debug.assert(!!state.currentAffectedFilesExportedModulesMap);
662609

663610
// If exported modules has path, all files referencing file exported from are affected
664-
forEachKeyOfExportedModulesMap(state, filePath, exportedFromPath =>
611+
state.exportedModulesMap!.getKeys(filePath)?.forEach(exportedFromPath =>
665612
handleDtsMayChangeOfFileAndExportsOfFile(
666613
state,
667614
exportedFromPath,
@@ -849,8 +796,8 @@ namespace ts {
849796
// Ensure fileId
850797
const fileId = toFileId(key);
851798
Debug.assert(fileNames[fileId - 1] === relativeToBuildInfo(key));
852-
const signature = state.currentAffectedFilesSignatures && state.currentAffectedFilesSignatures.get(key);
853-
const actualSignature = signature ?? value.signature;
799+
const oldSignature = state.oldSignatures?.get(key);
800+
const actualSignature = oldSignature !== undefined ? oldSignature || undefined : value.signature;
854801
if (state.compilerOptions.composite) {
855802
const file = state.program!.getSourceFileByPath(key)!;
856803
if (!isJsonSourceFile(file) && sourceFileMayBeEmitted(file, state.program!)) {
@@ -867,11 +814,11 @@ namespace ts {
867814
// If file info only contains version and signature and both are same we can just write string
868815
value.version :
869816
actualSignature !== undefined ? // If signature is not same as version, encode signature in the fileInfo
870-
signature === undefined ?
817+
oldSignature === undefined ?
871818
// If we havent computed signature, use fileInfo as is
872819
value :
873820
// Serialize fileInfo with new updated signature
874-
{ version: value.version, signature, affectsGlobalScope: value.affectsGlobalScope, impliedFormat: value.impliedFormat } :
821+
{ version: value.version, signature: actualSignature, affectsGlobalScope: value.affectsGlobalScope, impliedFormat: value.impliedFormat } :
875822
// Signature of the FileInfo is undefined, serialize it as false
876823
{ version: value.version, signature: false, affectsGlobalScope: value.affectsGlobalScope, impliedFormat: value.impliedFormat };
877824
});
@@ -887,19 +834,11 @@ namespace ts {
887834
let exportedModulesMap: ProgramBuildInfoReferencedMap | undefined;
888835
if (state.exportedModulesMap) {
889836
exportedModulesMap = mapDefined(arrayFrom(state.exportedModulesMap.keys()).sort(compareStringsCaseSensitive), key => {
890-
if (state.currentAffectedFilesExportedModulesMap) {
891-
if (state.currentAffectedFilesExportedModulesMap.deletedKeys()?.has(key)) {
892-
return undefined;
893-
}
894-
895-
const newValue = state.currentAffectedFilesExportedModulesMap.getValues(key);
896-
if (newValue) {
897-
return [toFileId(key), toFileIdListId(newValue)];
898-
}
899-
}
900-
837+
const oldValue = state.oldExportedModulesMap?.get(key);
901838
// Not in temporary cache, use existing value
902-
return [toFileId(key), toFileIdListId(state.exportedModulesMap!.getValues(key)!)];
839+
if (oldValue === undefined) return [toFileId(key), toFileIdListId(state.exportedModulesMap!.getValues(key)!)];
840+
if (oldValue) return [toFileId(key), toFileIdListId(oldValue)];
841+
return undefined;
903842
});
904843
}
905844

@@ -1221,18 +1160,21 @@ namespace ts {
12211160
if (!customTransformers) {
12221161
const file = sourceFiles[0];
12231162
const info = state.fileInfos.get(file.resolvedPath)!;
1224-
const signature = state.currentAffectedFilesSignatures?.get(file.resolvedPath) || info.signature;
1225-
if (signature === file.version) {
1163+
if (info.signature === file.version) {
12261164
newSignature = computeSignature(text, data, computeHash);
12271165
if (newSignature !== file.version) { // Update it
12281166
if (host.storeFilesChangingSignatureDuringEmit) (state.filesChangingSignature ||= new Set()).add(file.resolvedPath);
1229-
if (state.exportedModulesMap) BuilderState.updateExportedModules(file, file.exportedModulesFromDeclarationEmit, state.currentAffectedFilesExportedModulesMap ||= BuilderState.createManyToManyPathMap());
1230-
if (state.affectedFiles && state.affectedFilesIndex! < state.affectedFiles.length) {
1231-
state.currentAffectedFilesSignatures!.set(file.resolvedPath, newSignature);
1167+
if (state.exportedModulesMap) BuilderState.updateExportedModules(state, file, file.exportedModulesFromDeclarationEmit);
1168+
if (state.affectedFiles) {
1169+
// Keep old signature so we know what to undo if cancellation happens
1170+
const existing = state.oldSignatures?.get(file.resolvedPath);
1171+
if (existing === undefined) (state.oldSignatures ||= new Map()).set(file.resolvedPath, info.signature || false);
1172+
info.signature = newSignature;
12321173
}
12331174
else {
1175+
// These are directly commited
12341176
info.signature = newSignature;
1235-
if (state.exportedModulesMap) BuilderState.updateExportedFilesMapFromCache(state, state.currentAffectedFilesExportedModulesMap);
1177+
state.oldExportedModulesMap?.clear();
12361178
}
12371179
}
12381180
}

0 commit comments

Comments
 (0)