refactor(core): replace loadLContext with getLContext calls (#41606)
This commit refactors the code to replace `loadLContext` with `getLContext` calls. The only difference between these two functions is that the `loadLContext` supports throwing an error in case `LContext` can not be found. The investigation performed in #41525 revealed that throwing while retrieving `LContext` might have undesirable performance implications, so we should avoid that to make sure there are no accidental perf regressions in other parts of code that used `loadLContext`. Moreover, in most of the places the `loadLContext` was already called in a mode that prevented an error from being thrown, so this refactoring should have no effect on the actual behavior. PR Close #41606
This commit is contained in:
parent
64567d3d39
commit
3ceb3dea67
|
@ -8,11 +8,12 @@
|
||||||
|
|
||||||
import {Injector} from '../di/injector';
|
import {Injector} from '../di/injector';
|
||||||
import {assertTNodeForLView} from '../render3/assert';
|
import {assertTNodeForLView} from '../render3/assert';
|
||||||
|
import {getLContext} from '../render3/context_discovery';
|
||||||
import {CONTAINER_HEADER_OFFSET, LContainer, NATIVE} from '../render3/interfaces/container';
|
import {CONTAINER_HEADER_OFFSET, LContainer, NATIVE} from '../render3/interfaces/container';
|
||||||
import {TElementNode, TNode, TNodeFlags, TNodeType} from '../render3/interfaces/node';
|
import {TElementNode, TNode, TNodeFlags, TNodeType} from '../render3/interfaces/node';
|
||||||
import {isComponentHost, isLContainer} from '../render3/interfaces/type_checks';
|
import {isComponentHost, isLContainer} from '../render3/interfaces/type_checks';
|
||||||
import {DECLARATION_COMPONENT_VIEW, LView, PARENT, T_HOST, TData, TVIEW} from '../render3/interfaces/view';
|
import {DECLARATION_COMPONENT_VIEW, LView, PARENT, T_HOST, TData, TVIEW} from '../render3/interfaces/view';
|
||||||
import {getComponent, getContext, getInjectionTokens, getInjector, getListeners, getLocalRefs, getOwningComponent, loadLContext} from '../render3/util/discovery_utils';
|
import {getComponent, getContext, getInjectionTokens, getInjector, getListeners, getLocalRefs, getOwningComponent} from '../render3/util/discovery_utils';
|
||||||
import {INTERPOLATION_DELIMITER} from '../render3/util/misc_utils';
|
import {INTERPOLATION_DELIMITER} from '../render3/util/misc_utils';
|
||||||
import {renderStringify} from '../render3/util/stringify_utils';
|
import {renderStringify} from '../render3/util/stringify_utils';
|
||||||
import {getComponentLViewByIndex, getNativeByTNodeOrNull} from '../render3/util/view_utils';
|
import {getComponentLViewByIndex, getNativeByTNodeOrNull} from '../render3/util/view_utils';
|
||||||
|
@ -261,13 +262,13 @@ class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugEleme
|
||||||
}
|
}
|
||||||
|
|
||||||
get name(): string {
|
get name(): string {
|
||||||
try {
|
const context = getLContext(this.nativeNode);
|
||||||
const context = loadLContext(this.nativeNode)!;
|
if (context !== null) {
|
||||||
const lView = context.lView;
|
const lView = context.lView;
|
||||||
const tData = lView[TVIEW].data;
|
const tData = lView[TVIEW].data;
|
||||||
const tNode = tData[context.nodeIndex] as TNode;
|
const tNode = tData[context.nodeIndex] as TNode;
|
||||||
return tNode.value!;
|
return tNode.value!;
|
||||||
} catch (e) {
|
} else {
|
||||||
return this.nativeNode.nodeName;
|
return this.nativeNode.nodeName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -285,8 +286,8 @@ class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugEleme
|
||||||
* - attribute bindings (e.g. `[attr.role]="menu"`)
|
* - attribute bindings (e.g. `[attr.role]="menu"`)
|
||||||
*/
|
*/
|
||||||
get properties(): {[key: string]: any;} {
|
get properties(): {[key: string]: any;} {
|
||||||
const context = loadLContext(this.nativeNode, false);
|
const context = getLContext(this.nativeNode);
|
||||||
if (context == null) {
|
if (context === null) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,8 +312,8 @@ class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugEleme
|
||||||
return attributes;
|
return attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
const context = loadLContext(element, false);
|
const context = getLContext(element);
|
||||||
if (context == null) {
|
if (context === null) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -501,7 +502,7 @@ function _queryAllR3(
|
||||||
function _queryAllR3(
|
function _queryAllR3(
|
||||||
parentElement: DebugElement, predicate: Predicate<DebugElement>|Predicate<DebugNode>,
|
parentElement: DebugElement, predicate: Predicate<DebugElement>|Predicate<DebugNode>,
|
||||||
matches: DebugElement[]|DebugNode[], elementsOnly: boolean) {
|
matches: DebugElement[]|DebugNode[], elementsOnly: boolean) {
|
||||||
const context = loadLContext(parentElement.nativeNode, false);
|
const context = getLContext(parentElement.nativeNode);
|
||||||
if (context !== null) {
|
if (context !== null) {
|
||||||
const parentTNode = context.lView[TVIEW].data[context.nodeIndex] as TNode;
|
const parentTNode = context.lView[TVIEW].data[context.nodeIndex] as TNode;
|
||||||
_queryNodeChildrenR3(
|
_queryNodeChildrenR3(
|
||||||
|
|
|
@ -53,7 +53,7 @@ import {getTNode, unwrapRNode} from './view_utils';
|
||||||
*/
|
*/
|
||||||
export function getComponent<T>(element: Element): T|null {
|
export function getComponent<T>(element: Element): T|null {
|
||||||
assertDomElement(element);
|
assertDomElement(element);
|
||||||
const context = loadLContext(element, false);
|
const context = getLContext(element);
|
||||||
if (context === null) return null;
|
if (context === null) return null;
|
||||||
|
|
||||||
if (context.component === undefined) {
|
if (context.component === undefined) {
|
||||||
|
@ -78,7 +78,7 @@ export function getComponent<T>(element: Element): T|null {
|
||||||
*/
|
*/
|
||||||
export function getContext<T>(element: Element): T|null {
|
export function getContext<T>(element: Element): T|null {
|
||||||
assertDomElement(element);
|
assertDomElement(element);
|
||||||
const context = loadLContext(element, false);
|
const context = getLContext(element);
|
||||||
return context === null ? null : context.lView[CONTEXT] as T;
|
return context === null ? null : context.lView[CONTEXT] as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ export function getContext<T>(element: Element): T|null {
|
||||||
* @globalApi ng
|
* @globalApi ng
|
||||||
*/
|
*/
|
||||||
export function getOwningComponent<T>(elementOrDir: Element|{}): T|null {
|
export function getOwningComponent<T>(elementOrDir: Element|{}): T|null {
|
||||||
const context = loadLContext(elementOrDir, false);
|
const context = getLContext(elementOrDir);
|
||||||
if (context === null) return null;
|
if (context === null) return null;
|
||||||
|
|
||||||
let lView = context.lView;
|
let lView = context.lView;
|
||||||
|
@ -136,7 +136,7 @@ export function getRootComponents(elementOrDir: Element|{}): {}[] {
|
||||||
* @globalApi ng
|
* @globalApi ng
|
||||||
*/
|
*/
|
||||||
export function getInjector(elementOrDir: Element|{}): Injector {
|
export function getInjector(elementOrDir: Element|{}): Injector {
|
||||||
const context = loadLContext(elementOrDir, false);
|
const context = getLContext(elementOrDir);
|
||||||
if (context === null) return Injector.NULL;
|
if (context === null) return Injector.NULL;
|
||||||
|
|
||||||
const tNode = context.lView[TVIEW].data[context.nodeIndex] as TElementNode;
|
const tNode = context.lView[TVIEW].data[context.nodeIndex] as TElementNode;
|
||||||
|
@ -149,7 +149,7 @@ export function getInjector(elementOrDir: Element|{}): Injector {
|
||||||
* @param element Element for which the injection tokens should be retrieved.
|
* @param element Element for which the injection tokens should be retrieved.
|
||||||
*/
|
*/
|
||||||
export function getInjectionTokens(element: Element): any[] {
|
export function getInjectionTokens(element: Element): any[] {
|
||||||
const context = loadLContext(element, false);
|
const context = getLContext(element);
|
||||||
if (context === null) return [];
|
if (context === null) return [];
|
||||||
const lView = context.lView;
|
const lView = context.lView;
|
||||||
const tView = lView[TVIEW];
|
const tView = lView[TVIEW];
|
||||||
|
@ -200,7 +200,7 @@ export function getDirectives(node: Node): {}[] {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const context = loadLContext(node, false);
|
const context = getLContext(node);
|
||||||
if (context === null) {
|
if (context === null) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
@ -284,22 +284,6 @@ export function getDirectiveMetadata(directiveOrComponentInstance: any): Compone
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns LContext associated with a target passed as an argument.
|
|
||||||
* Throws if a given target doesn't have associated LContext.
|
|
||||||
*/
|
|
||||||
export function loadLContext(target: {}): LContext;
|
|
||||||
export function loadLContext(target: {}, throwOnNotFound: false): LContext|null;
|
|
||||||
export function loadLContext(target: {}, throwOnNotFound: boolean = true): LContext|null {
|
|
||||||
const context = getLContext(target);
|
|
||||||
if (!context && throwOnNotFound) {
|
|
||||||
throw new Error(
|
|
||||||
ngDevMode ? `Unable to find context associated with ${stringifyForError(target)}` :
|
|
||||||
'Invalid ng target');
|
|
||||||
}
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve map of local references.
|
* Retrieve map of local references.
|
||||||
*
|
*
|
||||||
|
@ -309,7 +293,7 @@ export function loadLContext(target: {}, throwOnNotFound: boolean = true): LCont
|
||||||
* the local references.
|
* the local references.
|
||||||
*/
|
*/
|
||||||
export function getLocalRefs(target: {}): {[key: string]: any} {
|
export function getLocalRefs(target: {}): {[key: string]: any} {
|
||||||
const context = loadLContext(target, false);
|
const context = getLContext(target);
|
||||||
if (context === null) return {};
|
if (context === null) return {};
|
||||||
|
|
||||||
if (context.localRefs === undefined) {
|
if (context.localRefs === undefined) {
|
||||||
|
@ -349,11 +333,6 @@ export function getRenderedText(component: any): string {
|
||||||
return hostElement.textContent || '';
|
return hostElement.textContent || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loadLContextFromNode(node: Node): LContext {
|
|
||||||
if (!(node instanceof Node)) throw new Error('Expecting instance of DOM Element');
|
|
||||||
return loadLContext(node)!;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event listener configuration returned from `getListeners`.
|
* Event listener configuration returned from `getListeners`.
|
||||||
* @publicApi
|
* @publicApi
|
||||||
|
@ -405,7 +384,7 @@ export interface Listener {
|
||||||
*/
|
*/
|
||||||
export function getListeners(element: Element): Listener[] {
|
export function getListeners(element: Element): Listener[] {
|
||||||
assertDomElement(element);
|
assertDomElement(element);
|
||||||
const lContext = loadLContext(element, false);
|
const lContext = getLContext(element);
|
||||||
if (lContext === null) return [];
|
if (lContext === null) return [];
|
||||||
|
|
||||||
const lView = lContext.lView;
|
const lView = lContext.lView;
|
||||||
|
@ -458,9 +437,15 @@ function isDirectiveDefHack(obj: any): obj is DirectiveDef<any> {
|
||||||
* @param element DOM element which is owned by an existing component's view.
|
* @param element DOM element which is owned by an existing component's view.
|
||||||
*/
|
*/
|
||||||
export function getDebugNode(element: Element): DebugNode|null {
|
export function getDebugNode(element: Element): DebugNode|null {
|
||||||
let debugNode: DebugNode|null = null;
|
if (ngDevMode && !(element instanceof Node)) {
|
||||||
|
throw new Error('Expecting instance of DOM Element');
|
||||||
|
}
|
||||||
|
|
||||||
|
const lContext = getLContext(element);
|
||||||
|
if (lContext === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const lContext = loadLContextFromNode(element);
|
|
||||||
const lView = lContext.lView;
|
const lView = lContext.lView;
|
||||||
const nodeIndex = lContext.nodeIndex;
|
const nodeIndex = lContext.nodeIndex;
|
||||||
if (nodeIndex !== -1) {
|
if (nodeIndex !== -1) {
|
||||||
|
@ -471,10 +456,10 @@ export function getDebugNode(element: Element): DebugNode|null {
|
||||||
isLView(valueInLView) ? (valueInLView[T_HOST] as TNode) : getTNode(lView[TVIEW], nodeIndex);
|
isLView(valueInLView) ? (valueInLView[T_HOST] as TNode) : getTNode(lView[TVIEW], nodeIndex);
|
||||||
ngDevMode &&
|
ngDevMode &&
|
||||||
assertEqual(tNode.index, nodeIndex, 'Expecting that TNode at index is same as index');
|
assertEqual(tNode.index, nodeIndex, 'Expecting that TNode at index is same as index');
|
||||||
debugNode = buildDebugNode(tNode, lView);
|
return buildDebugNode(tNode, lView);
|
||||||
}
|
}
|
||||||
|
|
||||||
return debugNode;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -486,7 +471,7 @@ export function getDebugNode(element: Element): DebugNode|null {
|
||||||
* @param target DOM element or component instance for which to retrieve the LView.
|
* @param target DOM element or component instance for which to retrieve the LView.
|
||||||
*/
|
*/
|
||||||
export function getComponentLView(target: any): LView {
|
export function getComponentLView(target: any): LView {
|
||||||
const lContext = loadLContext(target);
|
const lContext = getLContext(target)!;
|
||||||
const nodeIndx = lContext.nodeIndex;
|
const nodeIndx = lContext.nodeIndex;
|
||||||
const lView = lContext.lView;
|
const lView = lContext.lView;
|
||||||
const componentLView = lView[nodeIndx];
|
const componentLView = lView[nodeIndx];
|
||||||
|
|
|
@ -17,8 +17,9 @@ import {getElementStyles} from '@angular/core/testing/src/styling';
|
||||||
import {expect} from '@angular/core/testing/src/testing_internal';
|
import {expect} from '@angular/core/testing/src/testing_internal';
|
||||||
import {onlyInIvy} from '@angular/private/testing';
|
import {onlyInIvy} from '@angular/private/testing';
|
||||||
|
|
||||||
|
import {getLContext} from '../../src/render3/context_discovery';
|
||||||
import {getHostElement, markDirty} from '../../src/render3/index';
|
import {getHostElement, markDirty} from '../../src/render3/index';
|
||||||
import {ComponentDebugMetadata, getComponent, getComponentLView, getContext, getDebugNode, getDirectiveMetadata, getDirectives, getInjectionTokens, getInjector, getListeners, getLocalRefs, getOwningComponent, getRootComponents, loadLContext} from '../../src/render3/util/discovery_utils';
|
import {ComponentDebugMetadata, getComponent, getComponentLView, getContext, getDebugNode, getDirectiveMetadata, getDirectives, getInjectionTokens, getInjector, getListeners, getLocalRefs, getOwningComponent, getRootComponents} from '../../src/render3/util/discovery_utils';
|
||||||
|
|
||||||
onlyInIvy('Ivy-specific utilities').describe('discovery utils', () => {
|
onlyInIvy('Ivy-specific utilities').describe('discovery utils', () => {
|
||||||
let fixture: ComponentFixture<MyApp>;
|
let fixture: ComponentFixture<MyApp>;
|
||||||
|
@ -270,9 +271,9 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('loadLContext', () => {
|
describe('getLContext', () => {
|
||||||
it('should work on components', () => {
|
it('should work on components', () => {
|
||||||
const lContext = loadLContext(child[0]);
|
const lContext = getLContext(child[0])!;
|
||||||
expect(lContext).toBeDefined();
|
expect(lContext).toBeDefined();
|
||||||
expect(lContext.native as any).toBe(child[0]);
|
expect(lContext.native as any).toBe(child[0]);
|
||||||
});
|
});
|
||||||
|
@ -280,7 +281,7 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils', () => {
|
||||||
it('should work on templates', () => {
|
it('should work on templates', () => {
|
||||||
const templateComment = Array.from((fixture.nativeElement as HTMLElement).childNodes)
|
const templateComment = Array.from((fixture.nativeElement as HTMLElement).childNodes)
|
||||||
.find((node: ChildNode) => node.nodeType === Node.COMMENT_NODE)!;
|
.find((node: ChildNode) => node.nodeType === Node.COMMENT_NODE)!;
|
||||||
const lContext = loadLContext(templateComment);
|
const lContext = getLContext(templateComment)!;
|
||||||
expect(lContext).toBeDefined();
|
expect(lContext).toBeDefined();
|
||||||
expect(lContext.native as any).toBe(templateComment);
|
expect(lContext.native as any).toBe(templateComment);
|
||||||
});
|
});
|
||||||
|
@ -290,7 +291,7 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils', () => {
|
||||||
.find(
|
.find(
|
||||||
(node: ChildNode) => node.nodeType === Node.COMMENT_NODE &&
|
(node: ChildNode) => node.nodeType === Node.COMMENT_NODE &&
|
||||||
node.textContent === `ng-container`)!;
|
node.textContent === `ng-container`)!;
|
||||||
const lContext = loadLContext(ngContainerComment);
|
const lContext = getLContext(ngContainerComment)!;
|
||||||
expect(lContext).toBeDefined();
|
expect(lContext).toBeDefined();
|
||||||
expect(lContext.native as any).toBe(ngContainerComment);
|
expect(lContext.native as any).toBe(ngContainerComment);
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
|
|
||||||
import {CommonModule} from '@angular/common';
|
import {CommonModule} from '@angular/common';
|
||||||
import {Component} from '@angular/core';
|
import {Component} from '@angular/core';
|
||||||
import {LView} from '@angular/core/src/render3/interfaces/view';
|
import {getLContext} from '@angular/core/src/render3/context_discovery';
|
||||||
import {getComponentLView, loadLContext} from '@angular/core/src/render3/util/discovery_utils';
|
import {getComponentLView} from '@angular/core/src/render3/util/discovery_utils';
|
||||||
import {createNamedArrayType} from '@angular/core/src/util/named_array_type';
|
import {createNamedArrayType} from '@angular/core/src/util/named_array_type';
|
||||||
import {TestBed} from '@angular/core/testing';
|
import {TestBed} from '@angular/core/testing';
|
||||||
import {onlyInIvy} from '@angular/private/testing';
|
import {onlyInIvy} from '@angular/private/testing';
|
||||||
|
@ -32,7 +32,7 @@ onlyInIvy('Debug information exist in ivy only').describe('ngDevMode debug', ()
|
||||||
|
|
||||||
TestBed.configureTestingModule({declarations: [MyApp], imports: [CommonModule]});
|
TestBed.configureTestingModule({declarations: [MyApp], imports: [CommonModule]});
|
||||||
const fixture = TestBed.createComponent(MyApp);
|
const fixture = TestBed.createComponent(MyApp);
|
||||||
const rootLView = loadLContext(fixture.nativeElement).lView;
|
const rootLView = getLContext(fixture.nativeElement)!.lView;
|
||||||
expect(rootLView.constructor.name).toEqual('LRootView');
|
expect(rootLView.constructor.name).toEqual('LRootView');
|
||||||
|
|
||||||
const componentLView = getComponentLView(fixture.componentInstance);
|
const componentLView = getComponentLView(fixture.componentInstance);
|
||||||
|
@ -41,7 +41,7 @@ onlyInIvy('Debug information exist in ivy only').describe('ngDevMode debug', ()
|
||||||
const element: HTMLElement = fixture.nativeElement;
|
const element: HTMLElement = fixture.nativeElement;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
const li = element.querySelector('li')!;
|
const li = element.querySelector('li')!;
|
||||||
const embeddedLView = loadLContext(li).lView;
|
const embeddedLView = getLContext(li)!.lView;
|
||||||
expect(embeddedLView.constructor.name).toEqual('LEmbeddedView_MyApp_li_1');
|
expect(embeddedLView.constructor.name).toEqual('LEmbeddedView_MyApp_li_1');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue