fix(compiler-cli): prevent prior compilations from being retained in watch builds (#42537)
In watch builds, the compiler attempts to reuse as much information from a prior compilation as possible. To accomplish this, it keeps a reference to the most recently succeeded `TraitCompiler`, which contains all analysis data for the program. However, `TraitCompiler` has an internal reference to an `IncrementalBuild`, which is itself built on top of its prior state. Consequently, all prior compilations continued to be referenced, preventing garbage collection from cleaning up these instances. This commit changes the `AnalyzedIncrementalState` to no longer retain a `TraitCompiler` instance, but only the analysis data it contains. This breaks the retainer path to the prior incremental state, allowing it to be garbage collected. PR Close #42537
This commit is contained in:
parent
3961b3c360
commit
22bda2226b
|
@ -259,7 +259,7 @@ export class IncrementalCompilation implements IncrementalBuild<ClassRecord, Fil
|
|||
versions: this.versions,
|
||||
depGraph: this.depGraph,
|
||||
semanticDepGraph: newGraph,
|
||||
traitCompiler,
|
||||
priorAnalysis: traitCompiler.getAnalyzedRecords(),
|
||||
typeCheckResults: null,
|
||||
emitted,
|
||||
};
|
||||
|
@ -303,7 +303,11 @@ export class IncrementalCompilation implements IncrementalBuild<ClassRecord, Fil
|
|||
return null;
|
||||
}
|
||||
|
||||
return this.step.priorState.traitCompiler.recordsFor(sf);
|
||||
const priorAnalysis = this.step.priorState.priorAnalysis;
|
||||
if (!priorAnalysis.has(sf)) {
|
||||
return null;
|
||||
}
|
||||
return priorAnalysis.get(sf)!;
|
||||
}
|
||||
|
||||
priorTypeCheckingResultsFor(sf: ts.SourceFile): FileTypeCheckingData|null {
|
||||
|
|
|
@ -6,8 +6,10 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {AbsoluteFsPath} from '../../file_system';
|
||||
import {TraitCompiler} from '../../transform';
|
||||
import {ClassRecord} from '../../transform';
|
||||
import {FileTypeCheckingData} from '../../typecheck/src/checker';
|
||||
import {SemanticDepGraph} from '../semantic_graph';
|
||||
|
||||
|
@ -51,9 +53,10 @@ export interface AnalyzedIncrementalState {
|
|||
semanticDepGraph: SemanticDepGraph;
|
||||
|
||||
/**
|
||||
* `TraitCompiler` which contains records of all analyzed classes within the build.
|
||||
* The analysis data from a prior compilation. This stores the trait information for all source
|
||||
* files that was present in a prior compilation.
|
||||
*/
|
||||
traitCompiler: TraitCompiler;
|
||||
priorAnalysis: Map<ts.SourceFile, ClassRecord[]>;
|
||||
|
||||
/**
|
||||
* All generated template type-checking files produced as part of this compilation, or `null` if
|
||||
|
|
|
@ -16,6 +16,7 @@ ts_library(
|
|||
"//packages/compiler-cli/src/ngtsc/incremental",
|
||||
"//packages/compiler-cli/src/ngtsc/perf",
|
||||
"//packages/compiler-cli/src/ngtsc/testing",
|
||||
"//packages/compiler-cli/src/ngtsc/transform",
|
||||
"@npm//typescript",
|
||||
],
|
||||
)
|
||||
|
|
|
@ -10,6 +10,7 @@ import {absoluteFrom, getSourceFileOrError} from '../../file_system';
|
|||
import {runInEachFileSystem} from '../../file_system/testing';
|
||||
import {NOOP_PERF_RECORDER} from '../../perf';
|
||||
import {makeProgram} from '../../testing';
|
||||
import {TraitCompiler} from '../../transform';
|
||||
import {IncrementalCompilation} from '../src/incremental';
|
||||
|
||||
runInEachFileSystem(() => {
|
||||
|
@ -20,13 +21,14 @@ runInEachFileSystem(() => {
|
|||
{name: FOO_PATH, contents: `export const FOO = true;`},
|
||||
]);
|
||||
const fooSf = getSourceFileOrError(program, FOO_PATH);
|
||||
const traitCompiler = {getAnalyzedRecords: () => new Map()} as TraitCompiler;
|
||||
|
||||
const versionMapFirst = new Map([[FOO_PATH, 'version.1']]);
|
||||
const firstCompilation = IncrementalCompilation.fresh(
|
||||
program,
|
||||
versionMapFirst,
|
||||
);
|
||||
firstCompilation.recordSuccessfulAnalysis(null!);
|
||||
firstCompilation.recordSuccessfulAnalysis(traitCompiler);
|
||||
firstCompilation.recordSuccessfulEmit(fooSf);
|
||||
|
||||
const versionMapSecond = new Map([[FOO_PATH, 'version.2']]);
|
||||
|
@ -34,7 +36,7 @@ runInEachFileSystem(() => {
|
|||
program, versionMapSecond, program, firstCompilation.state, new Set(),
|
||||
NOOP_PERF_RECORDER);
|
||||
|
||||
secondCompilation.recordSuccessfulAnalysis(null!);
|
||||
secondCompilation.recordSuccessfulAnalysis(traitCompiler);
|
||||
expect(secondCompilation.safeToSkipEmit(fooSf)).toBeFalse();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -166,6 +166,18 @@ export class TraitCompiler implements ProgramTypeCheckAdapter {
|
|||
return records;
|
||||
}
|
||||
|
||||
getAnalyzedRecords(): Map<ts.SourceFile, ClassRecord[]> {
|
||||
const result = new Map<ts.SourceFile, ClassRecord[]>();
|
||||
for (const [sf, classes] of this.fileToClasses) {
|
||||
const records: ClassRecord[] = [];
|
||||
for (const clazz of classes) {
|
||||
records.push(this.classes.get(clazz)!);
|
||||
}
|
||||
result.set(sf, records);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a `ClassRecord` from a previous compilation.
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue