diff --git a/packages/core/src/render3/assert.ts b/packages/core/src/render3/assert.ts index 67822c2e0e..32c0c57c2f 100644 --- a/packages/core/src/render3/assert.ts +++ b/packages/core/src/render3/assert.ts @@ -10,7 +10,7 @@ import {assertDefined, assertEqual, assertNumber, throwError} from '../util/asse import {getComponentDef, getNgModuleDef} from './definition'; import {LContainer} from './interfaces/container'; import {DirectiveDef} from './interfaces/definition'; -import {PARENT_INJECTOR} from './interfaces/injector'; +import {NodeInjectorOffset} from './interfaces/injector'; import {TNode} from './interfaces/node'; import {isLContainer, isLView} from './interfaces/type_checks'; import {HEADER_OFFSET, LView, TVIEW, TView} from './interfaces/view'; @@ -136,7 +136,7 @@ export function assertBetween(lower: number, upper: number, index: number) { */ export function assertNodeInjector(lView: LView, injectorIndex: number) { assertIndexInExpandoRange(lView, injectorIndex); - assertIndexInExpandoRange(lView, injectorIndex + PARENT_INJECTOR); + assertIndexInExpandoRange(lView, injectorIndex + NodeInjectorOffset.PARENT); assertNumber(lView[injectorIndex + 0], 'injectorIndex should point to a bloom filter'); assertNumber(lView[injectorIndex + 1], 'injectorIndex should point to a bloom filter'); assertNumber(lView[injectorIndex + 2], 'injectorIndex should point to a bloom filter'); @@ -146,6 +146,6 @@ export function assertNodeInjector(lView: LView, injectorIndex: number) { assertNumber(lView[injectorIndex + 6], 'injectorIndex should point to a bloom filter'); assertNumber(lView[injectorIndex + 7], 'injectorIndex should point to a bloom filter'); assertNumber( - lView[injectorIndex + 8 /*PARENT_INJECTOR*/], + lView[injectorIndex + NodeInjectorOffset.PARENT], 'injectorIndex should point to parent injector'); } diff --git a/packages/core/src/render3/context_discovery.ts b/packages/core/src/render3/context_discovery.ts index de2211c710..cf924e82b8 100644 --- a/packages/core/src/render3/context_discovery.ts +++ b/packages/core/src/render3/context_discovery.ts @@ -207,7 +207,7 @@ function traverseNextElement(tNode: TNode): TNode|null { if (tNode.child && tNode.child.parent === tNode) { // FIXME(misko): checking if `tNode.child.parent === tNode` should not be necessary // We have added it here because i18n creates TNode's which are not valid, so this is a work - // around. The i18n code is being refactored in #??? and once it lands this extra check can be + // around. The i18n code will be refactored in #39003 and once it lands this extra check can be // deleted. return tNode.child; } else if (tNode.next) { diff --git a/packages/core/src/render3/di.ts b/packages/core/src/render3/di.ts index d2284c7194..4b225ba508 100644 --- a/packages/core/src/render3/di.ts +++ b/packages/core/src/render3/di.ts @@ -21,7 +21,7 @@ import {getFactoryDef} from './definition'; import {NG_ELEMENT_ID, NG_FACTORY_DEF} from './fields'; import {registerPreOrderHooks} from './hooks'; import {DirectiveDef, FactoryFn} from './interfaces/definition'; -import {isFactory, NO_PARENT_INJECTOR, NodeInjectorFactory, PARENT_INJECTOR, RelativeInjectorLocation, RelativeInjectorLocationFlags, TNODE} from './interfaces/injector'; +import {isFactory, NO_PARENT_INJECTOR, NodeInjectorFactory, NodeInjectorOffset, RelativeInjectorLocation, RelativeInjectorLocationFlags} from './interfaces/injector'; import {AttributeMarker, TContainerNode, TDirectiveHostNode, TElementContainerNode, TElementNode, TNode, TNodeProviderIndexes, TNodeType} from './interfaces/node'; import {isComponentDef, isComponentHost} from './interfaces/type_checks'; import {DECLARATION_COMPONENT_VIEW, DECLARATION_VIEW, INJECTOR, LView, T_HOST, TData, TVIEW, TView, TViewType} from './interfaces/view'; @@ -170,12 +170,12 @@ export function getOrCreateNodeInjectorForNode( const parentData = parentLView[TVIEW].data as any; // Creates a cumulative bloom filter that merges the parent's bloom filter // and its own cumulative bloom (which contains tokens for all ancestors) - for (let i = 0; i < 8; i++) { + for (let i = 0; i < NodeInjectorOffset.BLOOM_SIZE; i++) { lView[injectorIndex + i] = parentLView[parentIndex + i] | parentData[parentIndex + i]; } } - lView[injectorIndex + PARENT_INJECTOR] = parentLoc; + lView[injectorIndex + NodeInjectorOffset.PARENT] = parentLoc; return injectorIndex; } @@ -191,7 +191,7 @@ export function getInjectorIndex(tNode: TNode, lView: LView): number { (tNode.parent && tNode.parent.injectorIndex === tNode.injectorIndex) || // After the first template pass, the injector index might exist but the parent values // might not have been calculated yet for this instance - lView[tNode.injectorIndex + PARENT_INJECTOR] == null) { + lView[tNode.injectorIndex + NodeInjectorOffset.PARENT] === null) { return -1; } else { ngDevMode && assertIndexInRange(lView, tNode.injectorIndex); @@ -402,7 +402,7 @@ export function getOrCreateInjectable( // searching the parent injector. if (injectorIndex === -1 || flags & InjectFlags.SkipSelf) { parentLocation = injectorIndex === -1 ? getParentInjectorLocation(tNode, lView) : - lView[injectorIndex + PARENT_INJECTOR]; + lView[injectorIndex + NodeInjectorOffset.PARENT]; if (parentLocation === NO_PARENT_INJECTOR || !shouldSearchParent(flags, false)) { injectorIndex = -1; @@ -420,7 +420,9 @@ export function getOrCreateInjectable( // Check the current injector. If it matches, see if it contains token. const tView = lView[TVIEW]; - ngDevMode && assertTNodeForLView(tView.data[injectorIndex + TNODE] as TNode, lView); + ngDevMode && + assertTNodeForLView( + tView.data[injectorIndex + NodeInjectorOffset.TNODE] as TNode, lView); if (bloomHasToken(bloomHash, injectorIndex, tView.data)) { // At this point, we have an injector which *may* contain the token, so we step through // the providers and directives associated with the injector's corresponding node to get @@ -431,10 +433,11 @@ export function getOrCreateInjectable( return instance; } } - parentLocation = lView[injectorIndex + PARENT_INJECTOR]; + parentLocation = lView[injectorIndex + NodeInjectorOffset.PARENT]; if (parentLocation !== NO_PARENT_INJECTOR && shouldSearchParent( - flags, lView[TVIEW].data[injectorIndex + TNODE] === hostTElementNode) && + flags, + lView[TVIEW].data[injectorIndex + NodeInjectorOffset.TNODE] === hostTElementNode) && bloomHasToken(bloomHash, injectorIndex, lView)) { // The def wasn't found anywhere on this node, so it was a false positive. // Traverse up the tree and continue searching. @@ -485,7 +488,7 @@ function searchTokensOnInjector( injectorIndex: number, lView: LView, token: Type|InjectionToken, previousTView: TView|null, flags: InjectFlags, hostTElementNode: TNode|null) { const currentTView = lView[TVIEW]; - const tNode = currentTView.data[injectorIndex + TNODE] as TNode; + const tNode = currentTView.data[injectorIndex + NodeInjectorOffset.TNODE] as TNode; // First, we need to determine if view providers can be accessed by the starting element. // There are two possibilities const canAccessViewProviders = previousTView == null ? diff --git a/packages/core/src/render3/instructions/lview_debug.ts b/packages/core/src/render3/instructions/lview_debug.ts index d7f47afaa9..17f6d56c9d 100644 --- a/packages/core/src/render3/instructions/lview_debug.ts +++ b/packages/core/src/render3/instructions/lview_debug.ts @@ -16,7 +16,7 @@ import {assertNodeInjector} from '../assert'; import {getInjectorIndex} from '../di'; import {CONTAINER_HEADER_OFFSET, HAS_TRANSPLANTED_VIEWS, LContainer, MOVED_VIEWS, NATIVE} from '../interfaces/container'; import {ComponentTemplate, DirectiveDef, DirectiveDefList, PipeDefList, ViewQueriesFunction} from '../interfaces/definition'; -import {NO_PARENT_INJECTOR, PARENT_INJECTOR, TNODE} from '../interfaces/injector'; +import {NO_PARENT_INJECTOR, NodeInjectorOffset} from '../interfaces/injector'; import {AttributeMarker, PropertyAliases, TConstants, TContainerNode, TElementNode, TNode as ITNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TNodeTypeAsString} from '../interfaces/node'; import {SelectorFlags} from '../interfaces/projection'; import {LQueries, TQueries} from '../interfaces/query'; @@ -218,9 +218,9 @@ class TNode implements ITNode { let injectorIndex = getInjectorIndex(this, lView); ngDevMode && assertNodeInjector(lView, injectorIndex); while (injectorIndex !== -1) { - const tNode = lView[TVIEW].data[injectorIndex + TNODE] as TNode; + const tNode = lView[TVIEW].data[injectorIndex + NodeInjectorOffset.TNODE] as TNode; path.push(buildDebugNode(tNode, lView)); - const parentLocation = lView[injectorIndex + PARENT_INJECTOR]; + const parentLocation = lView[injectorIndex + NodeInjectorOffset.PARENT]; if (parentLocation === NO_PARENT_INJECTOR) { injectorIndex = -1; } else { diff --git a/packages/core/src/render3/instructions/shared.ts b/packages/core/src/render3/instructions/shared.ts index 20b9b245c8..39242c4a38 100644 --- a/packages/core/src/render3/instructions/shared.ts +++ b/packages/core/src/render3/instructions/shared.ts @@ -25,7 +25,7 @@ import {throwMultipleComponentError} from '../errors'; import {executeCheckHooks, executeInitAndCheckHooks, incrementInitPhaseFlags} from '../hooks'; import {CONTAINER_HEADER_OFFSET, HAS_TRANSPLANTED_VIEWS, LContainer, MOVED_VIEWS} from '../interfaces/container'; import {ComponentDef, ComponentTemplate, DirectiveDef, DirectiveDefListOrFactory, PipeDefListOrFactory, RenderFlags, ViewQueriesFunction} from '../interfaces/definition'; -import {INJECTOR_BLOOM_PARENT_SIZE, NodeInjectorFactory} from '../interfaces/injector'; +import {NodeInjectorFactory, NodeInjectorOffset} from '../interfaces/injector'; import {AttributeMarker, InitialInputData, InitialInputs, LocalRefExtractor, PropertyAliases, PropertyAliasValue, TAttributes, TConstantsOrFactory, TContainerNode, TDirectiveHostNode, TElementContainerNode, TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TProjectionNode} from '../interfaces/node'; import {isProceduralRenderer, RComment, RElement, Renderer3, RendererFactory3, RNode, RText} from '../interfaces/renderer'; import {SanitizerFn} from '../interfaces/sanitization'; @@ -88,7 +88,7 @@ export function setHostBindingsByExecutingExpandoInstructions(tView: TView, lVie // Injector block and providers are taken into account. const providerCount = (expandoInstructions[++i] as number); - bindingRootIndex += INJECTOR_BLOOM_PARENT_SIZE + providerCount; + bindingRootIndex += NodeInjectorOffset.SIZE + providerCount; currentDirectiveIndex = bindingRootIndex; } else { diff --git a/packages/core/src/render3/interfaces/injector.ts b/packages/core/src/render3/interfaces/injector.ts index 566775cfa1..6762562651 100644 --- a/packages/core/src/render3/interfaces/injector.ts +++ b/packages/core/src/render3/interfaces/injector.ts @@ -15,13 +15,49 @@ import {TDirectiveHostNode} from './node'; import {LView, TData} from './view'; /** - * Offset from 'injectorIndex' where: - * - `TViewData[injectorIndex + TNODE]` => `TNode` associated with the current injector. - * - `LView[injectorIndex + TNODE]` => index to the parent injector. + * Offsets of the `NodeInjector` data structure in the expando. + * + * `NodeInjector` is stored in both `LView` as well as `TView.data`. All storage requires 9 words. + * First 8 are reserved for bloom filter and the 9th is reserved for the associated `TNode` as well + * as parent `NodeInjector` pointer. All indexes are starting with `index` and have an offset as + * shown. + * + * `LView` layout: + * ``` + * index + 0: cumulative bloom filter + * index + 1: cumulative bloom filter + * index + 2: cumulative bloom filter + * index + 3: cumulative bloom filter + * index + 4: cumulative bloom filter + * index + 5: cumulative bloom filter + * index + 6: cumulative bloom filter + * index + 7: cumulative bloom filter + * index + 8: cumulative bloom filter + * index + PARENT: Index to the parent injector. See `RelativeInjectorLocation` + * `const parent = lView[index + NodeInjectorOffset.PARENT]` + * ``` + * + * `TViewData` layout: + * ``` + * index + 0: cumulative bloom filter + * index + 1: cumulative bloom filter + * index + 2: cumulative bloom filter + * index + 3: cumulative bloom filter + * index + 4: cumulative bloom filter + * index + 5: cumulative bloom filter + * index + 6: cumulative bloom filter + * index + 7: cumulative bloom filter + * index + 8: cumulative bloom filter + * index + TNODE: TNode associated with this `NodeInjector` + * `canst tNode = tView.data[index + NodeInjectorOffset.TNODE]` + * ``` */ -export const TNODE = 8; -export const PARENT_INJECTOR = 8; -export const INJECTOR_BLOOM_PARENT_SIZE = 9; +export const enum NodeInjectorOffset { + TNODE = 8, + PARENT = 8, + BLOOM_SIZE = 8, + SIZE = 9, +} /** * Represents a relative location of parent injector. diff --git a/packages/core/src/render3/view_engine_compatibility.ts b/packages/core/src/render3/view_engine_compatibility.ts index 70c96e4629..7a29718943 100644 --- a/packages/core/src/render3/view_engine_compatibility.ts +++ b/packages/core/src/render3/view_engine_compatibility.ts @@ -22,7 +22,7 @@ import {assertLContainer, assertNodeInjector} from './assert'; import {getParentInjectorLocation, NodeInjector} from './di'; import {addToViewTree, createLContainer, createLView, renderView} from './instructions/shared'; import {CONTAINER_HEADER_OFFSET, LContainer, NATIVE, VIEW_REFS} from './interfaces/container'; -import {TNODE} from './interfaces/injector'; +import {NodeInjectorOffset} from './interfaces/injector'; import {TContainerNode, TDirectiveHostNode, TElementContainerNode, TElementNode, TNode, TNodeType} from './interfaces/node'; import {isProceduralRenderer, RComment, RElement} from './interfaces/renderer'; import {isComponentHost, isLContainer, isLView, isRootView} from './interfaces/type_checks'; @@ -190,7 +190,8 @@ export function createContainerRef( const parentView = getParentInjectorView(parentLocation, this._hostView); const injectorIndex = getParentInjectorIndex(parentLocation); ngDevMode && assertNodeInjector(parentView, injectorIndex); - const parentTNode = parentView[TVIEW].data[injectorIndex + TNODE] as TElementNode; + const parentTNode = + parentView[TVIEW].data[injectorIndex + NodeInjectorOffset.TNODE] as TElementNode; return new NodeInjector(parentTNode, parentView); } else { return new NodeInjector(null, this._hostView); diff --git a/packages/core/test/render3/di_spec.ts b/packages/core/test/render3/di_spec.ts index 2aa775ecc0..bd0585da29 100644 --- a/packages/core/test/render3/di_spec.ts +++ b/packages/core/test/render3/di_spec.ts @@ -9,11 +9,11 @@ import {InjectFlags, Optional, Renderer2, Self} from '@angular/core'; import {createLView, createTView, getOrCreateTNode} from '@angular/core/src/render3/instructions/shared'; import {RenderFlags} from '@angular/core/src/render3/interfaces/definition'; +import {NodeInjectorOffset} from '@angular/core/src/render3/interfaces/injector'; import {ɵɵdefineComponent} from '../../src/render3/definition'; import {bloomAdd, bloomHashBitOrFactory as bloomHash, bloomHasToken, getOrCreateNodeInjectorForNode} from '../../src/render3/di'; import {ɵɵdefineDirective, ɵɵdirectiveInject, ɵɵelement, ɵɵelementEnd, ɵɵelementStart, ɵɵtext} from '../../src/render3/index'; -import {TNODE} from '../../src/render3/interfaces/injector'; import {TNodeType} from '../../src/render3/interfaces/node'; import {isProceduralRenderer} from '../../src/render3/interfaces/renderer'; import {LViewFlags, TVIEW, TViewType} from '../../src/render3/interfaces/view'; @@ -150,7 +150,7 @@ describe('di', () => { }); function bloomState() { - return mockTView.data.slice(0, TNODE).reverse(); + return mockTView.data.slice(0, NodeInjectorOffset.TNODE).reverse(); } class Dir0 {