perf(ivy): guard directive-related operations with a TNode flag (#32445)

PR Close #32445
This commit is contained in:
Pawel Kozlowski 2019-09-02 15:17:44 +02:00 committed by Miško Hevery
parent a383a5a165
commit 641c5c1c1e
16 changed files with 87 additions and 57 deletions

View File

@ -10,7 +10,7 @@ import {Injector} from '../di';
import {getViewComponent} from '../render3/global_utils_api';
import {CONTAINER_HEADER_OFFSET, LContainer, NATIVE} from '../render3/interfaces/container';
import {TElementNode, TNode, TNodeFlags, TNodeType} from '../render3/interfaces/node';
import {isComponent, isLContainer} from '../render3/interfaces/type_checks';
import {isComponentHost, isLContainer} from '../render3/interfaces/type_checks';
import {LView, PARENT, TData, TVIEW, T_HOST} from '../render3/interfaces/view';
import {TStylingContext} from '../render3/styling_next/interfaces';
import {stylingMapToStringMap} from '../render3/styling_next/map_based_bindings';
@ -481,7 +481,7 @@ function _queryNodeChildrenR3(
// Case 1: the TNode is an element
// The native node has to be checked.
_addQueryMatchR3(nativeNode, predicate, matches, elementsOnly, rootNativeNode);
if (isComponent(tNode)) {
if (isComponentHost(tNode)) {
// If the element is the host of a component, then all nodes in its view have to be processed.
// Note: the component's content (tNode.child) will be processed from the insertion points.
const componentView = getComponentViewByIndex(tNode.index, lView);

View File

@ -282,14 +282,14 @@ export function getDirectivesAtNodeIndex(
let directiveStartIndex = tNode.directiveStart;
if (directiveStartIndex == 0) return EMPTY_ARRAY;
const directiveEndIndex = tNode.directiveEnd;
if (!includeComponents && tNode.flags & TNodeFlags.isComponent) directiveStartIndex++;
if (!includeComponents && tNode.flags & TNodeFlags.isComponentHost) directiveStartIndex++;
return lView.slice(directiveStartIndex, directiveEndIndex);
}
export function getComponentAtNodeIndex(nodeIndex: number, lView: LView): {}|null {
const tNode = lView[TVIEW].data[nodeIndex] as TNode;
let directiveStartIndex = tNode.directiveStart;
return tNode.flags & TNodeFlags.isComponent ? lView[directiveStartIndex] : null;
return tNode.flags & TNodeFlags.isComponentHost ? lView[directiveStartIndex] : null;
}
/**

View File

@ -20,7 +20,7 @@ import {NG_ELEMENT_ID} from './fields';
import {DirectiveDef, FactoryFn} from './interfaces/definition';
import {NO_PARENT_INJECTOR, NodeInjectorFactory, PARENT_INJECTOR, RelativeInjectorLocation, RelativeInjectorLocationFlags, TNODE, isFactory} from './interfaces/injector';
import {AttributeMarker, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType} from './interfaces/node';
import {isComponent, isComponentDef} from './interfaces/type_checks';
import {isComponentDef, isComponentHost} from './interfaces/type_checks';
import {DECLARATION_VIEW, INJECTOR, LView, TData, TVIEW, TView, T_HOST} from './interfaces/view';
import {assertNodeOfPossibleTypes} from './node_assert';
import {getLView, getPreviousOrParentTNode, setTNodeAndViewData} from './state';
@ -154,7 +154,7 @@ export function getOrCreateNodeInjectorForNode(
insertBloom(tView.blueprint, null);
ngDevMode && assertEqual(
tNode.flags === 0 || tNode.flags === TNodeFlags.isComponent, true,
tNode.flags === 0 || tNode.flags === TNodeFlags.isComponentHost, true,
'expected tNode.flags to not be initialized');
}
@ -464,7 +464,7 @@ function searchTokensOnInjector<T>(
// - AND the injector set `includeViewProviders` to true (implying that the token can see
// ViewProviders because it is the Component or a Service which itself was declared in
// ViewProviders)
(isComponent(tNode) && includeViewProviders) :
(isComponentHost(tNode) && includeViewProviders) :
// 2) `previousTView != null` which means that we are now walking across the parent nodes.
// In such a case we are only allowed to look into the ViewProviders if:
// - We just crossed from child View to Parent View `previousTView != currentTView`

View File

@ -11,14 +11,15 @@ import {attachPatchData} from '../context_discovery';
import {executeCheckHooks, executeInitAndCheckHooks, incrementInitPhaseFlags, registerPostOrderHooks} from '../hooks';
import {ACTIVE_INDEX, CONTAINER_HEADER_OFFSET, LContainer} from '../interfaces/container';
import {ComponentTemplate} from '../interfaces/definition';
import {LocalRefExtractor, TAttributes, TContainerNode, TNode, TNodeType, TViewNode} from '../interfaces/node';
import {LocalRefExtractor, TAttributes, TContainerNode, TNode, TNodeFlags, TNodeType, TViewNode} from '../interfaces/node';
import {isDirectiveHost} from '../interfaces/type_checks';
import {BINDING_INDEX, FLAGS, HEADER_OFFSET, InitPhaseState, LView, LViewFlags, RENDERER, TVIEW, T_HOST} from '../interfaces/view';
import {assertNodeType} from '../node_assert';
import {appendChild, removeView} from '../node_manipulation';
import {getCheckNoChangesMode, getIsParent, getLView, getPreviousOrParentTNode, setIsNotParent, setPreviousOrParentTNode} from '../state';
import {getNativeByTNode, load} from '../util/view_utils';
import {addToViewTree, createDirectivesAndLocals, createLContainer, createTNode, createTView, getOrCreateTNode, resolveDirectives} from './shared';
import {addToViewTree, createDirectivesInstances, createLContainer, createTNode, createTView, getOrCreateTNode, resolveDirectives, saveResolvedLocalsInData} from './shared';
@ -89,8 +90,13 @@ export function ɵɵtemplate(
}
}
createDirectivesAndLocals(tView, lView, tContainerNode, localRefExtractor);
attachPatchData(getNativeByTNode(tContainerNode, lView), lView);
if (isDirectiveHost(tContainerNode)) {
createDirectivesInstances(tView, lView, tContainerNode);
}
if (localRefs != null) {
saveResolvedLocalsInData(lView, tContainerNode, localRefExtractor);
}
setIsNotParent();
}
@ -176,6 +182,7 @@ function containerInternal(
const lContainer = lView[adjustedIndex] = createLContainer(comment, lView, comment, tNode);
appendChild(comment, tNode, lView);
attachPatchData(getNativeByTNode(tNode, lView), lView);
// Containers are added to the current view tree instead of their embedded views
// because views can be removed and re-inserted.

View File

@ -12,7 +12,7 @@ import {attachPatchData} from '../context_discovery';
import {registerPostOrderHooks} from '../hooks';
import {TAttributes, TNodeFlags, TNodeType} from '../interfaces/node';
import {RElement} from '../interfaces/renderer';
import {isContentQueryHost} from '../interfaces/type_checks';
import {isContentQueryHost, isDirectiveHost} from '../interfaces/type_checks';
import {BINDING_INDEX, HEADER_OFFSET, LView, RENDERER, TVIEW, T_HOST} from '../interfaces/view';
import {assertNodeType} from '../node_assert';
import {appendChild} from '../node_manipulation';
@ -23,7 +23,7 @@ import {getInitialStylingValue, hasClassInput, hasStyleInput} from '../styling_n
import {setUpAttributes} from '../util/attrs_utils';
import {getNativeByTNode, getTNode} from '../util/view_utils';
import {createDirectivesAndLocals, elementCreate, executeContentQueries, getOrCreateTNode, initializeTNodeInputs, renderInitialStyling, resolveDirectives, setInputsForProperty} from './shared';
import {createDirectivesInstances, elementCreate, executeContentQueries, getOrCreateTNode, initializeTNodeInputs, renderInitialStyling, resolveDirectives, saveResolvedLocalsInData, setInputsForProperty} from './shared';
@ -98,8 +98,13 @@ export function ɵɵelementStart(
}
}
createDirectivesAndLocals(tView, lView, tNode);
executeContentQueries(tView, tNode, lView);
if (isDirectiveHost(tNode)) {
createDirectivesInstances(tView, lView, tNode);
executeContentQueries(tView, tNode, lView);
}
if (localRefs != null) {
saveResolvedLocalsInData(lView, tNode);
}
}
/**

View File

@ -10,14 +10,14 @@ import {assertHasParent} from '../assert';
import {attachPatchData} from '../context_discovery';
import {registerPostOrderHooks} from '../hooks';
import {TAttributes, TNodeType} from '../interfaces/node';
import {isContentQueryHost} from '../interfaces/type_checks';
import {isContentQueryHost, isDirectiveHost} from '../interfaces/type_checks';
import {BINDING_INDEX, HEADER_OFFSET, RENDERER, TVIEW, T_HOST} from '../interfaces/view';
import {assertNodeType} from '../node_assert';
import {appendChild} from '../node_manipulation';
import {getIsParent, getLView, getPreviousOrParentTNode, setIsNotParent, setPreviousOrParentTNode} from '../state';
import {registerInitialStylingOnTNode} from '../styling_next/instructions';
import {createDirectivesAndLocals, executeContentQueries, getOrCreateTNode, resolveDirectives} from './shared';
import {createDirectivesInstances, executeContentQueries, getOrCreateTNode, resolveDirectives, saveResolvedLocalsInData} from './shared';
@ -61,6 +61,7 @@ export function ɵɵelementContainerStart(
}
appendChild(native, tNode, lView);
attachPatchData(native, lView);
if (tView.firstTemplatePass) {
ngDevMode && ngDevMode.firstTemplatePass++;
@ -70,9 +71,14 @@ export function ɵɵelementContainerStart(
}
}
createDirectivesAndLocals(tView, lView, tNode);
attachPatchData(native, lView);
executeContentQueries(tView, tNode, lView);
if (isDirectiveHost(tNode)) {
createDirectivesInstances(tView, lView, tNode);
executeContentQueries(tView, tNode, lView);
}
if (localRefs != null) {
saveResolvedLocalsInData(lView, tNode);
}
}
/**

View File

@ -244,8 +244,9 @@ function wrapListener(
// In order to be backwards compatible with View Engine, events on component host nodes
// must also mark the component view itself dirty (i.e. the view that it owns).
const startView =
tNode.flags & TNodeFlags.isComponent ? getComponentViewByIndex(tNode.index, lView) : lView;
const startView = tNode.flags & TNodeFlags.isComponentHost ?
getComponentViewByIndex(tNode.index, lView) :
lView;
// See interfaces/view.ts for more on LViewFlags.ManualOnPush
if ((lView[FLAGS] & LViewFlags.ManualOnPush) === 0) {

View File

@ -161,7 +161,8 @@ export const TNodeConstructor = class TNode implements ITNode {
if (this.flags & TNodeFlags.hasClassInput) flags.push('TNodeFlags.hasClassInput');
if (this.flags & TNodeFlags.hasContentQuery) flags.push('TNodeFlags.hasContentQuery');
if (this.flags & TNodeFlags.hasStyleInput) flags.push('TNodeFlags.hasStyleInput');
if (this.flags & TNodeFlags.isComponent) flags.push('TNodeFlags.isComponent');
if (this.flags & TNodeFlags.isComponentHost) flags.push('TNodeFlags.isComponentHost');
if (this.flags & TNodeFlags.isDirectiveHost) flags.push('TNodeFlags.isDirectiveHost');
if (this.flags & TNodeFlags.isDetached) flags.push('TNodeFlags.isDetached');
if (this.flags & TNodeFlags.isProjected) flags.push('TNodeFlags.isProjected');
return flags.join('|');

View File

@ -25,7 +25,7 @@ import {INJECTOR_BLOOM_PARENT_SIZE, NodeInjectorFactory} from '../interfaces/inj
import {AttributeMarker, InitialInputData, InitialInputs, LocalRefExtractor, PropertyAliasValue, PropertyAliases, TAttributes, TContainerNode, TElementContainerNode, TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TProjectionNode, TViewNode} from '../interfaces/node';
import {RComment, RElement, RText, Renderer3, RendererFactory3, isProceduralRenderer} from '../interfaces/renderer';
import {SanitizerFn} from '../interfaces/sanitization';
import {isComponent, isComponentDef, isContentQueryHost, isLContainer, isRootView} from '../interfaces/type_checks';
import {isComponentDef, isComponentHost, isContentQueryHost, isLContainer, isRootView} from '../interfaces/type_checks';
import {BINDING_INDEX, CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_VIEW, ExpandoInstructions, FLAGS, HEADER_OFFSET, HOST, INJECTOR, InitPhaseState, LView, LViewFlags, NEXT, PARENT, RENDERER, RENDERER_FACTORY, RootContext, RootContextFlags, SANITIZER, TData, TVIEW, TView, T_HOST} from '../interfaces/view';
import {assertNodeOfPossibleTypes} from '../node_assert';
import {isNodeMatchingSelectorList} from '../node_selector_matcher';
@ -523,18 +523,13 @@ export function executeContentQueries(tView: TView, tNode: TNode, lView: LView)
/**
* Creates directive instances and populates local refs.
*
* @param localRefs Local refs of the node in question
* @param localRefExtractor mapping function that extracts local ref value from TNode
* Creates directive instances.
*/
export function createDirectivesAndLocals(
tView: TView, lView: LView, tNode: TElementNode | TContainerNode | TElementContainerNode,
localRefExtractor: LocalRefExtractor = getNativeByTNode) {
export function createDirectivesInstances(
tView: TView, lView: LView, tNode: TElementNode | TContainerNode | TElementContainerNode) {
if (!getBindingsEnabled()) return;
instantiateAllDirectives(tView, lView, tNode);
invokeDirectivesHostBindings(tView, lView, tNode);
saveResolvedLocalsInData(lView, tNode, localRefExtractor);
setActiveHostElement(null);
}
@ -542,8 +537,8 @@ export function createDirectivesAndLocals(
* Takes a list of local names and indices and pushes the resolved local variable values
* to LView in the same order as they are loaded in the template with load().
*/
function saveResolvedLocalsInData(
viewData: LView, tNode: TNode, localRefExtractor: LocalRefExtractor): void {
export function saveResolvedLocalsInData(
viewData: LView, tNode: TNode, localRefExtractor: LocalRefExtractor = getNativeByTNode): void {
const localNames = tNode.localNames;
if (localNames) {
let localIndex = tNode.index + 1;
@ -866,7 +861,7 @@ export function elementPropertyInternal<T>(
if (!nativeOnly && (inputData = initializeTNodeInputs(tView, tNode)) &&
(dataValue = inputData[propName])) {
setInputsForProperty(lView, dataValue, value);
if (isComponent(tNode)) markDirtyIfOnPush(lView, index + HEADER_OFFSET);
if (isComponentHost(tNode)) markDirtyIfOnPush(lView, index + HEADER_OFFSET);
if (ngDevMode) {
if (tNode.type === TNodeType.Element || tNode.type === TNodeType.Container) {
/**
@ -1199,7 +1194,7 @@ function findDirectiveMatches(
diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, viewData), tView, def.type);
if (isComponentDef(def)) {
if (tNode.flags & TNodeFlags.isComponent) throwMultipleComponentError(tNode);
if (tNode.flags & TNodeFlags.isComponentHost) throwMultipleComponentError(tNode);
markAsComponentHost(tView, tNode);
// The component is always stored first with directives after.
matches.unshift(def);
@ -1219,7 +1214,7 @@ function findDirectiveMatches(
*/
export function markAsComponentHost(tView: TView, hostTNode: TNode): void {
ngDevMode && assertFirstTemplatePass(tView);
hostTNode.flags = TNodeFlags.isComponent;
hostTNode.flags = TNodeFlags.isComponentHost;
(tView.components || (tView.components = ngDevMode ? new TViewComponents !() : [
])).push(hostTNode.index);
}
@ -1268,14 +1263,14 @@ function saveNameToExportMap(
export function initNodeFlags(tNode: TNode, index: number, numberOfDirectives: number) {
const flags = tNode.flags;
ngDevMode && assertEqual(
flags === 0 || flags === TNodeFlags.isComponent, true,
flags === 0 || flags === TNodeFlags.isComponentHost, true,
'expected node flags to not be initialized');
ngDevMode && assertNotEqual(
numberOfDirectives, tNode.directiveEnd - tNode.directiveStart,
'Reached the max number of directives');
// When the first directive is created on a node, save the index
tNode.flags = flags & TNodeFlags.isComponent;
tNode.flags = (flags & TNodeFlags.isComponentHost) | TNodeFlags.isDirectiveHost;
tNode.directiveStart = index;
tNode.directiveEnd = index + numberOfDirectives;
tNode.providerIndexes = index;

View File

@ -45,23 +45,28 @@ export const enum TNodeType {
* Corresponds to the TNode.flags property.
*/
export const enum TNodeFlags {
/** This bit is set if the node is a component */
isComponent = 0b000001,
/** This bit is set if the node is a host for any directive (including a component) */
isDirectiveHost = 0b00000001,
/**
* This bit is set if the node is a host for a component. Setting this bit implies that the
* isDirectiveHost bit is set as well. */
isComponentHost = 0b00000010,
/** This bit is set if the node has been projected */
isProjected = 0b000010,
isProjected = 0b00000100,
/** This bit is set if any directive on this node has content queries */
hasContentQuery = 0b000100,
hasContentQuery = 0b00001000,
/** This bit is set if the node has any "class" inputs */
hasClassInput = 0b001000,
hasClassInput = 0b00010000,
/** This bit is set if the node has any "style" inputs */
hasStyleInput = 0b010000,
hasStyleInput = 0b00100000,
/** This bit is set if the node has been detached by i18n */
isDetached = 0b100000,
isDetached = 0b01000000,
}
/**

View File

@ -34,8 +34,12 @@ export function isContentQueryHost(tNode: TNode): boolean {
return (tNode.flags & TNodeFlags.hasContentQuery) !== 0;
}
export function isComponent(tNode: TNode): boolean {
return (tNode.flags & TNodeFlags.isComponent) === TNodeFlags.isComponent;
export function isComponentHost(tNode: TNode): boolean {
return (tNode.flags & TNodeFlags.isComponentHost) === TNodeFlags.isComponentHost;
}
export function isDirectiveHost(tNode: TNode): boolean {
return (tNode.flags & TNodeFlags.isDirectiveHost) === TNodeFlags.isDirectiveHost;
}
export function isComponentDef<T>(def: DirectiveDef<T>): def is ComponentDef<T> {

View File

@ -526,7 +526,7 @@ function getRenderParent(tNode: TNode, currentView: LView): RElement|null {
}
ngDevMode && assertNodeType(parentTNode, TNodeType.Element);
if (parentTNode.flags & TNodeFlags.isComponent) {
if (parentTNode.flags & TNodeFlags.isComponentHost) {
const tData = currentView[TVIEW].data;
const tNode = tData[parentTNode.index] as TNode;
const encapsulation = (tData[tNode.directiveStart] as ComponentDef<any>).encapsulation;

View File

@ -24,7 +24,7 @@ import {addToViewTree, createLContainer, createLView, renderView} from './instru
import {ACTIVE_INDEX, CONTAINER_HEADER_OFFSET, LContainer, VIEW_REFS} from './interfaces/container';
import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeType, TViewNode} from './interfaces/node';
import {RComment, RElement, isProceduralRenderer} from './interfaces/renderer';
import {isComponent, isLContainer, isLView, isRootView} from './interfaces/type_checks';
import {isComponentHost, isLContainer, isLView, isRootView} from './interfaces/type_checks';
import {CONTEXT, DECLARATION_LCONTAINER, LView, LViewFlags, QUERIES, RENDERER, TView, T_HOST} from './interfaces/view';
import {assertNodeOfPossibleTypes} from './node_assert';
import {addRemoveViewFromContainer, appendChild, detachView, getBeforeNodeForView, insertView, nativeInsertBefore, nativeNextSibling, nativeParentNode, removeView} from './node_manipulation';
@ -371,7 +371,7 @@ export function injectChangeDetectorRef(isPipe = false): ViewEngine_ChangeDetect
*/
function createViewRef(
hostTNode: TNode, hostView: LView, isPipe: boolean): ViewEngine_ChangeDetectorRef {
if (isComponent(hostTNode) && !isPipe) {
if (isComponentHost(hostTNode) && !isPipe) {
const componentIndex = hostTNode.directiveStart;
const componentView = getComponentViewByIndex(hostTNode.index, hostView);
return new ViewRef(componentView, null, componentIndex);

View File

@ -204,7 +204,7 @@
"name": "concatString"
},
{
"name": "createDirectivesAndLocals"
"name": "createDirectivesInstances"
},
{
"name": "createLView"
@ -455,6 +455,9 @@
{
"name": "isCssClassMatching"
},
{
"name": "isDirectiveHost"
},
{
"name": "isFactory"
},
@ -698,4 +701,4 @@
{
"name": "ɵɵtext"
}
]
]

View File

@ -491,4 +491,4 @@
{
"name": "ɵɵtext"
}
]
]

View File

@ -537,7 +537,7 @@
"name": "createContainerRef"
},
{
"name": "createDirectivesAndLocals"
"name": "createDirectivesInstances"
},
{
"name": "createElementRef"
@ -987,10 +987,10 @@
"name": "isAnimationProp"
},
{
"name": "isComponent"
"name": "isComponentDef"
},
{
"name": "isComponentDef"
"name": "isComponentHost"
},
{
"name": "isContentQueryHost"
@ -1007,6 +1007,9 @@
{
"name": "isDevMode"
},
{
"name": "isDirectiveHost"
},
{
"name": "isFactory"
},