refactor(compiler): remove directive matching from template compiler (#44731)

The directive matching pass that happens during template compilation is
redundant, since directive matching has already happened during the resolution
phase of ngtsc and only matching declarables are provided to the template
compiler. In JIT mode the declarables only become available after the primary
template compilation has completed, so there is no need to perform directive
matching in both JIT and AOT mode.

PR Close #44731
This commit is contained in:
JoostK
2022-01-16 18:20:01 +01:00
committed by Dylan Hunn
parent dac0430efd
commit 1f6249dae8
2 changed files with 10 additions and 46 deletions
+6 -20
View File
@@ -12,7 +12,7 @@ import * as core from '../../core';
import {AST, ParsedEvent, ParsedEventType, ParsedProperty} from '../../expression_parser/ast';
import * as o from '../../output/output_ast';
import {ParseError, ParseSourceSpan, sanitizeIdentifier} from '../../parse_util';
import {CssSelector, SelectorMatcher} from '../../selector';
import {CssSelector} from '../../selector';
import {ShadowCss} from '../../shadow_css';
import {BindingParser} from '../../template_parser/binding_parser';
import {error} from '../../util';
@@ -152,30 +152,16 @@ export function compileComponentFromMetadata(
}
}
// Generate the CSS matcher that recognize directive
let directiveMatcher: SelectorMatcher|null = null;
if (meta.directives.length > 0) {
const matcher = new SelectorMatcher();
for (const {selector, type} of meta.directives) {
matcher.addSelectables(CssSelector.parse(selector), type);
}
directiveMatcher = matcher;
}
// e.g. `template: function MyComponent_Template(_ctx, _cm) {...}`
const templateTypeName = meta.name;
const templateName = templateTypeName ? `${templateTypeName}_Template` : null;
const directivesUsed = new Set<o.Expression>();
const pipesUsed = new Set<o.Expression>();
const changeDetection = meta.changeDetection;
const template = meta.template;
const templateBuilder = new TemplateDefinitionBuilder(
constantPool, BindingScope.createRootScope(), 0, templateTypeName, null, null, templateName,
directiveMatcher, directivesUsed, meta.pipes, pipesUsed, R3.namespaceHTML,
meta.relativeContextFilePath, meta.i18nUseExternalIds);
R3.namespaceHTML, meta.relativeContextFilePath, meta.i18nUseExternalIds);
const templateFunctionExpression = templateBuilder.buildTemplateFunction(template.nodes, []);
@@ -210,15 +196,15 @@ export function compileComponentFromMetadata(
definitionMap.set('template', templateFunctionExpression);
// e.g. `directives: [MyDirective]`
if (directivesUsed.size) {
const directivesList = o.literalArr(Array.from(directivesUsed));
if (meta.directives.length > 0) {
const directivesList = o.literalArr(meta.directives.map(dir => dir.type));
const directivesExpr = compileDeclarationList(directivesList, meta.declarationListEmitMode);
definitionMap.set('directives', directivesExpr);
}
// e.g. `pipes: [MyPipe]`
if (pipesUsed.size) {
const pipesList = o.literalArr(Array.from(pipesUsed));
if (meta.pipes.size > 0) {
const pipesList = o.literalArr(Array.from(meta.pipes.values()));
const pipesExpr = compileDeclarationList(pipesList, meta.declarationListEmitMode);
definitionMap.set('pipes', pipesExpr);
}
+4 -26
View File
@@ -24,7 +24,7 @@ import * as o from '../../output/output_ast';
import {ParseError, ParseSourceSpan, sanitizeIdentifier} from '../../parse_util';
import {DomElementSchemaRegistry} from '../../schema/dom_element_schema_registry';
import {isTrustedTypesSink} from '../../schema/trusted_types_sinks';
import {CssSelector, SelectorMatcher} from '../../selector';
import {CssSelector} from '../../selector';
import {BindingParser} from '../../template_parser/binding_parser';
import {error, partitionArray} from '../../util';
import * as t from '../r3_ast';
@@ -38,7 +38,7 @@ import {createLocalizeStatements} from './i18n/localize_utils';
import {I18nMetaVisitor} from './i18n/meta';
import {assembleBoundTextPlaceholders, assembleI18nBoundString, declareI18nVariable, getTranslationConstPrefix, hasI18nMeta, I18N_ICU_MAPPING_PREFIX, i18nFormatPlaceholderNames, icuFromI18nMessage, isI18nRootNode, isSingleI18nIcu, placeholdersToParams, TRANSLATION_VAR_PREFIX, wrapI18nPlaceholder} from './i18n/util';
import {StylingBuilder, StylingInstruction} from './styling_builder';
import {asLiteral, chainedInstruction, CONTEXT_NAME, getAttrsForDirectiveMatching, getInterpolationArgsLength, IMPLICIT_REFERENCE, invalid, NON_BINDABLE_ATTR, REFERENCE_PREFIX, RENDER_FLAGS, RESTORED_VIEW_CONTEXT_NAME, trimTrailingNulls} from './util';
import {asLiteral, chainedInstruction, CONTEXT_NAME, getInterpolationArgsLength, IMPLICIT_REFERENCE, invalid, NON_BINDABLE_ATTR, REFERENCE_PREFIX, RENDER_FLAGS, RESTORED_VIEW_CONTEXT_NAME, trimTrailingNulls} from './util';
@@ -200,8 +200,6 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
private constantPool: ConstantPool, parentBindingScope: BindingScope, private level = 0,
private contextName: string|null, private i18nContext: I18nContext|null,
private templateIndex: number|null, private templateName: string|null,
private directiveMatcher: SelectorMatcher|null, private directives: Set<o.Expression>,
private pipeTypeByName: Map<string, o.Expression>, private pipes: Set<o.Expression>,
private _namespace: o.ExternalReference, relativeContextFilePath: string,
private i18nUseExternalIds: boolean,
private _constants: ComponentDefConsts = createComponentDefConsts()) {
@@ -215,10 +213,6 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
constantPool, () => this.allocateDataSlot(),
(numSlots: number) => this.allocatePureFunctionSlots(numSlots),
(name, localName, slot, value: o.Expression) => {
const pipeType = pipeTypeByName.get(name);
if (pipeType) {
this.pipes.add(pipeType);
}
this._bindingScope.set(this.level, localName, value);
this.creationInstruction(null, R3.pipe, [o.literal(slot), o.literal(name)]);
});
@@ -638,9 +632,6 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
}
}
// Match directives on non i18n attributes
this.matchDirectives(element.name, element);
// Regular element or ng-container creation mode
const parameters: o.Expression[] = [o.literal(elementIndex)];
if (!isNgContainer) {
@@ -891,9 +882,6 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
o.literal(tagNameWithoutNamespace),
];
// find directives matching on a given <ng-template> node
this.matchDirectives(NG_TEMPLATE_TAG_NAME, template);
// prepare attributes parameter (including attributes used for directive matching)
const attrsExprs: o.Expression[] = this.getAttributeExpressions(
NG_TEMPLATE_TAG_NAME, template.attributes, template.inputs, template.outputs,
@@ -910,9 +898,8 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
// Create the template function
const templateVisitor = new TemplateDefinitionBuilder(
this.constantPool, this._bindingScope, this.level + 1, contextName, this.i18n,
templateIndex, templateName, this.directiveMatcher, this.directives, this.pipeTypeByName,
this.pipes, this._namespace, this.fileBasedI18nSuffix, this.i18nUseExternalIds,
this._constants);
templateIndex, templateName, this._namespace, this.fileBasedI18nSuffix,
this.i18nUseExternalIds, this._constants);
// Nested templates must not be visited until after their parent templates have completed
// processing, so they are queued here until after the initial pass. Otherwise, we wouldn't
@@ -1281,15 +1268,6 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
return args;
}
private matchDirectives(elementName: string, elOrTpl: t.Element|t.Template) {
if (this.directiveMatcher) {
const selector = createCssSelector(elementName, getAttrsForDirectiveMatching(elOrTpl));
this.directiveMatcher.match(selector, (cssSelector, staticType) => {
this.directives.add(staticType);
});
}
}
/**
* Prepares all attribute expression values for the `TAttributes` array.
*