Skip to content

Commit

Permalink
fix(compiler-cli): prevent prior compilations from being retained in …
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
JoostK authored and alxhub committed Jun 9, 2021
1 parent 3961b3c commit 22bda22
Show file tree
Hide file tree
Showing 5 changed files with 29 additions and 7 deletions.
Expand Up @@ -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,
};
Expand Down Expand Up @@ -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 {
Expand Down
9 changes: 6 additions & 3 deletions packages/compiler-cli/src/ngtsc/incremental/src/state.ts
Expand Up @@ -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';

Expand Down Expand Up @@ -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
Expand Down
Expand Up @@ -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",
],
)
Expand Down
Expand Up @@ -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(() => {
Expand All @@ -20,21 +21,22 @@ 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']]);
const secondCompilation = IncrementalCompilation.incremental(
program, versionMapSecond, program, firstCompilation.state, new Set(),
NOOP_PERF_RECORDER);

secondCompilation.recordSuccessfulAnalysis(null!);
secondCompilation.recordSuccessfulAnalysis(traitCompiler);
expect(secondCompilation.safeToSkipEmit(fooSf)).toBeFalse();
});
});
Expand Down
12 changes: 12 additions & 0 deletions packages/compiler-cli/src/ngtsc/transform/src/compilation.ts
Expand Up @@ -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.
*
Expand Down

0 comments on commit 22bda22

Please sign in to comment.