refactor(ivy): flatten LInjector into LViewData (#26220)

PR Close #26220
This commit is contained in:
Kara Erickson 2018-10-02 21:12:26 -07:00 committed by Alex Rickabaugh
parent cb59d87489
commit 730679964f
13 changed files with 457 additions and 353 deletions

View File

@ -47,10 +47,7 @@ class Render3DebugContext implements DebugContext {
get injector(): Injector {
if (this.nodeIndex !== null) {
const tNode = this.view[TVIEW].data[this.nodeIndex];
const nodeInjector = di.getInjector(tNode, this.view);
if (nodeInjector) {
return new di.NodeInjector(nodeInjector);
}
return new di.NodeInjector(tNode, this.view);
}
return Injector.NULL;
}

View File

@ -20,10 +20,10 @@ import {getComponentDef, getDirectiveDef, getPipeDef} from './definition';
import {NG_ELEMENT_ID} from './fields';
import {_getViewData, assertPreviousIsParent, getPreviousOrParentTNode, resolveDirective, setEnvironment} from './instructions';
import {DirectiveDefInternal} from './interfaces/definition';
import {LInjector} from './interfaces/injector';
import {INJECTOR_SIZE, InjectorLocationFlags, PARENT_INJECTOR, TNODE,} from './interfaces/injector';
import {AttributeMarker, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType} from './interfaces/node';
import {isProceduralRenderer} from './interfaces/renderer';
import {DECLARATION_VIEW, DIRECTIVES, HOST_NODE, INJECTOR, LViewData, RENDERER, TVIEW, TView} from './interfaces/view';
import {DECLARATION_VIEW, DIRECTIVES, HOST_NODE, INJECTOR, LViewData, PARENT, RENDERER, TData, TVIEW, TView} from './interfaces/view';
import {assertNodeOfPossibleTypes} from './node_assert';
/**
@ -41,43 +41,47 @@ let nextNgElementId = 0;
* Registers this directive as present in its node's injector by flipping the directive's
* corresponding bit in the injector's bloom filter.
*
* @param injector The node injector in which the directive should be registered
* @param type The directive to register
* @param injectorIndex The index of the node injector where this token should be registered
* @param tView The TView for the injector's bloom filters
* @param type The directive token to register
*/
export function bloomAdd(injector: LInjector, type: Type<any>): void {
let id: number|undefined = (type as any)[NG_ELEMENT_ID];
export function bloomAdd(injectorIndex: number, tView: TView, type: Type<any>): void {
if (tView.firstTemplatePass) {
let id: number|undefined = (type as any)[NG_ELEMENT_ID];
// Set a unique ID on the directive type, so if something tries to inject the directive,
// we can easily retrieve the ID and hash it into the bloom bit that should be checked.
if (id == null) {
id = (type as any)[NG_ELEMENT_ID] = nextNgElementId++;
}
// Set a unique ID on the directive type, so if something tries to inject the directive,
// we can easily retrieve the ID and hash it into the bloom bit that should be checked.
if (id == null) {
id = (type as any)[NG_ELEMENT_ID] = nextNgElementId++;
}
// We only have BLOOM_SIZE (256) slots in our bloom filter (8 buckets * 32 bits each),
// so all unique IDs must be modulo-ed into a number from 0 - 255 to fit into the filter.
const bloomBit = id & BLOOM_MASK;
// We only have BLOOM_SIZE (256) slots in our bloom filter (8 buckets * 32 bits each),
// so all unique IDs must be modulo-ed into a number from 0 - 255 to fit into the filter.
const bloomBit = id & BLOOM_MASK;
// Create a mask that targets the specific bit associated with the directive.
// JS bit operations are 32 bits, so this will be a number between 2^0 and 2^31, corresponding
// to bit positions 0 - 31 in a 32 bit integer.
const mask = 1 << bloomBit;
// Create a mask that targets the specific bit associated with the directive.
// JS bit operations are 32 bits, so this will be a number between 2^0 and 2^31, corresponding
// to bit positions 0 - 31 in a 32 bit integer.
const mask = 1 << bloomBit;
// Use the raw bloomBit number to determine which bloom filter bucket we should check
// e.g: bf0 = [0 - 31], bf1 = [32 - 63], bf2 = [64 - 95], bf3 = [96 - 127], etc
const b7 = bloomBit & 0x80;
const b6 = bloomBit & 0x40;
const b5 = bloomBit & 0x20;
// Use the raw bloomBit number to determine which bloom filter bucket we should check
// e.g: bf0 = [0 - 31], bf1 = [32 - 63], bf2 = [64 - 95], bf3 = [96 - 127], etc
const b7 = bloomBit & 0x80;
const b6 = bloomBit & 0x40;
const b5 = bloomBit & 0x20;
const tData = tView.data as number[];
if (b7) {
b6 ? (b5 ? (injector.bf7 |= mask) : (injector.bf6 |= mask)) :
(b5 ? (injector.bf5 |= mask) : (injector.bf4 |= mask));
} else {
b6 ? (b5 ? (injector.bf3 |= mask) : (injector.bf2 |= mask)) :
(b5 ? (injector.bf1 |= mask) : (injector.bf0 |= mask));
if (b7) {
b6 ? (b5 ? (tData[injectorIndex + 7] |= mask) : (tData[injectorIndex + 6] |= mask)) :
(b5 ? (tData[injectorIndex + 5] |= mask) : (tData[injectorIndex + 4] |= mask));
} else {
b6 ? (b5 ? (tData[injectorIndex + 3] |= mask) : (tData[injectorIndex + 2] |= mask)) :
(b5 ? (tData[injectorIndex + 1] |= mask) : (tData[injectorIndex] |= mask));
}
}
}
export function getOrCreateNodeInjector(): LInjector {
export function getOrCreateNodeInjector(): number {
ngDevMode && assertPreviousIsParent();
return getOrCreateNodeInjectorForNode(
getPreviousOrParentTNode() as TElementNode | TElementContainerNode | TContainerNode,
@ -92,67 +96,97 @@ export function getOrCreateNodeInjector(): LInjector {
* @returns Node injector
*/
export function getOrCreateNodeInjectorForNode(
tNode: TElementNode | TContainerNode | TElementContainerNode, hostView: LViewData): LInjector {
const injector = getInjector(tNode, hostView);
if (injector) return injector;
tNode: TElementNode | TContainerNode | TElementContainerNode, hostView: LViewData): number {
const existingInjectorIndex = getInjectorIndex(tNode, hostView);
if (existingInjectorIndex !== -1) {
return existingInjectorIndex;
}
const tView = hostView[TVIEW];
if (tView.firstTemplatePass) {
// TODO(kara): Store node injector with host bindings for that node (see VIEW_DATA.md)
tNode.injectorIndex = hostView.length;
tView.blueprint.push(null);
tView.hostBindingStartIndex++;
tView.blueprint.push(0, 0, 0, 0, 0, 0, 0, 0, null); // foundation for cumulative bloom
tView.data.push(0, 0, 0, 0, 0, 0, 0, 0, tNode); // foundation for node bloom
tView.hostBindingStartIndex += INJECTOR_SIZE;
}
const parentInjector = getParentInjector(tNode, hostView);
return hostView[tNode.injectorIndex] = {
parent: parentInjector,
tNode: tNode,
view: hostView,
bf0: 0,
bf1: 0,
bf2: 0,
bf3: 0,
bf4: 0,
bf5: 0,
bf6: 0,
bf7: 0,
cbf0: parentInjector == null ? 0 : parentInjector.cbf0 | parentInjector.bf0,
cbf1: parentInjector == null ? 0 : parentInjector.cbf1 | parentInjector.bf1,
cbf2: parentInjector == null ? 0 : parentInjector.cbf2 | parentInjector.bf2,
cbf3: parentInjector == null ? 0 : parentInjector.cbf3 | parentInjector.bf3,
cbf4: parentInjector == null ? 0 : parentInjector.cbf4 | parentInjector.bf4,
cbf5: parentInjector == null ? 0 : parentInjector.cbf5 | parentInjector.bf5,
cbf6: parentInjector == null ? 0 : parentInjector.cbf6 | parentInjector.bf6,
cbf7: parentInjector == null ? 0 : parentInjector.cbf7 | parentInjector.bf7,
};
const parentLoc = getParentInjectorLocation(tNode, hostView);
const parentIndex = parentLoc & InjectorLocationFlags.InjectorIndexMask;
const parentView: LViewData = getParentInjectorView(parentLoc, hostView);
const parentData = parentView[TVIEW].data as any;
const injectorIndex = tNode.injectorIndex;
for (let i = 0; i < PARENT_INJECTOR; i++) {
const bloomIndex = parentIndex + i;
hostView[injectorIndex + i] =
parentLoc === -1 ? 0 : parentView[bloomIndex] | parentData[bloomIndex];
}
hostView[injectorIndex + PARENT_INJECTOR] = parentLoc;
return injectorIndex;
}
export function getInjector(tNode: TNode, view: LViewData): LInjector|null {
// If the injector index is the same as its parent's injector index, then the index has been
// copied down from the parent node. No injector has been created yet on this node.
export function getInjectorIndex(tNode: TNode, hostView: LViewData): number {
if (tNode.injectorIndex === -1 ||
tNode.parent && tNode.parent.injectorIndex === tNode.injectorIndex) {
return null;
// If the injector index is the same as its parent's injector index, then the index has been
// copied down from the parent node. No injector has been created yet on this node.
(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
hostView[tNode.injectorIndex + PARENT_INJECTOR] == null) {
return -1;
} else {
return view[tNode.injectorIndex];
return tNode.injectorIndex;
}
}
export function getParentInjector(tNode: TNode, view: LViewData): LInjector {
/**
* Finds the index of the parent injector, with a view offset if applicable. Used to set the
* parent injector initially.
*/
export function getParentInjectorLocation(tNode: TNode, view: LViewData): number {
if (tNode.parent && tNode.parent.injectorIndex !== -1) {
return view[tNode.parent.injectorIndex];
return tNode.parent.injectorIndex; // view offset is 0
}
// For most cases, the parent injector index can be found on the host node (e.g. for component
// or container), so this loop will be skipped, but we must keep the loop here to support
// the rarer case of deeply nested <ng-template> tags.
// the rarer case of deeply nested <ng-template> tags or inline views.
let hostTNode = view[HOST_NODE];
let viewOffset = 1;
while (hostTNode && hostTNode.injectorIndex === -1) {
view = view[DECLARATION_VIEW] !;
hostTNode = view[HOST_NODE] !;
viewOffset++;
}
return hostTNode ? view[DECLARATION_VIEW] ![hostTNode.injectorIndex] : null;
return hostTNode ?
hostTNode.injectorIndex | (viewOffset << InjectorLocationFlags.ViewOffsetShift) :
-1;
}
/**
* Unwraps a parent injector location number to find the view offset from the current injector,
* then walks up the declaration view tree until the view is found that contains the parent
* injector.
*
* @param location The location of the parent injector, which contains the view offset
* @param startView The LViewData instance from which to start walking up the view tree
* @returns The LViewData instance that contains the parent injector
*/
export function getParentInjectorView(location: number, startView: LViewData): LViewData {
let viewOffset = location >> InjectorLocationFlags.ViewOffsetShift;
let parentView = startView;
// For most cases, the parent injector can be found on the host node (e.g. for component
// or container), but we must keep the loop here to support the rarer case of deeply nested
// <ng-template> tags or inline views, where the parent injector might live many views
// above the child injector.
while (viewOffset > 0) {
parentView = parentView[DECLARATION_VIEW] !;
viewOffset--;
}
return parentView;
}
/**
@ -161,8 +195,9 @@ export function getParentInjector(tNode: TNode, view: LViewData): LInjector {
* @param di The node injector in which a directive will be added
* @param def The definition of the directive to be made public
*/
export function diPublicInInjector(di: LInjector, def: DirectiveDefInternal<any>): void {
bloomAdd(di, def.type);
export function diPublicInInjector(
injectorIndex: number, view: LViewData, def: DirectiveDefInternal<any>): void {
bloomAdd(injectorIndex, view[TVIEW], def.type);
}
/**
@ -171,7 +206,7 @@ export function diPublicInInjector(di: LInjector, def: DirectiveDefInternal<any>
* @param def The definition of the directive to be made public
*/
export function diPublic(def: DirectiveDefInternal<any>): void {
diPublicInInjector(getOrCreateNodeInjector(), def);
diPublicInInjector(getOrCreateNodeInjector(), _getViewData(), def);
}
/**
@ -199,11 +234,11 @@ export function directiveInject<T>(token: Type<T>| InjectionToken<T>): T;
export function directiveInject<T>(token: Type<T>| InjectionToken<T>, flags: InjectFlags): T;
export function directiveInject<T>(
token: Type<T>| InjectionToken<T>, flags = InjectFlags.Default): T|null {
return getOrCreateInjectable<T>(getOrCreateNodeInjector(), token, flags);
return getOrCreateInjectable<T>(getOrCreateNodeInjector(), _getViewData(), token, flags);
}
export function injectRenderer2(): Renderer2 {
return getOrCreateRenderer2(getOrCreateNodeInjector());
return getOrCreateRenderer2(_getViewData());
}
/**
* Inject static attribute value into directive constructor.
@ -254,8 +289,8 @@ export function injectAttribute(attrNameToInject: string): string|undefined {
return undefined;
}
function getOrCreateRenderer2(di: LInjector): Renderer2 {
const renderer = di.view[RENDERER];
function getOrCreateRenderer2(view: LViewData): Renderer2 {
const renderer = view[RENDERER];
if (isProceduralRenderer(renderer)) {
return renderer as Renderer2;
} else {
@ -275,7 +310,7 @@ function getOrCreateRenderer2(di: LInjector): Renderer2 {
* @returns the value from the injector or `null` when not found
*/
export function getOrCreateInjectable<T>(
nodeInjector: LInjector, token: Type<T>| InjectionToken<T>,
startInjectorIndex: number, hostView: LViewData, token: Type<T>| InjectionToken<T>,
flags: InjectFlags = InjectFlags.Default): T|null {
const bloomHash = bloomHashBitOrFactory(token);
// If the ID stored here is a function, this is a special object like ElementRef or TemplateRef
@ -285,60 +320,73 @@ export function getOrCreateInjectable<T>(
// If the token has a bloom hash, then it is a directive that is public to the injection system
// (diPublic) otherwise fall back to the module injector.
if (bloomHash != null) {
let injector: LInjector|null = nodeInjector;
let injectorIndex = startInjectorIndex;
let injectorView = hostView;
while (injector) {
// Get the closest potential matching injector (upwards in the injector tree) that
// *potentially* has the token.
injector = bloomFindPossibleInjector(injector, bloomHash, flags);
if (flags & InjectFlags.SkipSelf) {
const parentLocation = injectorView[injectorIndex + PARENT_INJECTOR];
injectorIndex = parentLocation & InjectorLocationFlags.InjectorIndexMask;
injectorView = getParentInjectorView(parentLocation, injectorView);
}
while (injectorIndex !== -1) {
// Traverse up the injector tree until we find a potential match or until we know there
// *isn't* a match. Outer loop is necessary in case we get a false positive injector.
while (injectorIndex !== -1) {
// Check the current injector. If it matches, stop searching for an injector.
if (injectorHasToken(bloomHash, injectorIndex, injectorView[TVIEW].data)) {
break;
}
if (flags & InjectFlags.Self ||
flags & InjectFlags.Host &&
!sameHostView(injectorView[injectorIndex + PARENT_INJECTOR])) {
injectorIndex = -1;
break;
}
// If the ancestor bloom filter value has the bit corresponding to the directive, traverse
// up to find the specific injector. If the ancestor bloom filter does not have the bit, we
// can abort.
if (injectorHasToken(bloomHash, injectorIndex, injectorView)) {
const parentLocation = injectorView[injectorIndex + PARENT_INJECTOR];
injectorIndex = parentLocation & InjectorLocationFlags.InjectorIndexMask;
injectorView = getParentInjectorView(parentLocation, injectorView);
} else {
injectorIndex = -1;
break;
}
}
// If no injector is found, we *know* that there is no ancestor injector that contains the
// token, so we abort.
if (!injector) {
if (injectorIndex === -1) {
break;
}
// At this point, we have an injector which *may* contain the token, so we step through the
// directives associated with the injector's corresponding node to get the directive instance.
const tNode = injector.tNode;
const injectorView = injector.view;
const nodeFlags = tNode.flags;
const count = nodeFlags & TNodeFlags.DirectiveCountMask;
if (count !== 0) {
const start = nodeFlags >> TNodeFlags.DirectiveStartingIndexShift;
const end = start + count;
const defs = injectorView[TVIEW].directives !;
for (let i = start; i < end; i++) {
// Get the definition for the directive at this index and, if it is injectable (diPublic),
// and matches the given token, return the directive instance.
const directiveDef = defs[i] as DirectiveDefInternal<any>;
if (directiveDef.type === token && directiveDef.diPublic) {
return injectorView[DIRECTIVES] ![i];
}
}
let instance: T|null;
if (instance = searchDirectivesOnInjector<T>(injectorIndex, injectorView, token)) {
return instance;
}
// If we *didn't* find the directive for the token and we are searching the current node's
// injector, it's possible the directive is on this node and hasn't been created yet.
let instance: T|null;
if (injector === nodeInjector &&
if (injectorIndex === startInjectorIndex && hostView === injectorView &&
(instance = searchMatchesQueuedForCreation<T>(token, injectorView[TVIEW]))) {
return instance;
}
// The def wasn't found anywhere on this node, so it was a false positive.
// If flags permit, traverse up the tree and continue searching.
if (flags & InjectFlags.Self || flags & InjectFlags.Host && !sameHostView(injector)) {
injector = null;
} else {
injector = injector.parent;
}
// Traverse up the tree and continue searching.
const parentLocation = injectorView[injectorIndex + PARENT_INJECTOR];
injectorIndex = parentLocation & InjectorLocationFlags.InjectorIndexMask;
injectorView = getParentInjectorView(parentLocation, injectorView);
}
}
const moduleInjector = nodeInjector.view[INJECTOR];
const moduleInjector = hostView[INJECTOR];
const formerInjector = setCurrentInjector(moduleInjector);
try {
return inject(token, flags);
@ -360,6 +408,29 @@ function searchMatchesQueuedForCreation<T>(token: any, hostTView: TView): T|null
return null;
}
function searchDirectivesOnInjector<T>(
injectorIndex: number, injectorView: LViewData, token: Type<T>| InjectionToken<T>) {
const tNode = injectorView[TVIEW].data[injectorIndex + TNODE] as TNode;
const nodeFlags = tNode.flags;
const count = nodeFlags & TNodeFlags.DirectiveCountMask;
if (count !== 0) {
const start = nodeFlags >> TNodeFlags.DirectiveStartingIndexShift;
const end = start + count;
const defs = injectorView[TVIEW].directives !;
for (let i = start; i < end; i++) {
// Get the definition for the directive at this index and, if it is injectable (diPublic),
// and matches the given token, return the directive instance.
const directiveDef = defs[i] as DirectiveDefInternal<any>;
if (directiveDef.type === token && directiveDef.diPublic) {
return injectorView[DIRECTIVES] ![i];
}
}
}
return null;
}
/**
* Returns the bit in an injector's bloom filter that should be used to determine whether or not
* the directive might be provided by the injector.
@ -371,108 +442,67 @@ function searchMatchesQueuedForCreation<T>(token: any, hostTView: TView): T|null
* @param token the injection token
* @returns the matching bit to check in the bloom filter or `null` if the token is not known.
*/
function bloomHashBitOrFactory(token: Type<any>| InjectionToken<any>): number|Function|undefined {
const tokenId: number|undefined = (token as any)[NG_ELEMENT_ID] || null;
export function bloomHashBitOrFactory(token: Type<any>| InjectionToken<any>): number|Function|
undefined {
const tokenId: number|undefined = (token as any)[NG_ELEMENT_ID];
return typeof tokenId === 'number' ? tokenId & BLOOM_MASK : tokenId;
}
/**
* Finds the closest injector that might have a certain directive.
*
* Each directive corresponds to a bit in an injector's bloom filter. Given the bloom bit to
* check and a starting injector, this function traverses up injectors until it finds an
* injector that contains a 1 for that bit in its bloom filter. A 1 indicates that the
* injector may have that directive. It only *may* have the directive because directives begin
* to share bloom filter bits after the BLOOM_SIZE is reached, and it could correspond to a
* different directive sharing the bit.
*
* Note: We can skip checking further injectors up the tree if an injector's cbf structure
* has a 0 for that bloom bit. Since cbf contains the merged value of all the parent
* injectors, a 0 in the bloom bit indicates that the parents definitely do not contain
* the directive and do not need to be checked.
*
* @param injector The starting node injector to check
* @param bloomBit The bit to check in each injector's bloom filter
* @param flags The injection flags for this injection site (e.g. Optional or SkipSelf)
* @returns An injector that might have the directive
*/
export function bloomFindPossibleInjector(
startInjector: LInjector, bloomBit: number, flags: InjectFlags): LInjector|null {
export function injectorHasToken(
bloomHash: number, injectorIndex: number, injectorView: LViewData | TData) {
// Create a mask that targets the specific bit associated with the directive we're looking for.
// JS bit operations are 32 bits, so this will be a number between 2^0 and 2^31, corresponding
// to bit positions 0 - 31 in a 32 bit integer.
const mask = 1 << bloomBit;
const b7 = bloomBit & 0x80;
const b6 = bloomBit & 0x40;
const b5 = bloomBit & 0x20;
const mask = 1 << bloomHash;
const b7 = bloomHash & 0x80;
const b6 = bloomHash & 0x40;
const b5 = bloomHash & 0x20;
// Traverse up the injector tree until we find a potential match or until we know there *isn't* a
// match.
let injector: LInjector|null =
flags & InjectFlags.SkipSelf ? startInjector.parent : startInjector;
// Our bloom filter size is 256 bits, which is eight 32-bit bloom filter buckets:
// bf0 = [0 - 31], bf1 = [32 - 63], bf2 = [64 - 95], bf3 = [96 - 127], etc.
// Get the bloom filter value from the appropriate bucket based on the directive's bloomBit.
let value: number;
while (injector) {
// Our bloom filter size is 256 bits, which is eight 32-bit bloom filter buckets:
// bf0 = [0 - 31], bf1 = [32 - 63], bf2 = [64 - 95], bf3 = [96 - 127], etc.
// Get the bloom filter value from the appropriate bucket based on the directive's bloomBit.
let value: number;
if (b7) {
value = b6 ? (b5 ? injector.bf7 : injector.bf6) : (b5 ? injector.bf5 : injector.bf4);
} else {
value = b6 ? (b5 ? injector.bf3 : injector.bf2) : (b5 ? injector.bf1 : injector.bf0);
}
// If the bloom filter value has the bit corresponding to the directive's bloomBit flipped on,
// this injector is a potential match.
if (value & mask) {
return injector;
}
if (flags & InjectFlags.Self || flags & InjectFlags.Host && !sameHostView(injector)) {
return null;
}
// If the current injector does not have the directive, check the bloom filters for the ancestor
// injectors (cbf0 - cbf7). These filters capture *all* ancestor injectors.
if (b7) {
value = b6 ? (b5 ? injector.cbf7 : injector.cbf6) : (b5 ? injector.cbf5 : injector.cbf4);
} else {
value = b6 ? (b5 ? injector.cbf3 : injector.cbf2) : (b5 ? injector.cbf1 : injector.cbf0);
}
// If the ancestor bloom filter value has the bit corresponding to the directive, traverse up to
// find the specific injector. If the ancestor bloom filter does not have the bit, we can abort.
if (value & mask) {
injector = injector.parent;
} else {
return null;
}
if (b7) {
value = b6 ? (b5 ? injectorView[injectorIndex + 7] : injectorView[injectorIndex + 6]) :
(b5 ? injectorView[injectorIndex + 5] : injectorView[injectorIndex + 4]);
} else {
value = b6 ? (b5 ? injectorView[injectorIndex + 3] : injectorView[injectorIndex + 2]) :
(b5 ? injectorView[injectorIndex + 1] : injectorView[injectorIndex]);
}
return null;
// If the bloom filter value has the bit corresponding to the directive's bloomBit flipped on,
// this injector is a potential match.
return !!(value & mask);
}
/**
* Checks whether the current injector and its parent are in the same host view.
*
* This is necessary to support @Host() decorators. If @Host() is set, we should stop searching once
* the injector and its parent view don't match because it means we'd cross the view boundary.
*/
function sameHostView(injector: LInjector): boolean {
return !!injector.parent && injector.parent.view === injector.view;
function sameHostView(parentLocation: number): boolean {
return !!parentLocation && (parentLocation >> InjectorLocationFlags.ViewOffsetShift) === 0;
}
export class NodeInjector implements Injector {
constructor(private _lInjector: LInjector) {}
private _injectorIndex: number;
constructor(
private _tNode: TElementNode|TContainerNode|TElementContainerNode,
private _hostView: LViewData) {
this._injectorIndex = getOrCreateNodeInjectorForNode(_tNode, _hostView);
}
get(token: any): any {
if (token === Renderer2) {
return getOrCreateRenderer2(this._lInjector);
return getOrCreateRenderer2(this._hostView);
}
setEnvironment(this._lInjector.tNode, this._lInjector.view);
return getOrCreateInjectable(this._lInjector, token);
setEnvironment(this._tNode, this._hostView);
return getOrCreateInjectable(this._injectorIndex, this._hostView, token);
}
}
export function getFactoryOf<T>(type: Type<any>): ((type?: Type<T>) => T)|null {

View File

@ -19,7 +19,6 @@ import {throwCyclicDependencyError, throwErrorIfNoChangesMode, throwMultipleComp
import {executeHooks, executeInitHooks, queueInitHooks, queueLifecycleHooks} from './hooks';
import {ACTIVE_INDEX, LContainer, RENDER_PARENT, VIEWS} from './interfaces/container';
import {ComponentDefInternal, ComponentQuery, ComponentTemplate, DirectiveDefInternal, DirectiveDefListOrFactory, InitialStylingFlags, PipeDefListOrFactory, RenderFlags} from './interfaces/definition';
import {LInjector} from './interfaces/injector';
import {AttributeMarker, InitialInputData, InitialInputs, LContainerNode, LElementContainerNode, LElementNode, LNode, LProjectionNode, LTextNode, LViewNode, LocalRefExtractor, PropertyAliasValue, PropertyAliases, TAttributes, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, TProjectionNode, TViewNode} from './interfaces/node';
import {CssSelectorList, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection';
import {LQueries} from './interfaces/query';
@ -1106,8 +1105,8 @@ export function createTView(
template: templateFn,
viewQuery: viewQuery,
node: null !,
data: HEADER_FILLER.slice(), // Fill in to match HEADER_OFFSET in LViewData
childIndex: -1, // Children set in addToViewTree(), if any
data: blueprint.slice(), // Fill in to match HEADER_OFFSET in LViewData
childIndex: -1, // Children set in addToViewTree(), if any
bindingStartIndex: bindingStartIndex,
hostBindingStartIndex: initialViewLength,
directives: null,

View File

@ -7,71 +7,97 @@
*/
import {ChangeDetectorRef} from '../../change_detection/change_detector_ref';
import {ElementRef} from '../../linker/element_ref';
import {TemplateRef} from '../../linker/template_ref';
import {ViewContainerRef} from '../../linker/view_container_ref';
import {TContainerNode, TElementContainerNode, TElementNode,} from './node';
import {LViewData} from './view';
export interface LInjector {
/**
* We need to store a reference to the injector's parent so DI can keep looking up
* the injector tree until it finds the dependency it's looking for.
*/
readonly parent: LInjector|null;
export const TNODE = 8;
export const PARENT_INJECTOR = 8;
export const INJECTOR_SIZE = 9;
/** Necessary to find directive indices for a particular node and look up the LNode. */
readonly tNode: TElementNode|TElementContainerNode|TContainerNode;
/**
* The view where the node is stored. Necessary because as we traverse up the injector
* tree the view where we search directives may change.
*/
readonly view: LViewData;
/**
* The following bloom filter determines whether a directive is available
* on the associated node or not. This prevents us from searching the directives
* array at this level unless it's probable the directive is in it.
*
* - bf0: Check directive IDs 0-31 (IDs are % 128)
* - bf1: Check directive IDs 32-63
* - bf2: Check directive IDs 64-95
* - bf3: Check directive IDs 96-127
* - bf4: Check directive IDs 128-159
* - bf5: Check directive IDs 160 - 191
* - bf6: Check directive IDs 192 - 223
* - bf7: Check directive IDs 224 - 255
*
* See: https://en.wikipedia.org/wiki/Bloom_filter for more about bloom filters.
*/
bf0: number;
bf1: number;
bf2: number;
bf3: number;
bf4: number;
bf5: number;
bf6: number;
bf7: number;
/**
* cbf0 - cbf7 properties determine whether a directive is available through a
* parent injector. They refer to the merged values of parent bloom filters. This
* allows us to skip looking up the chain unless it's probable that directive exists
* up the chain.
*/
cbf0: number;
cbf1: number;
cbf2: number;
cbf3: number;
cbf4: number;
cbf5: number;
cbf6: number;
cbf7: number;
export const enum InjectorLocationFlags {
InjectorIndexMask = 0b111111111111111,
ViewOffsetShift = 15
}
/**
* Each injector is saved in 9 contiguous slots in `LViewData` and 9 contiguous slots in
* `TView.data`. This allows us to store information about the current node's tokens (which
* can be shared in `TView`) as well as the tokens of its ancestor nodes (which cannot be
* shared, so they live in `LViewData`).
*
* Each of these slots (aside from the last slot) contains a bloom filter. This bloom filter
* determines whether a directive is available on the associated node or not. This prevents us
* from searching the directives array at this level unless it's probable the directive is in it.
*
* See: https://en.wikipedia.org/wiki/Bloom_filter for more about bloom filters.
*
* Because all injectors have been flattened into `LViewData` and `TViewData`, they cannot typed
* using interfaces as they were previously. The start index of each `LInjector` and `TInjector`
* will differ based on where it is flattened into the main array, so it's not possible to know
* the indices ahead of time and save their types here. The interfaces are still included here
* for documentation purposes.
*
* export interface LInjector extends Array<any> {
*
* // Cumulative bloom for directive IDs 0-31 (IDs are % BLOOM_SIZE)
* [0]: number;
*
* // Cumulative bloom for directive IDs 32-63
* [1]: number;
*
* // Cumulative bloom for directive IDs 64-95
* [2]: number;
*
* // Cumulative bloom for directive IDs 96-127
* [3]: number;
*
* // Cumulative bloom for directive IDs 128-159
* [4]: number;
*
* // Cumulative bloom for directive IDs 160 - 191
* [5]: number;
*
* // Cumulative bloom for directive IDs 192 - 223
* [6]: number;
*
* // Cumulative bloom for directive IDs 224 - 255
* [7]: number;
*
* // We need to store a reference to the injector's parent so DI can keep looking up
* // the injector tree until it finds the dependency it's looking for.
* [PARENT_INJECTOR]: number;
* }
*
* export interface TInjector extends Array<any> {
*
* // Shared node bloom for directive IDs 0-31 (IDs are % BLOOM_SIZE)
* [0]: number;
*
* // Shared node bloom for directive IDs 32-63
* [1]: number;
*
* // Shared node bloom for directive IDs 64-95
* [2]: number;
*
* // Shared node bloom for directive IDs 96-127
* [3]: number;
*
* // Shared node bloom for directive IDs 128-159
* [4]: number;
*
* // Shared node bloom for directive IDs 160 - 191
* [5]: number;
*
* // Shared node bloom for directive IDs 192 - 223
* [6]: number;
*
* // Shared node bloom for directive IDs 224 - 255
* [7]: number;
*
* // Necessary to find directive indices for a particular node.
* [TNODE]: TElementNode|TElementContainerNode|TContainerNode;
* }
*/
// Note: This hack is necessary so we don't erroneously get a circular dependency
// failure based on types.
export const unusedValueExportToPlacateAjd = 1;

View File

@ -7,8 +7,6 @@
*/
import {LContainer} from './container';
import {LInjector} from './injector';
import {LQueries} from './query';
import {RComment, RElement, RText} from './renderer';
import {StylingContext} from './styling';
import {LViewData, TView} from './view';

View File

@ -550,11 +550,15 @@ export type HookData = (number | (() => void))[];
* Static data that corresponds to the instance-specific data array on an LView.
*
* Each node's static data is stored in tData at the same index that it's stored
* in the data array. Each pipe's definition is stored here at the same index
* as its pipe instance in the data array. Any nodes that do not have static
* data store a null value in tData to avoid a sparse array.
* in the data array. Any nodes that do not have static data store a null value in
* tData to avoid a sparse array.
*
* Each pipe's definition is stored here at the same index as its pipe instance in
* the data array.
*
* Injector bloom filters are also stored here.
*/
export type TData = (TNode | PipeDefInternal<any>| null)[];
export type TData = (TNode | PipeDefInternal<any>| number | null)[];
/** Type for TView.currentMatches */
export type CurrentMatchesList = [DirectiveDefInternal<any>, (string | number | null)];

View File

@ -14,7 +14,6 @@ import {EventEmitter} from '../event_emitter';
import {ElementRef as ViewEngine_ElementRef} from '../linker/element_ref';
import {QueryList as viewEngine_QueryList} from '../linker/query_list';
import {TemplateRef as ViewEngine_TemplateRef} from '../linker/template_ref';
import {ViewContainerRef as ViewEngine_ViewContainerRef} from '../linker/view_container_ref';
import {Type} from '../type';
import {getSymbolIterator} from '../util';
@ -485,4 +484,4 @@ export function queryRefresh(queryList: QueryList<any>): boolean {
return true;
}
return false;
}
}

View File

@ -16,16 +16,17 @@ import {ViewContainerRef as ViewEngine_ViewContainerRef} from '../linker/view_co
import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, ViewRef as viewEngine_ViewRef} from '../linker/view_ref';
import {assertDefined, assertGreaterThan, assertLessThan} from './assert';
import {NodeInjector, getInjector, getOrCreateNodeInjectorForNode, getParentInjector} from './di';
import {NodeInjector, getParentInjectorLocation, getParentInjectorView} from './di';
import {_getViewData, addToViewTree, createEmbeddedViewAndNode, createLContainer, createLNodeObject, createTNode, getPreviousOrParentTNode, getRenderer, renderEmbeddedTemplate} from './instructions';
import {LContainer, RENDER_PARENT, VIEWS} from './interfaces/container';
import {RenderFlags} from './interfaces/definition';
import {InjectorLocationFlags} from './interfaces/injector';
import {LContainerNode, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, TViewNode} from './interfaces/node';
import {LQueries} from './interfaces/query';
import {RComment, RElement, Renderer3} from './interfaces/renderer';
import {CONTEXT, HOST_NODE, LViewData, QUERIES, RENDERER, TView} from './interfaces/view';
import {CONTEXT, HOST_NODE, LViewData, QUERIES, RENDERER, TVIEW, TView} from './interfaces/view';
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
import {addRemoveViewFromContainer, appendChild, detachView, findComponentView, getBeforeNodeForView, getParentLNode, getRenderParent, insertView, removeView} from './node_manipulation';
import {addRemoveViewFromContainer, appendChild, detachView, findComponentView, getBeforeNodeForView, getRenderParent, insertView, removeView} from './node_manipulation';
import {getLNode, isComponent} from './util';
import {ViewRef} from './view_ref';
@ -180,15 +181,17 @@ export function createContainerRef(
return createElementRef(ElementRefToken, this._hostTNode, this._hostView);
}
get injector(): Injector {
const nodeInjector = getOrCreateNodeInjectorForNode(this._hostTNode, this._hostView);
return new NodeInjector(nodeInjector);
}
get injector(): Injector { return new NodeInjector(this._hostTNode, this._hostView); }
/** @deprecated No replacement */
get parentInjector(): Injector {
const parentLInjector = getParentInjector(this._hostTNode, this._hostView);
return parentLInjector ? new NodeInjector(parentLInjector) : new NullInjector();
const parentLocation = getParentInjectorLocation(this._hostTNode, this._hostView);
const parentView = getParentInjectorView(parentLocation, this._hostView);
const parentIndex = parentLocation & InjectorLocationFlags.InjectorIndexMask;
const parentTNode = parentView[TVIEW].data[parentIndex] as TElementNode | TContainerNode;
return parentLocation === -1 ? new NullInjector() :
new NodeInjector(parentTNode, parentView);
}
clear(): void {

View File

@ -68,9 +68,6 @@
{
"name": "FLAGS"
},
{
"name": "HEADER_FILLER"
},
{
"name": "HEADER_OFFSET"
},
@ -80,6 +77,9 @@
{
"name": "INJECTOR$1"
},
{
"name": "INJECTOR_SIZE"
},
{
"name": "IterableChangeRecord_"
},
@ -140,6 +140,9 @@
{
"name": "PARENT"
},
{
"name": "PARENT_INJECTOR"
},
{
"name": "PublicFeature"
},
@ -182,6 +185,9 @@
{
"name": "TAIL"
},
{
"name": "TNODE"
},
{
"name": "TVIEW"
},
@ -302,9 +308,6 @@
{
"name": "bloomAdd"
},
{
"name": "bloomFindPossibleInjector"
},
{
"name": "bloomHashBitOrFactory"
},
@ -582,7 +585,7 @@
"name": "getInjectableDef"
},
{
"name": "getInjector$1"
"name": "getInjectorIndex"
},
{
"name": "getLElementFromComponent"
@ -621,7 +624,10 @@
"name": "getOrCreateTView"
},
{
"name": "getParentInjector"
"name": "getParentInjectorLocation"
},
{
"name": "getParentInjectorView"
},
{
"name": "getParentLNode"
@ -707,6 +713,9 @@
{
"name": "injectViewContainerRef"
},
{
"name": "injectorHasToken"
},
{
"name": "insertNewMultiProperty"
},
@ -911,6 +920,9 @@
{
"name": "scheduleTick"
},
{
"name": "searchDirectivesOnInjector"
},
{
"name": "searchMatchesQueuedForCreation"
},

View File

@ -38,9 +38,6 @@
{
"name": "FLAGS"
},
{
"name": "HEADER_FILLER"
},
{
"name": "HEADER_OFFSET"
},
@ -50,6 +47,9 @@
{
"name": "INJECTOR$1"
},
{
"name": "INJECTOR_SIZE"
},
{
"name": "MONKEY_PATCH_KEY_NAME"
},
@ -80,6 +80,9 @@
{
"name": "PARENT"
},
{
"name": "PARENT_INJECTOR"
},
{
"name": "PublicFeature"
},
@ -107,9 +110,6 @@
{
"name": "ViewEncapsulation$1"
},
{
"name": "_CLEAN_PROMISE"
},
{
"name": "_getViewData"
},
@ -240,7 +240,7 @@
"name": "getHostElementNode"
},
{
"name": "getInjector$1"
"name": "getInjectorIndex"
},
{
"name": "getLNode"
@ -258,7 +258,10 @@
"name": "getOrCreateTView"
},
{
"name": "getParentInjector"
"name": "getParentInjectorLocation"
},
{
"name": "getParentInjectorView"
},
{
"name": "getParentLNode"

View File

@ -53,9 +53,6 @@
{
"name": "FLAGS"
},
{
"name": "HEADER_FILLER"
},
{
"name": "HEADER_OFFSET"
},
@ -65,6 +62,9 @@
{
"name": "INJECTOR$1"
},
{
"name": "INJECTOR_SIZE"
},
{
"name": "IterableChangeRecord_"
},
@ -131,6 +131,9 @@
{
"name": "PARENT"
},
{
"name": "PARENT_INJECTOR"
},
{
"name": "PublicFeature"
},
@ -170,6 +173,9 @@
{
"name": "TAIL"
},
{
"name": "TNODE"
},
{
"name": "TVIEW"
},
@ -368,9 +374,6 @@
{
"name": "bloomAdd"
},
{
"name": "bloomFindPossibleInjector"
},
{
"name": "bloomHashBitOrFactory"
},
@ -624,7 +627,7 @@
"name": "getInjectableDef"
},
{
"name": "getInjector$1"
"name": "getInjectorIndex"
},
{
"name": "getLElementFromComponent"
@ -657,7 +660,10 @@
"name": "getOrCreateTView"
},
{
"name": "getParentInjector"
"name": "getParentInjectorLocation"
},
{
"name": "getParentInjectorView"
},
{
"name": "getParentLNode"
@ -737,6 +743,9 @@
{
"name": "injectViewContainerRef"
},
{
"name": "injectorHasToken"
},
{
"name": "insertView"
},
@ -935,6 +944,9 @@
{
"name": "scheduleTick"
},
{
"name": "searchDirectivesOnInjector"
},
{
"name": "searchMatchesQueuedForCreation"
},

View File

@ -320,9 +320,6 @@
{
"name": "HAMMER_LOADER"
},
{
"name": "HEADER_FILLER"
},
{
"name": "HEADER_OFFSET"
},
@ -353,6 +350,9 @@
{
"name": "INJECTOR$1"
},
{
"name": "INJECTOR_SIZE"
},
{
"name": "INSPECT_GLOBAL_NAME"
},
@ -623,6 +623,9 @@
{
"name": "PARENT"
},
{
"name": "PARENT_INJECTOR"
},
{
"name": "PATTERN_SEP"
},
@ -815,6 +818,9 @@
{
"name": "THURSDAY"
},
{
"name": "TNODE"
},
{
"name": "TRANSITION_ID"
},
@ -1202,9 +1208,6 @@
{
"name": "bloomAdd"
},
{
"name": "bloomFindPossibleInjector"
},
{
"name": "bloomHashBitOrFactory"
},
@ -1653,10 +1656,10 @@
"name": "getInjectableDef"
},
{
"name": "getInjector$1"
"name": "getInjectorDef"
},
{
"name": "getInjectorDef"
"name": "getInjectorIndex"
},
{
"name": "getLElementFromComponent"
@ -1752,7 +1755,10 @@
"name": "getOriginalError"
},
{
"name": "getParentInjector"
"name": "getParentInjectorLocation"
},
{
"name": "getParentInjectorView"
},
{
"name": "getParentLNode"
@ -1886,6 +1892,9 @@
{
"name": "injectableDefRecord"
},
{
"name": "injectorHasToken"
},
{
"name": "insertView"
},
@ -2282,6 +2291,9 @@
{
"name": "scheduleTick"
},
{
"name": "searchDirectivesOnInjector"
},
{
"name": "searchMatchesQueuedForCreation"
},

View File

@ -10,20 +10,20 @@ import {Attribute, ChangeDetectorRef, ElementRef, Host, InjectFlags, Injector, O
import {RenderFlags} from '@angular/core/src/render3/interfaces/definition';
import {defineComponent} from '../../src/render3/definition';
import {bloomAdd, bloomFindPossibleInjector, getInjector, getOrCreateNodeInjector, injectAttribute} from '../../src/render3/di';
import {PublicFeature, defineDirective, directiveInject, elementProperty, getCurrentView, getRenderedText, injectRenderer2, load, templateRefExtractor} from '../../src/render3/index';
import {bloomAdd, bloomHashBitOrFactory as bloomHash, getOrCreateInjectable, getOrCreateNodeInjector, injectAttribute, injectorHasToken} from '../../src/render3/di';
import {PublicFeature, defineDirective, directiveInject, elementProperty, injectRenderer2, load, templateRefExtractor} from '../../src/render3/index';
import {bind, container, containerRefreshEnd, containerRefreshStart, createNodeAtIndex, createLViewData, createTView, element, elementEnd, elementStart, embeddedViewEnd, embeddedViewStart, enterView, interpolation2, leaveView, projection, projectionDef, reference, template, text, textBinding, loadDirective, elementContainerStart, elementContainerEnd, _getViewData, getTNode} from '../../src/render3/instructions';
import {LInjector} from '../../src/render3/interfaces/injector';
import {isProceduralRenderer} from '../../src/render3/interfaces/renderer';
import {AttributeMarker, LContainerNode, LElementNode, TNodeType} from '../../src/render3/interfaces/node';
import {HEADER_OFFSET, LViewData, LViewFlags, TVIEW, TView} from '../../src/render3/interfaces/view';
import {LViewFlags} from '../../src/render3/interfaces/view';
import {ViewRef} from '../../src/render3/view_ref';
import {getRendererFactory2} from './imported_renderer2';
import {ComponentFixture, createComponent, createDirective, renderComponent, toHtml} from './render_util';
import {NgIf} from './common_with_def';
import {TNODE} from '../../src/render3/interfaces/injector';
describe('di', () => {
describe('no dependencies', () => {
@ -1680,72 +1680,79 @@ describe('di', () => {
describe('inject', () => {
describe('bloom filter', () => {
let di: LInjector;
let mockTView: any;
beforeEach(() => {
di = {} as any;
di.bf0 = 0;
di.bf1 = 0;
di.bf2 = 0;
di.bf3 = 0;
di.bf4 = 0;
di.bf5 = 0;
di.bf6 = 0;
di.bf7 = 0;
di.bf3 = 0;
di.cbf0 = 0;
di.cbf1 = 0;
di.cbf2 = 0;
di.cbf3 = 0;
di.cbf4 = 0;
di.cbf5 = 0;
di.cbf6 = 0;
di.cbf7 = 0;
mockTView = {data: [0, 0, 0, 0, 0, 0, 0, 0, null], firstTemplatePass: true};
});
function bloomState() {
return [di.bf7, di.bf6, di.bf5, di.bf4, di.bf3, di.bf2, di.bf1, di.bf0];
function bloomState() { return mockTView.data.slice(0, TNODE).reverse(); }
class Dir0 {
/** @internal */ static __NG_ELEMENT_ID__ = 0;
}
class Dir1 {
/** @internal */ static __NG_ELEMENT_ID__ = 1;
}
class Dir33 {
/** @internal */ static __NG_ELEMENT_ID__ = 33;
}
class Dir66 {
/** @internal */ static __NG_ELEMENT_ID__ = 66;
}
class Dir99 {
/** @internal */ static __NG_ELEMENT_ID__ = 99;
}
class Dir132 {
/** @internal */ static __NG_ELEMENT_ID__ = 132;
}
class Dir165 {
/** @internal */ static __NG_ELEMENT_ID__ = 165;
}
class Dir198 {
/** @internal */ static __NG_ELEMENT_ID__ = 198;
}
class Dir231 {
/** @internal */ static __NG_ELEMENT_ID__ = 231;
}
it('should add values', () => {
bloomAdd(di, { __NG_ELEMENT_ID__: 0 } as any);
bloomAdd(0, mockTView, Dir0);
expect(bloomState()).toEqual([0, 0, 0, 0, 0, 0, 0, 1]);
bloomAdd(di, { __NG_ELEMENT_ID__: 32 + 1 } as any);
bloomAdd(0, mockTView, Dir33);
expect(bloomState()).toEqual([0, 0, 0, 0, 0, 0, 2, 1]);
bloomAdd(di, { __NG_ELEMENT_ID__: 64 + 2 } as any);
bloomAdd(0, mockTView, Dir66);
expect(bloomState()).toEqual([0, 0, 0, 0, 0, 4, 2, 1]);
bloomAdd(di, { __NG_ELEMENT_ID__: 96 + 3 } as any);
bloomAdd(0, mockTView, Dir99);
expect(bloomState()).toEqual([0, 0, 0, 0, 8, 4, 2, 1]);
bloomAdd(di, { __NG_ELEMENT_ID__: 128 + 4 } as any);
bloomAdd(0, mockTView, Dir132);
expect(bloomState()).toEqual([0, 0, 0, 16, 8, 4, 2, 1]);
bloomAdd(di, { __NG_ELEMENT_ID__: 160 + 5 } as any);
bloomAdd(0, mockTView, Dir165);
expect(bloomState()).toEqual([0, 0, 32, 16, 8, 4, 2, 1]);
bloomAdd(di, { __NG_ELEMENT_ID__: 192 + 6 } as any);
bloomAdd(0, mockTView, Dir198);
expect(bloomState()).toEqual([0, 64, 32, 16, 8, 4, 2, 1]);
bloomAdd(di, { __NG_ELEMENT_ID__: 224 + 7 } as any);
bloomAdd(0, mockTView, Dir231);
expect(bloomState()).toEqual([128, 64, 32, 16, 8, 4, 2, 1]);
});
it('should query values', () => {
bloomAdd(di, { __NG_ELEMENT_ID__: 0 } as any);
bloomAdd(di, { __NG_ELEMENT_ID__: 32 } as any);
bloomAdd(di, { __NG_ELEMENT_ID__: 64 } as any);
bloomAdd(di, { __NG_ELEMENT_ID__: 96 } as any);
bloomAdd(di, { __NG_ELEMENT_ID__: 127 } as any);
bloomAdd(di, { __NG_ELEMENT_ID__: 161 } as any);
bloomAdd(di, { __NG_ELEMENT_ID__: 188 } as any);
bloomAdd(di, { __NG_ELEMENT_ID__: 223 } as any);
bloomAdd(di, { __NG_ELEMENT_ID__: 255 } as any);
bloomAdd(0, mockTView, Dir0);
bloomAdd(0, mockTView, Dir33);
bloomAdd(0, mockTView, Dir66);
bloomAdd(0, mockTView, Dir99);
bloomAdd(0, mockTView, Dir132);
bloomAdd(0, mockTView, Dir165);
bloomAdd(0, mockTView, Dir198);
bloomAdd(0, mockTView, Dir231);
expect(bloomFindPossibleInjector(di, 0, InjectFlags.Default)).toEqual(di);
expect(bloomFindPossibleInjector(di, 1, InjectFlags.Default)).toEqual(null);
expect(bloomFindPossibleInjector(di, 32, InjectFlags.Default)).toEqual(di);
expect(bloomFindPossibleInjector(di, 64, InjectFlags.Default)).toEqual(di);
expect(bloomFindPossibleInjector(di, 96, InjectFlags.Default)).toEqual(di);
expect(bloomFindPossibleInjector(di, 127, InjectFlags.Default)).toEqual(di);
expect(bloomFindPossibleInjector(di, 161, InjectFlags.Default)).toEqual(di);
expect(bloomFindPossibleInjector(di, 188, InjectFlags.Default)).toEqual(di);
expect(bloomFindPossibleInjector(di, 223, InjectFlags.Default)).toEqual(di);
expect(bloomFindPossibleInjector(di, 255, InjectFlags.Default)).toEqual(di);
expect(injectorHasToken(bloomHash(Dir0) as number, 0, mockTView.data)).toEqual(true);
expect(injectorHasToken(bloomHash(Dir1) as number, 0, mockTView.data)).toEqual(false);
expect(injectorHasToken(bloomHash(Dir33) as number, 0, mockTView.data)).toEqual(true);
expect(injectorHasToken(bloomHash(Dir66) as number, 0, mockTView.data)).toEqual(true);
expect(injectorHasToken(bloomHash(Dir99) as number, 0, mockTView.data)).toEqual(true);
expect(injectorHasToken(bloomHash(Dir132) as number, 0, mockTView.data)).toEqual(true);
expect(injectorHasToken(bloomHash(Dir165) as number, 0, mockTView.data)).toEqual(true);
expect(injectorHasToken(bloomHash(Dir198) as number, 0, mockTView.data)).toEqual(true);
expect(injectorHasToken(bloomHash(Dir231) as number, 0, mockTView.data)).toEqual(true);
});
});
@ -1778,9 +1785,11 @@ describe('di', () => {
/**
* <div parentDir>
* % if (...) {
* <span childDir child2Dir #child1="childDir" #child2="child2Dir">
* {{ child1.value }} - {{ child2.value }}
* </span>
* % }
* </div>
*/
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {