refactor(migrations): speed up reference resolution in templates (#58106)

Instead of inspecting all types of property accesses, we can use a
similar optimization we did for TS references— leveraging the list of
known field names to minimize type checker calls.

PR Close #58106
This commit is contained in:
Paul Gschwendtner 2024-10-07 11:37:10 +00:00
parent 03af45540c
commit 37483926ea
4 changed files with 33 additions and 5 deletions

View file

@ -38,6 +38,7 @@ export function identifyHostBindingReferences<D extends ClassFieldDescriptor>(
reflector: ReflectionHost,
result: ReferenceResult<D>,
knownFields: KnownFields<D>,
fieldNamesToConsiderForReferenceLookup: Set<string> | null,
) {
if (node.name === undefined) {
return;
@ -97,6 +98,7 @@ export function identifyHostBindingReferences<D extends ClassFieldDescriptor>(
null,
node,
knownFields,
fieldNamesToConsiderForReferenceLookup,
);
for (const [rawName, expression] of hostMap.entries()) {

View file

@ -14,7 +14,7 @@ import {absoluteFrom} from '@angular/compiler-cli/src/ngtsc/file_system';
import {PartialEvaluator} from '@angular/compiler-cli/src/ngtsc/partial_evaluator';
import {ClassDeclaration, ReflectionHost} from '@angular/compiler-cli/src/ngtsc/reflection';
import {CompilationMode} from '@angular/compiler-cli/src/ngtsc/transform';
import {TemplateTypeChecker} from '@angular/compiler-cli/src/ngtsc/typecheck/api';
import {OptimizeFor, TemplateTypeChecker} from '@angular/compiler-cli/src/ngtsc/typecheck/api';
import ts from 'typescript';
import {ProgramInfo, projectFile} from '../../../../../utils/tsurge';
import {TemplateReferenceVisitor} from './template_reference_visitor';
@ -38,9 +38,10 @@ export function identifyTemplateReferences<D extends ClassFieldDescriptor>(
options: NgCompilerOptions,
result: ReferenceResult<D>,
knownFields: KnownFields<D>,
fieldNamesToConsiderForReferenceLookup: Set<string> | null,
) {
const template =
templateTypeChecker.getTemplate(node) ??
templateTypeChecker.getTemplate(node, OptimizeFor.WholeProgram) ??
// If there is no template registered in the TCB or compiler, the template may
// be skipped due to an explicit `jit: true` setting. We try to detect this case
// and parse the template manually.
@ -54,7 +55,13 @@ export function identifyTemplateReferences<D extends ClassFieldDescriptor>(
);
if (template !== null) {
const visitor = new TemplateReferenceVisitor(checker, templateTypeChecker, node, knownFields);
const visitor = new TemplateReferenceVisitor(
checker,
templateTypeChecker,
node,
knownFields,
fieldNamesToConsiderForReferenceLookup,
);
template.forEach((node) => node.visit(visitor));
for (const res of visitor.result) {
@ -67,7 +74,7 @@ export function identifyTemplateReferences<D extends ClassFieldDescriptor>(
if (templateFilePath === '') {
// TODO: Incorporate a TODO potentially.
console.error(
`Found reference to input ${res.targetField.key} that cannot be ` +
`Found reference to field ${res.targetField.key} that cannot be ` +
`migrated because the template cannot be parsed with source map information ` +
`(in file: ${node.getSourceFile().fileName}).`,
);

View file

@ -79,11 +79,20 @@ export function createFindAllSourceFileReferencesVisitor<D extends ClassFieldDes
programInfo.userOptions,
result,
knownFields,
fieldNamesToConsiderForReferenceLookup,
);
perfCounters.template += (currentTimeInMs() - lastTime) / 1000;
lastTime = currentTimeInMs();
identifyHostBindingReferences(node, programInfo, checker, reflector, result, knownFields);
identifyHostBindingReferences(
node,
programInfo,
checker,
reflector,
result,
knownFields,
fieldNamesToConsiderForReferenceLookup,
);
perfCounters.hostBindings += (currentTimeInMs() - lastTime) / 1000;
lastTime = currentTimeInMs();

View file

@ -81,6 +81,7 @@ export class TemplateReferenceVisitor<
templateTypeChecker: TemplateTypeChecker,
componentClass: ts.ClassDeclaration,
knownFields: KnownFields<D>,
fieldNamesToConsiderForReferenceLookup: Set<string> | null,
) {
super();
this.expressionVisitor = new TemplateExpressionReferenceVisitor(
@ -88,6 +89,7 @@ export class TemplateReferenceVisitor<
templateTypeChecker,
componentClass,
knownFields,
fieldNamesToConsiderForReferenceLookup,
);
}
@ -244,6 +246,7 @@ export class TemplateExpressionReferenceVisitor<
private templateTypeChecker: TemplateTypeChecker | null,
private componentClass: ts.ClassDeclaration,
private knownFields: KnownFields<D>,
private fieldNamesToConsiderForReferenceLookup: Set<string> | null,
) {
super();
}
@ -294,6 +297,13 @@ export class TemplateExpressionReferenceVisitor<
* a known field. If so, the result is captured.
*/
private _inspectPropertyAccess(ast: PropertyRead | PropertyWrite, astPath: AST[]) {
if (
this.fieldNamesToConsiderForReferenceLookup !== null &&
!this.fieldNamesToConsiderForReferenceLookup.has(ast.name)
) {
return;
}
const isWrite = !!(
ast instanceof PropertyWrite ||
(this.activeTmplAstNode && isTwoWayBindingNode(this.activeTmplAstNode))