71956250dd
Incremental compilation allows for the output state of one compilation to be reused as input to the next compilation. This involves retaining references to instances from prior compilations, which must be done carefully to avoid memory leaks. This commit fixes such a leak with a complicated retention chain: * `TrackedIncrementalBuildStrategy` unnecessarily hangs on to the previous `IncrementalDriver` (state of the previous compilation) once the current compilation completes. In general this is unnecessary, but should be safe as long as the chain only goes back one level - if the `IncrementalDriver` doesn't retain any previous `TrackedIncrementalBuildStrategy` instances. However, this does happen: * `NgCompiler` indirectly causes retention of previous `NgCompiler` instances (and thus previous `TrackedIncrementalBuildStrategy` instances) through accidental capture of the `this` context in a closure created in its constructor. This closure is wrapped in a `ts.ModuleResolutionCache` used to create a `ModuleResolver` class, which is passed to the program's `TraitCompiler` on construction. * The `IncrementalDriver` retains a reference to the `TraitCompiler` of the previous compilation, completing the reference chain. The final retention chain thus looks like: * `TrackedIncrementalBuildStrategy` of current program * `.previous`: `IncrementalDriver` of previous program * `.lastGood.traitCompiler`: `TraitCompiler` * `.handlers[..].moduleResolver.moduleResolutionCache`: cache * (via `getCanonicalFileName` closure): `NgCompiler` * `.incrementalStrategy`: `TrackedIncrementalBuildStrategy` of previous program. The closure link is the "real" leak here. `NgCompiler` is creating a closure for `getCanonicalFileName`, delegating to its `this.adapter.getCanonicalFileName`, for the purposes of creating a `ts.ModuleResolutionCache`. The fact that the closure references `NgCompiler` thus eventually causes previous `NgCompiler` iterations to be retained. This is also potentially problematic due to the shared nature of `ts.ModuleResolutionCache`, which is potentially retained across multiple compilations intentionally. This commit fixes the first two links in the retention chain: the build strategy is patched to not retain a `previous` pointer, and the `NgCompiler` is patched to not create a closure in the first place, but instead pass a bound function. This ensures that the `NgCompiler` does not retain previous instances of itself in the first place, even if the build strategy does end up retaining the previous incremental state unnecessarily. The third link (`IncrementalDriver` unnecessarily retaining the whole `TraitCompiler`) is not addressed in this commit as it's a more architectural problem that will require some refactoring. However, the leak potential of this retention is eliminated thanks to fixing the first two issues. PR Close #37835 |
||
---|---|---|
.circleci | ||
.devcontainer | ||
.github | ||
.ng-dev | ||
.vscode | ||
.yarn | ||
aio | ||
dev-infra | ||
docs | ||
goldens | ||
integration | ||
modules | ||
packages | ||
scripts | ||
third_party | ||
tools | ||
.bazelignore | ||
.bazelrc | ||
.bazelversion | ||
.clang-format | ||
.editorconfig | ||
.gitattributes | ||
.gitignore | ||
.mailmap | ||
.nvmrc | ||
.pullapprove.yml | ||
.yarnrc | ||
BUILD.bazel | ||
CHANGELOG.md | ||
CODE_OF_CONDUCT.md | ||
CONTRIBUTING.md | ||
LICENSE | ||
README.md | ||
WORKSPACE | ||
browser-providers.conf.js | ||
gulpfile.js | ||
karma-js.conf.js | ||
package.json | ||
test-events.js | ||
test-main.js | ||
tslint.json | ||
yarn.lock | ||
yarn.lock.readme.md |
README.md
Angular
Angular is a development platform for building mobile and desktop web applications using TypeScript/JavaScript and other languages.
Quickstart
Changelog
Learn about the latest improvements.
Want to help?
Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on our guidelines for contributing and then check out one of our issues in the hotlist: community-help.