-
Notifications
You must be signed in to change notification settings - Fork 25.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
perf(compiler-cli): optimize computation of type-check scope informat…
…ion (#38539) When type-checking a component, the declaring NgModule scope is used to create a directive matcher that contains flattened directive metadata, i.e. the metadata of a directive and its base classes. This computation is done for all components, whereas the type-check scope is constant per NgModule. Additionally, the flattening of metadata is constant per directive instance so doesn't necessarily have to be recomputed for each component. This commit introduces a `TypeCheckScopes` class that is responsible for flattening directives and computing the scope per NgModule. It caches the computed results as appropriate to avoid repeated computation. PR Close #38539
- Loading branch information
1 parent
077f516
commit 297c060
Showing
5 changed files
with
117 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
105 changes: 105 additions & 0 deletions
105
packages/compiler-cli/src/ngtsc/annotations/src/typecheck_scopes.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
import {CssSelector, SchemaMetadata, SelectorMatcher} from '@angular/compiler'; | ||
import * as ts from 'typescript'; | ||
|
||
import {Reference} from '../../imports'; | ||
import {DirectiveMeta, flattenInheritedDirectiveMetadata, MetadataReader} from '../../metadata'; | ||
import {ClassDeclaration} from '../../reflection'; | ||
import {ComponentScopeReader} from '../../scope'; | ||
|
||
/** | ||
* The scope that is used for type-check code generation of a component template. | ||
*/ | ||
export interface TypeCheckScope { | ||
/** | ||
* A `SelectorMatcher` instance that contains the flattened directive metadata of all directives | ||
* that are in the compilation scope of the declaring NgModule. | ||
*/ | ||
matcher: SelectorMatcher<DirectiveMeta>; | ||
|
||
/** | ||
* The pipes that are available in the compilation scope. | ||
*/ | ||
pipes: Map<string, Reference<ClassDeclaration<ts.ClassDeclaration>>>; | ||
|
||
/** | ||
* The schemas that are used in this scope. | ||
*/ | ||
schemas: SchemaMetadata[]; | ||
} | ||
|
||
/** | ||
* Computes scope information to be used in template type checking. | ||
*/ | ||
export class TypeCheckScopes { | ||
/** | ||
* Cache of flattened directive metadata. Because flattened metadata is scope-invariant it's | ||
* cached individually, such that all scopes refer to the same flattened metadata. | ||
*/ | ||
private flattenedDirectiveMetaCache = new Map<ClassDeclaration, DirectiveMeta>(); | ||
|
||
/** | ||
* Cache of the computed type check scope per NgModule declaration. | ||
*/ | ||
private scopeCache = new Map<ClassDeclaration, TypeCheckScope>(); | ||
|
||
constructor(private scopeReader: ComponentScopeReader, private metaReader: MetadataReader) {} | ||
|
||
/** | ||
* Computes the type-check scope information for the component declaration. If the NgModule | ||
* contains an error, then 'error' is returned. If the component is not declared in any NgModule, | ||
* an empty type-check scope is returned. | ||
*/ | ||
getTypeCheckScope(node: ClassDeclaration): TypeCheckScope|'error' { | ||
const matcher = new SelectorMatcher<DirectiveMeta>(); | ||
const pipes = new Map<string, Reference<ClassDeclaration<ts.ClassDeclaration>>>(); | ||
|
||
const scope = this.scopeReader.getScopeForComponent(node); | ||
if (scope === null) { | ||
return {matcher, pipes, schemas: []}; | ||
} else if (scope === 'error') { | ||
return scope; | ||
} | ||
|
||
if (this.scopeCache.has(scope.ngModule)) { | ||
return this.scopeCache.get(scope.ngModule)!; | ||
} | ||
|
||
for (const meta of scope.compilation.directives) { | ||
if (meta.selector !== null) { | ||
const extMeta = this.getInheritedDirectiveMetadata(meta.ref); | ||
matcher.addSelectables(CssSelector.parse(meta.selector), extMeta); | ||
} | ||
} | ||
|
||
for (const {name, ref} of scope.compilation.pipes) { | ||
if (!ts.isClassDeclaration(ref.node)) { | ||
throw new Error(`Unexpected non-class declaration ${ | ||
ts.SyntaxKind[ref.node.kind]} for pipe ${ref.debugName}`); | ||
} | ||
pipes.set(name, ref as Reference<ClassDeclaration<ts.ClassDeclaration>>); | ||
} | ||
|
||
const typeCheckScope: TypeCheckScope = {matcher, pipes, schemas: scope.schemas}; | ||
this.scopeCache.set(scope.ngModule, typeCheckScope); | ||
return typeCheckScope; | ||
} | ||
|
||
private getInheritedDirectiveMetadata(ref: Reference<ClassDeclaration>): DirectiveMeta { | ||
const clazz = ref.node; | ||
if (this.flattenedDirectiveMetaCache.has(clazz)) { | ||
return this.flattenedDirectiveMetaCache.get(clazz)!; | ||
} | ||
|
||
const meta = flattenInheritedDirectiveMetadata(this.metaReader, ref); | ||
this.flattenedDirectiveMetaCache.set(clazz, meta); | ||
return meta; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters