docs(core): add more comments to di and fix formatting (#20855)
PR Close #20855
This commit is contained in:
parent
5bc869cb24
commit
1a9064ba2b
|
@ -6,11 +6,9 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* The functions in this file verify that the assumptions we are making
|
||||
* about state in an instruction are correct before implementing any logic.
|
||||
* They are meant only to be called in dev mode as sanity checks.
|
||||
*/
|
||||
// The functions in this file verify that the assumptions we are making
|
||||
// about state in an instruction are correct before implementing any logic.
|
||||
// They are meant only to be called in dev mode as sanity checks.
|
||||
|
||||
/**
|
||||
* Stringifies values such that strings are wrapped in explicit quotation marks and
|
||||
|
@ -19,8 +17,8 @@
|
|||
*
|
||||
* e.g. `expected "3" to be 3` is easier to understand than `expected 3 to be 3`.
|
||||
*
|
||||
* @param {any} value - The value to be stringified
|
||||
* @returns {string} the stringified value
|
||||
* @param value The value to be stringified
|
||||
* @returns The stringified value
|
||||
*/
|
||||
function stringifyValueForError(value: any): string {
|
||||
return typeof value === 'string' ? `"${value}"` : '' + value;
|
||||
|
@ -50,11 +48,11 @@ export function assertNotEqual<T>(actual: T, expected: T, name: string) {
|
|||
/**
|
||||
* Throws an error with a message constructed from the arguments.
|
||||
*
|
||||
* @param {T} actual - The actual value (e.g. 3)
|
||||
* @param {T} expected - The expected value (e.g. 5)
|
||||
* @param {string} name - The name of the value being checked (e.g. attrs.length)
|
||||
* @param {string} operator - The comparison operator (e.g. <, >, ==)
|
||||
* @param {(v: T) => string)} serializer - Function that maps a value to its display value
|
||||
* @param actual The actual value (e.g. 3)
|
||||
* @param expected The expected value (e.g. 5)
|
||||
* @param name The name of the value being checked (e.g. attrs.length)
|
||||
* @param operator The comparison operator (e.g. <, >, ==)
|
||||
* @param serializer Function that maps a value to its display value
|
||||
*/
|
||||
export function assertThrow<T>(
|
||||
actual: T, expected: T, name: string, operator: string,
|
||||
|
|
|
@ -14,20 +14,16 @@ import {LContainer, LNodeFlags, LNodeInjector} from './interfaces';
|
|||
import {ComponentTemplate, DirectiveDef} from './public_interfaces';
|
||||
import {stringify, notImplemented} from './util';
|
||||
|
||||
/**
|
||||
* Injection flags for DI.
|
||||
*
|
||||
* Optional: The dependency is not required.
|
||||
*
|
||||
* CheckSelf: Should check the current node for the dependency.
|
||||
*
|
||||
* CheckParent: Should check parent nodes for the dependency.
|
||||
*/
|
||||
/** Injection flags for DI. */
|
||||
export const enum InjectFlags {
|
||||
/** Dependency is not required. Null will be injected if there is no provider for the dependency. */
|
||||
Optional = 1 << 0,
|
||||
/** When resolving a dependency, include the node that is requesting injection. */
|
||||
CheckSelf = 1 << 1,
|
||||
/** When resolving a dependency, include ancestors of the node requesting injection. */
|
||||
CheckParent = 1 << 2,
|
||||
Default = CheckSelf | CheckParent
|
||||
/** Default injection options: required, checks both self and ancestors. */
|
||||
Default = CheckSelf | CheckParent,
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -53,6 +49,17 @@ function createInjectionError(text: string, token: any) {
|
|||
* If not found, it will propagate up to the next parent injector until the token
|
||||
* is found or the top is reached.
|
||||
*
|
||||
* Usage example (in factory function):
|
||||
*
|
||||
* class SomeDirective {
|
||||
* constructor(directive: DirectiveA) {}
|
||||
*
|
||||
* static ngDirectiveDef = defineDirective({
|
||||
* type: SomeDirective,
|
||||
* factory: () => new SomeDirective(inject(DirectiveA))
|
||||
* });
|
||||
* }
|
||||
*
|
||||
* @param token The directive type to search for
|
||||
* @param flags Injection flags (e.g. CheckParent)
|
||||
* @returns The instance found
|
||||
|
@ -60,6 +67,9 @@ function createInjectionError(text: string, token: any) {
|
|||
export function inject<T>(token: viewEngine.Type<T>, flags?: InjectFlags): T {
|
||||
const di = getOrCreateNodeInjector();
|
||||
const bloomHash = bloomHashBit(token);
|
||||
|
||||
// If the token has a bloom hash, then it is a directive that is public to the injection system
|
||||
// (diPublic). If there is no hash, fall back to the module injector.
|
||||
if (bloomHash === null) {
|
||||
const moduleInjector = di.injector;
|
||||
if (!moduleInjector) {
|
||||
|
@ -68,27 +78,49 @@ export function inject<T>(token: viewEngine.Type<T>, flags?: InjectFlags): T {
|
|||
moduleInjector.get(token);
|
||||
} else {
|
||||
let injector: LNodeInjector|null = di;
|
||||
|
||||
while (injector) {
|
||||
// Get the closest potential matching injector (upwards in the injector tree) that
|
||||
// *potentially* has the token.
|
||||
injector = bloomFindPossibleInjector(injector, bloomHash);
|
||||
if (injector) {
|
||||
const node = injector.node;
|
||||
const flags = node.flags;
|
||||
let size = flags & LNodeFlags.SIZE_MASK;
|
||||
if (size !== 0) {
|
||||
size = size >> LNodeFlags.SIZE_SHIFT;
|
||||
const start = flags >> LNodeFlags.INDX_SHIFT;
|
||||
const ngStaticData = node.view.ngStaticData;
|
||||
for (let i = start, ii = start + size; i < ii; i++) {
|
||||
const def = ngStaticData[i] as DirectiveDef<any>;
|
||||
if (def.diPublic && def.type == token) {
|
||||
return node.view.data[i];
|
||||
}
|
||||
|
||||
// If no injector is found, we *know* that there is no ancestor injector that contains the
|
||||
// token, so we abort.
|
||||
if (!injector) { 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 node = injector.node;
|
||||
|
||||
// The size of the node's directive's list is stored in certain bits of the node's flags,
|
||||
// so exact it with a mask and shift it back such that the bits reflect the real value.
|
||||
const flags = node.flags;
|
||||
const size = (flags & LNodeFlags.SIZE_MASK) >> LNodeFlags.SIZE_SHIFT;
|
||||
|
||||
if (size !== 0) {
|
||||
// The start index of the directives list is also part of the node's flags, but there is
|
||||
// nothing to the "left" of it so it doesn't need a mask.
|
||||
const start = flags >> LNodeFlags.INDX_SHIFT;
|
||||
|
||||
const ngStaticData = node.view.ngStaticData;
|
||||
for (let i = start, ii = start + size; i < ii; 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 = ngStaticData[i] as DirectiveDef<any>;
|
||||
if (directiveDef.diPublic && directiveDef.type == token) {
|
||||
return node.view.data[i];
|
||||
}
|
||||
}
|
||||
injector = injector.parent;
|
||||
}
|
||||
|
||||
// If we *didn't* find the directive for the token from the candidate injector, we had a false
|
||||
// positive. Traverse up the tree and continue.
|
||||
injector = injector.parent;
|
||||
}
|
||||
}
|
||||
|
||||
// No directive was found for the given token.
|
||||
// TODO: implement optional, check-self, and check-parent.
|
||||
throw createInjectionError('Not found', token);
|
||||
}
|
||||
|
||||
|
@ -113,7 +145,7 @@ function bloomHashBit(type: viewEngine.Type<any>): number|null {
|
|||
* 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 propagates up injectors until it finds an
|
||||
* 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
|
||||
|
@ -128,22 +160,36 @@ function bloomHashBit(type: viewEngine.Type<any>): number|null {
|
|||
* @param bloomBit The bit to check in each injector's bloom filter
|
||||
* @returns An injector that might have the directive
|
||||
*/
|
||||
export function bloomFindPossibleInjector(injector: LNodeInjector, bloomBit: number): LNodeInjector|
|
||||
null {
|
||||
export function bloomFindPossibleInjector(startInjector: LNodeInjector, bloomBit: number): LNodeInjector|
|
||||
null {
|
||||
// Create a mask that targets the specific bit associated with the directive we're looking for.
|
||||
// This will be a number between 0 and 31, corresponding to a bit position in a 32 bit integer.
|
||||
const mask = 1 << bloomBit;
|
||||
let di: LNodeInjector|null = injector;
|
||||
while (di) {
|
||||
// See if the current injector may have the value.
|
||||
|
||||
// Traverse up the injector tree until we find a potential match or until we know there *isn't* a
|
||||
// match.
|
||||
let injector: LNodeInjector|null = startInjector;
|
||||
while (injector) {
|
||||
// Our bloom filter size is 128 bits, which is four 32-bit bloom filter buckets:
|
||||
// bf0 = [0 - 31], bf1 = [32 - 63], bf2 = [64 - 95], bf3 = [96 - 127]
|
||||
// Get the bloom filter value from the appropriate bucket based on the directive's bloomBit.
|
||||
let value: number =
|
||||
bloomBit < 64 ? (bloomBit < 32 ? di.bf0 : di.bf1) : (bloomBit < 96 ? di.bf2 : di.bf3);
|
||||
bloomBit < 64 ? (bloomBit < 32 ? injector.bf0 : injector.bf1) : (bloomBit < 96 ? injector.bf2 : injector.bf3);
|
||||
|
||||
// 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) === mask) {
|
||||
return di;
|
||||
return injector;
|
||||
}
|
||||
// See if the parent injectors may have the value
|
||||
|
||||
// If the current injector does not have the directive, check the bloom filters for the ancestor
|
||||
// injectors (cbf0 - cbf3). These filters capture *all* ancestor injectors.
|
||||
value =
|
||||
bloomBit < 64 ? (bloomBit < 32 ? di.cbf0 : di.cbf1) : (bloomBit < 96 ? di.cbf2 : di.cbf3);
|
||||
// Only go to parent if parent may have value otherwise exit.
|
||||
di = (value & mask) ? di.parent : null;
|
||||
bloomBit < 64 ? (bloomBit < 32 ? injector.cbf0 : injector.cbf1) : (bloomBit < 96 ? injector.cbf2 : injector.cbf3);
|
||||
|
||||
// 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.
|
||||
injector = (value & mask) ? injector.parent : null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -17,8 +17,8 @@ import {RComment, RElement, RNode, RText, Renderer3Fn} from './renderer';
|
|||
* This is necessary to add or remove elements from the DOM when a view
|
||||
* is added or removed from the container. e.g. parent.removeChild(...)
|
||||
*
|
||||
* @param {LContainer} containerNode The container node whose parent must be found
|
||||
* @returns {RNode}
|
||||
* @param containerNode The container node whose parent must be found
|
||||
* @returns Closest DOM node above the container
|
||||
*/
|
||||
export function findNativeParent(containerNode: LContainer): RNode|null {
|
||||
let container: LContainer|null = containerNode;
|
||||
|
@ -49,10 +49,10 @@ export function findNativeParent(containerNode: LContainer): RNode|null {
|
|||
* the next view's first child element. Otherwise, the container's comment
|
||||
* anchor is the marker.
|
||||
*
|
||||
* @param {number} index The index of the view to check
|
||||
* @param {ContainerState} state ContainerState of the parent container
|
||||
* @param {RComment} native Comment anchor for container
|
||||
* @returns {RElement | RText | RComment}
|
||||
* @param index The index of the view to check
|
||||
* @param state ContainerState of the parent container
|
||||
* @param native Comment anchor for container
|
||||
* @returns The DOM element for which the view should insert elements
|
||||
*/
|
||||
export function findBeforeNode(index: number, state: ContainerState, native: RComment): RElement|
|
||||
RText|RComment {
|
||||
|
@ -70,10 +70,10 @@ export function findBeforeNode(index: number, state: ContainerState, native: RCo
|
|||
* to propagate deeply into the nested containers to remove all elements in the
|
||||
* views beneath it.
|
||||
*
|
||||
* @param {LContainer} container - The container to which the root view belongs
|
||||
* @param {LView} rootNode - The view from which elements should be added or removed
|
||||
* @param {boolean} insertMode - Whether or not elements should be added (if false, removing)
|
||||
* @param {RNode} beforeNode - The node before which elements should be added, if insert mode
|
||||
* @param container The container to which the root view belongs
|
||||
* @param rootNode The view from which elements should be added or removed
|
||||
* @param insertMode Whether or not elements should be added (if false, removing)
|
||||
* @param beforeNode The node before which elements should be added, if insert mode
|
||||
*/
|
||||
export function addRemoveViewFromContainer(
|
||||
container: LContainer, rootNode: LView, insertMode: true, beforeNode: RNode | null): void;
|
||||
|
@ -142,7 +142,7 @@ export function addRemoveViewFromContainer(
|
|||
* - Using a while loop because it's faster than recursion
|
||||
* - Destroy only called on movement to sibling or movement to parent (laterally or up)
|
||||
*
|
||||
* @param {ViewState} rootView - The view to destroy
|
||||
* @param rootView The view to destroy
|
||||
*/
|
||||
export function destroyViewTree(rootView: ViewState): void {
|
||||
let viewOrContainerState: ViewOrContainerState|null = rootView;
|
||||
|
@ -180,10 +180,10 @@ export function destroyViewTree(rootView: ViewState): void {
|
|||
* root node of another view (in that case, the view's elements will be added when
|
||||
* the container's parent view is added later).
|
||||
*
|
||||
* @param {LContainer} container - The container into which the view should be inserted
|
||||
* @param {LView} newView - The view to insert
|
||||
* @param {number} index - The index at which to insert the view
|
||||
* @returns {LView} - The inserted view
|
||||
* @param container The container into which the view should be inserted
|
||||
* @param newView The view to insert
|
||||
* @param index The index at which to insert the view
|
||||
* @returns The inserted view
|
||||
*/
|
||||
export function insertView(container: LContainer, newView: LView, index: number): LView {
|
||||
const state = container.data;
|
||||
|
@ -226,9 +226,9 @@ export function insertView(container: LContainer, newView: LView, index: number)
|
|||
* removes the view's elements from the DOM and conducts cleanup (e.g. removing
|
||||
* listeners, calling onDestroys).
|
||||
*
|
||||
* @param {LContainer} container - The container from which to remove a view
|
||||
* @param {number} removeIndex - The index of the view to remove
|
||||
* @returns {LView} - The removed view
|
||||
* @param container The container from which to remove a view
|
||||
* @param removeIndex The index of the view to remove
|
||||
* @returns The removed view
|
||||
*/
|
||||
export function removeView(container: LContainer, removeIndex: number): LView {
|
||||
const children = container.data.children;
|
||||
|
@ -249,8 +249,8 @@ export function removeView(container: LContainer, removeIndex: number): LView {
|
|||
* one view to the next to add/remove elements. Also adds the ViewState (view.data)
|
||||
* to the view tree for easy traversal when cleaning up the view.
|
||||
*
|
||||
* @param {LView} view - The view to set up
|
||||
* @param {LView} next - The view's new next
|
||||
* @param view The view to set up
|
||||
* @param next The view's new next
|
||||
*/
|
||||
export function setViewNext(view: LView, next: LView | null): void {
|
||||
view.next = next;
|
||||
|
@ -265,9 +265,9 @@ export function setViewNext(view: LView, next: LView | null): void {
|
|||
* embedded views, the container (which is the view node's parent, but not the
|
||||
* ViewState's parent) needs to be checked for a possible next property.
|
||||
*
|
||||
* @param {ViewOrContainerState} state - The ViewOrContainerState for which we need a parent state
|
||||
* @param {ViewState} rootView - The rootView, so we don't propagate too far up the view tree
|
||||
* @returns {ViewOrContainerState}
|
||||
* @param state The ViewOrContainerState for which we need a parent state
|
||||
* @param rootView The rootView, so we don't propagate too far up the view tree
|
||||
* @returns The correct parent ViewOrContainerState
|
||||
*/
|
||||
export function getParentState(
|
||||
state: ViewOrContainerState, rootView: ViewState): ViewOrContainerState|null {
|
||||
|
@ -286,7 +286,7 @@ export function getParentState(
|
|||
/**
|
||||
* Removes all listeners and call all onDestroys in a given view.
|
||||
*
|
||||
* @param {ViewState} viewState - The ViewState of the view to clean up
|
||||
* @param viewState The ViewState of the view to clean up
|
||||
*/
|
||||
function cleanUpView(viewState: ViewState): void {
|
||||
if (!viewState.cleanup) return;
|
||||
|
@ -310,10 +310,10 @@ function cleanUpView(viewState: ViewState): void {
|
|||
* of a parent component, the child will be appended to the right position later by
|
||||
* the content projection system. Otherwise, append normally.
|
||||
*
|
||||
* @param {LNode} parent - The parent to which to append the child
|
||||
* @param {RNode} child - The child that should be appended
|
||||
* @param {ViewState} currentView - The current view's ViewState
|
||||
* @returns {boolean} - Whether or not the child was appended
|
||||
* @param parent The parent to which to append the child
|
||||
* @param child The child that should be appended
|
||||
* @param currentView The current view's ViewState
|
||||
* @returns Whether or not the child was appended
|
||||
*/
|
||||
export function appendChild(parent: LNode, child: RNode | null, currentView: ViewState): boolean {
|
||||
// Only add native child element to parent element if the parent element is regular Element.
|
||||
|
@ -347,8 +347,8 @@ export function appendChild(parent: LNode, child: RNode | null, currentView: Vie
|
|||
* of a parent component, the child will be inserted to the right position later by
|
||||
* the content projection system. Otherwise, insertBefore normally.
|
||||
*
|
||||
* @param {LNode} node - Node to insert
|
||||
* @param {ViewState} currentView - The current view's ViewState
|
||||
* @param node Node to insert
|
||||
* @param currentView The current view's ViewState
|
||||
*/
|
||||
export function insertChild(node: LNode, currentView: ViewState): void {
|
||||
const parent = node.parent !;
|
||||
|
@ -383,10 +383,10 @@ export function insertChild(node: LNode, currentView: ViewState): void {
|
|||
* appends the nodes from all of the container's active views to the DOM. Also stores the
|
||||
* node in the given projectedNodes array.
|
||||
*
|
||||
* @param {ProjectionState} projectedNodes - Array to store the projected node
|
||||
* @param {LElement | LText | LContainer} node - The node to process
|
||||
* @param {LView | LElement} currentParent - The last parent element to be processed
|
||||
* @param {ViewState} currentView - The current view's ViewState
|
||||
* @param projectedNodes Array to store the projected node
|
||||
* @param node The node to process
|
||||
* @param currentParent The last parent element to be processed
|
||||
* @param currentView The current view's ViewState
|
||||
*/
|
||||
export function processProjectedNode(
|
||||
projectedNodes: ProjectionState, node: LElement | LText | LContainer,
|
||||
|
|
Loading…
Reference in New Issue