refactor(ivy): update context discovery to prep for dir merge (#26262)
PR Close #26262
This commit is contained in:
parent
51dfdd5dd1
commit
8f25321787
@ -34,9 +34,9 @@ export interface LContext {
|
||||
lViewData: LViewData;
|
||||
|
||||
/**
|
||||
* The index instance of the LNode.
|
||||
* The index instance of the node.
|
||||
*/
|
||||
lNodeIndex: number;
|
||||
nodeIndex: number;
|
||||
|
||||
/**
|
||||
* The instance of the DOM node that is attached to the lNode.
|
||||
@ -48,11 +48,6 @@ export interface LContext {
|
||||
*/
|
||||
component: {}|null|undefined;
|
||||
|
||||
/**
|
||||
* The list of indices for the active directives that exist on this element.
|
||||
*/
|
||||
directiveIndices: number[]|null|undefined;
|
||||
|
||||
/**
|
||||
* The list of active directives that exist on this element.
|
||||
*/
|
||||
@ -89,27 +84,25 @@ export function getContext(target: any): LContext|null {
|
||||
// ... otherwise it's an already constructed LContext instance
|
||||
if (Array.isArray(mpValue)) {
|
||||
const lViewData: LViewData = mpValue !;
|
||||
let lNodeIndex: number;
|
||||
let nodeIndex: number;
|
||||
let component: any = undefined;
|
||||
let directiveIndices: number[]|null|undefined = undefined;
|
||||
let directives: any[]|null|undefined = undefined;
|
||||
|
||||
if (isComponentInstance(target)) {
|
||||
lNodeIndex = findViaComponent(lViewData, target);
|
||||
if (lNodeIndex == -1) {
|
||||
nodeIndex = findViaComponent(lViewData, target);
|
||||
if (nodeIndex == -1) {
|
||||
throw new Error('The provided component was not found in the application');
|
||||
}
|
||||
component = target;
|
||||
} else if (isDirectiveInstance(target)) {
|
||||
lNodeIndex = findViaDirective(lViewData, target);
|
||||
if (lNodeIndex == -1) {
|
||||
nodeIndex = findViaDirective(lViewData, target);
|
||||
if (nodeIndex == -1) {
|
||||
throw new Error('The provided directive was not found in the application');
|
||||
}
|
||||
directiveIndices = discoverDirectiveIndices(lViewData, lNodeIndex);
|
||||
directives = directiveIndices ? discoverDirectives(lViewData, directiveIndices) : null;
|
||||
directives = discoverDirectives(nodeIndex, lViewData);
|
||||
} else {
|
||||
lNodeIndex = findViaNativeElement(lViewData, target as RElement);
|
||||
if (lNodeIndex == -1) {
|
||||
nodeIndex = findViaNativeElement(lViewData, target as RElement);
|
||||
if (nodeIndex == -1) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -118,11 +111,11 @@ export function getContext(target: any): LContext|null {
|
||||
// are expensive. Instead, only the target data (the element, compontent or
|
||||
// directive details) are filled into the context. If called multiple times
|
||||
// with different target values then the missing target data will be filled in.
|
||||
const lNode = getLNodeFromViewData(lViewData, lNodeIndex) !;
|
||||
const lNode = getLNodeFromViewData(lViewData, nodeIndex) !;
|
||||
const existingCtx = readPatchedData(lNode.native);
|
||||
const context: LContext = (existingCtx && !Array.isArray(existingCtx)) ?
|
||||
existingCtx :
|
||||
createLContext(lViewData, lNodeIndex, lNode.native);
|
||||
createLContext(lViewData, nodeIndex, lNode.native);
|
||||
|
||||
// only when the component has been discovered then update the monkey-patch
|
||||
if (component && context.component === undefined) {
|
||||
@ -131,8 +124,7 @@ export function getContext(target: any): LContext|null {
|
||||
}
|
||||
|
||||
// only when the directives have been discovered then update the monkey-patch
|
||||
if (directives && directiveIndices && context.directives === undefined) {
|
||||
context.directiveIndices = directiveIndices;
|
||||
if (directives && context.directives === undefined) {
|
||||
context.directives = directives;
|
||||
for (let i = 0; i < directives.length; i++) {
|
||||
attachPatchData(directives[i], context);
|
||||
@ -185,31 +177,13 @@ export function getContext(target: any): LContext|null {
|
||||
function createLContext(lViewData: LViewData, lNodeIndex: number, native: RElement): LContext {
|
||||
return {
|
||||
lViewData,
|
||||
lNodeIndex,
|
||||
native,
|
||||
nodeIndex: lNodeIndex, native,
|
||||
component: undefined,
|
||||
directiveIndices: undefined,
|
||||
directives: undefined,
|
||||
localRefs: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* A utility function for retrieving the matching lElementNode
|
||||
* from a given DOM element, component or directive.
|
||||
*/
|
||||
export function getLElementNode(target: any): LElementNode|null {
|
||||
const context = getContext(target);
|
||||
return context ? getLNodeFromViewData(context.lViewData, context.lNodeIndex) : null;
|
||||
}
|
||||
|
||||
export function getLElementFromRootComponent(rootComponentInstance: {}): LElementNode|null {
|
||||
// the host element for the root component is ALWAYS the first element
|
||||
// in the lViewData array (which is where HEADER_OFFSET points to)
|
||||
const lViewData = readPatchedLViewData(rootComponentInstance) !;
|
||||
return readElementValue(lViewData[HEADER_OFFSET]);
|
||||
}
|
||||
|
||||
/**
|
||||
* A simplified lookup function for finding the LElementNode from a component instance.
|
||||
*
|
||||
@ -230,7 +204,7 @@ export function getLElementFromComponent(componentInstance: {}): LElementNode {
|
||||
attachPatchData(context.native, context);
|
||||
} else {
|
||||
const context = lViewData as any as LContext;
|
||||
lNode = readElementValue(context.lViewData[context.lNodeIndex]);
|
||||
lNode = readElementValue(context.lViewData[context.nodeIndex]);
|
||||
}
|
||||
|
||||
return lNode;
|
||||
@ -330,20 +304,19 @@ function findViaDirective(lViewData: LViewData, directiveInstance: {}): number {
|
||||
// if a directive is monkey patched then it will (by default)
|
||||
// have a reference to the LViewData of the current view. The
|
||||
// element bound to the directive being search lives somewhere
|
||||
// in the view data. By first checking to see if the instance
|
||||
// is actually present we can narrow down to which lElementNode
|
||||
// contains the instance of the directive and then return the index
|
||||
// in the view data. We loop through the nodes and check their
|
||||
// list of directives for the instance.
|
||||
const directivesAcrossView = lViewData[DIRECTIVES];
|
||||
const directiveIndex =
|
||||
directivesAcrossView ? directivesAcrossView.indexOf(directiveInstance) : -1;
|
||||
if (directiveIndex >= 0) {
|
||||
let tNode = lViewData[TVIEW].firstChild;
|
||||
if (directivesAcrossView != null) {
|
||||
while (tNode) {
|
||||
const directiveIndexStart = getDirectiveStartIndex(tNode);
|
||||
const directiveIndexEnd = getDirectiveEndIndex(tNode, directiveIndexStart);
|
||||
if (directiveIndex >= directiveIndexStart && directiveIndex < directiveIndexEnd) {
|
||||
for (let i = directiveIndexStart; i < directiveIndexEnd; i++) {
|
||||
if (directivesAcrossView[i] === directiveInstance) {
|
||||
return tNode.index;
|
||||
}
|
||||
}
|
||||
tNode = traverseNextElement(tNode);
|
||||
}
|
||||
}
|
||||
@ -368,50 +341,21 @@ function getLNodeFromViewData(lViewData: LViewData, lElementIndex: number): LEle
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a collection of directive index values that are used on the element
|
||||
* (which is referenced by the lNodeIndex)
|
||||
*/
|
||||
export function discoverDirectiveIndices(
|
||||
lViewData: LViewData, lNodeIndex: number, includeComponents?: boolean): number[]|null {
|
||||
const directivesAcrossView = lViewData[DIRECTIVES];
|
||||
const tNode = lViewData[TVIEW].data[lNodeIndex] as TNode;
|
||||
if (directivesAcrossView && directivesAcrossView.length) {
|
||||
// this check for tNode is to determine if the value is a LElementNode instance
|
||||
const directiveIndexStart = getDirectiveStartIndex(tNode);
|
||||
const directiveIndexEnd = getDirectiveEndIndex(tNode, directiveIndexStart);
|
||||
const directiveIndices: number[] = [];
|
||||
for (let i = directiveIndexStart; i < directiveIndexEnd; i++) {
|
||||
// special case since the instance of the component (if it exists)
|
||||
// is stored in the directives array.
|
||||
if (i > directiveIndexStart ||
|
||||
!isComponentInstance(directivesAcrossView[directiveIndexStart])) {
|
||||
directiveIndices.push(i);
|
||||
}
|
||||
}
|
||||
return directiveIndices.length ? directiveIndices : null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of directives extracted from the given view based on the
|
||||
* provided list of directive index values.
|
||||
* Returns a list of directives extracted from the given view. Does not contain
|
||||
* the component.
|
||||
*
|
||||
* @param lViewData The target view data
|
||||
* @param indices A collection of directive index values which will be used to
|
||||
* figure out the directive instances
|
||||
*/
|
||||
export function discoverDirectives(lViewData: LViewData, indices: number[]): number[]|null {
|
||||
const directives: any[] = [];
|
||||
const directiveInstances = lViewData[DIRECTIVES];
|
||||
if (directiveInstances) {
|
||||
for (let i = 0; i < indices.length; i++) {
|
||||
const directiveIndex = indices[i];
|
||||
const directive = directiveInstances[directiveIndex];
|
||||
directives.push(directive);
|
||||
export function discoverDirectives(nodeIndex: number, lViewData: LViewData): any[]|null {
|
||||
const directivesAcrossView = lViewData[DIRECTIVES];
|
||||
if (directivesAcrossView != null) {
|
||||
const tNode = lViewData[TVIEW].data[nodeIndex] as TNode;
|
||||
let directiveStartIndex = getDirectiveStartIndex(tNode);
|
||||
const directiveEndIndex = getDirectiveEndIndex(tNode, directiveStartIndex);
|
||||
if (tNode.flags & TNodeFlags.isComponent) directiveStartIndex++;
|
||||
return directivesAcrossView.slice(directiveStartIndex, directiveEndIndex);
|
||||
}
|
||||
}
|
||||
return directives;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -11,10 +11,11 @@ import {Renderer2, RendererType2} from '../render/api';
|
||||
import {DebugContext} from '../view';
|
||||
import {DebugRenderer2, DebugRendererFactory2} from '../view/services';
|
||||
|
||||
import {getLElementNode} from './context_discovery';
|
||||
import * as di from './di';
|
||||
import {_getViewData} from './instructions';
|
||||
import {CONTEXT, DIRECTIVES, LViewData, TVIEW} from './interfaces/view';
|
||||
import {TNodeFlags} from './interfaces/node';
|
||||
import {CONTEXT, LViewData, TVIEW} from './interfaces/view';
|
||||
|
||||
|
||||
/**
|
||||
* Adapts the DebugRendererFactory2 to create a DebugRenderer2 specific for IVY.
|
||||
@ -68,25 +69,16 @@ class Render3DebugContext implements DebugContext {
|
||||
|
||||
// TODO(vicb): add view providers when supported
|
||||
get providerTokens(): any[] {
|
||||
const matchedDirectives: any[] = [];
|
||||
|
||||
// TODO(vicb): why/when
|
||||
if (this.nodeIndex === null) {
|
||||
return matchedDirectives;
|
||||
const directiveDefs = this.view[TVIEW].directives;
|
||||
if (this.nodeIndex === null || directiveDefs == null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const directives = this.view[DIRECTIVES];
|
||||
|
||||
if (directives) {
|
||||
const currentNode = this.view[this.nodeIndex];
|
||||
for (let dirIndex = 0; dirIndex < directives.length; dirIndex++) {
|
||||
const directive = directives[dirIndex];
|
||||
if (getLElementNode(directive) === currentNode) {
|
||||
matchedDirectives.push(directive.constructor);
|
||||
}
|
||||
}
|
||||
}
|
||||
return matchedDirectives;
|
||||
const currentTNode = this.view[TVIEW].data[this.nodeIndex];
|
||||
const dirStart = currentTNode >> TNodeFlags.DirectiveStartingIndexShift;
|
||||
const dirEnd = dirStart + (currentTNode & TNodeFlags.DirectiveCountMask);
|
||||
return directiveDefs.slice(dirStart, dirEnd);
|
||||
}
|
||||
|
||||
get references(): {[key: string]: any} {
|
||||
|
@ -8,7 +8,7 @@
|
||||
import {Injector} from '../di/injector';
|
||||
|
||||
import {assertDefined} from './assert';
|
||||
import {LContext, discoverDirectiveIndices, discoverDirectives, discoverLocalRefs, getContext, isComponentInstance, readPatchedLViewData} from './context_discovery';
|
||||
import {LContext, discoverDirectives, discoverLocalRefs, getContext, isComponentInstance, readPatchedLViewData} from './context_discovery';
|
||||
import {NodeInjector} from './di';
|
||||
import {LElementNode, TElementNode, TNode, TNodeFlags} from './interfaces/node';
|
||||
import {CONTEXT, FLAGS, LViewData, LViewFlags, PARENT, RootContext, TVIEW} from './interfaces/view';
|
||||
@ -59,9 +59,9 @@ export function getComponent<T = {}>(target: {}): T|null {
|
||||
*/
|
||||
export function getHostComponent<T = {}>(target: {}): T|null {
|
||||
const context = loadContext(target);
|
||||
const tNode = context.lViewData[TVIEW].data[context.lNodeIndex] as TNode;
|
||||
const tNode = context.lViewData[TVIEW].data[context.nodeIndex] as TNode;
|
||||
if (tNode.flags & TNodeFlags.isComponent) {
|
||||
const lNode = context.lViewData[context.lNodeIndex] as LElementNode;
|
||||
const lNode = context.lViewData[context.nodeIndex] as LElementNode;
|
||||
return lNode.data ![CONTEXT] as any as T;
|
||||
}
|
||||
return null;
|
||||
@ -91,7 +91,7 @@ export function getRootComponents(target: {}): any[] {
|
||||
*/
|
||||
export function getInjector(target: {}): Injector {
|
||||
const context = loadContext(target);
|
||||
const tNode = context.lViewData[TVIEW].data[context.lNodeIndex] as TElementNode;
|
||||
const tNode = context.lViewData[TVIEW].data[context.nodeIndex] as TElementNode;
|
||||
|
||||
return new NodeInjector(tNode, context.lViewData);
|
||||
}
|
||||
@ -104,10 +104,7 @@ export function getDirectives(target: {}): Array<{}> {
|
||||
const context = loadContext(target) !;
|
||||
|
||||
if (context.directives === undefined) {
|
||||
context.directiveIndices = discoverDirectiveIndices(context.lViewData, context.lNodeIndex);
|
||||
context.directives = context.directiveIndices ?
|
||||
discoverDirectives(context.lViewData, context.directiveIndices) :
|
||||
null;
|
||||
context.directives = discoverDirectives(context.nodeIndex, context.lViewData);
|
||||
}
|
||||
|
||||
return context.directives || [];
|
||||
@ -151,7 +148,7 @@ export function getLocalRefs(target: {}): {[key: string]: any} {
|
||||
const context = loadContext(target) !;
|
||||
|
||||
if (context.localRefs === undefined) {
|
||||
context.localRefs = discoverLocalRefs(context.lViewData, context.lNodeIndex);
|
||||
context.localRefs = discoverLocalRefs(context.lViewData, context.nodeIndex);
|
||||
}
|
||||
|
||||
return context.localRefs || {};
|
||||
|
@ -29,11 +29,11 @@ export function getOrCreatePlayerContext(target: {}, context?: LContext | null):
|
||||
'Only elements that exist in an Angular application can be used for player access');
|
||||
}
|
||||
|
||||
const {lViewData, lNodeIndex} = context;
|
||||
const value = lViewData[lNodeIndex];
|
||||
const {lViewData, nodeIndex} = context;
|
||||
const value = lViewData[nodeIndex];
|
||||
let stylingContext = value as StylingContext;
|
||||
if (!Array.isArray(value)) {
|
||||
stylingContext = lViewData[lNodeIndex] = createEmptyStylingContext(value as LElementNode);
|
||||
stylingContext = lViewData[nodeIndex] = createEmptyStylingContext(value as LElementNode);
|
||||
}
|
||||
return stylingContext[StylingIndex.PlayerContext] || allocPlayerContext(stylingContext);
|
||||
}
|
||||
|
@ -446,9 +446,6 @@
|
||||
{
|
||||
"name": "directiveInject"
|
||||
},
|
||||
{
|
||||
"name": "discoverDirectiveIndices"
|
||||
},
|
||||
{
|
||||
"name": "discoverDirectives"
|
||||
},
|
||||
|
@ -1765,14 +1765,14 @@ describe('render3 integration test', () => {
|
||||
const section = fixture.hostElement.querySelector('section') !;
|
||||
const sectionContext = getContext(section) !;
|
||||
const sectionLView = sectionContext.lViewData !;
|
||||
expect(sectionContext.lNodeIndex).toEqual(HEADER_OFFSET);
|
||||
expect(sectionContext.nodeIndex).toEqual(HEADER_OFFSET);
|
||||
expect(sectionLView.length).toBeGreaterThan(HEADER_OFFSET);
|
||||
expect(sectionContext.native).toBe(section);
|
||||
|
||||
const div = fixture.hostElement.querySelector('div') !;
|
||||
const divContext = getContext(div) !;
|
||||
const divLView = divContext.lViewData !;
|
||||
expect(divContext.lNodeIndex).toEqual(HEADER_OFFSET + 1);
|
||||
expect(divContext.nodeIndex).toEqual(HEADER_OFFSET + 1);
|
||||
expect(divLView.length).toBeGreaterThan(HEADER_OFFSET);
|
||||
expect(divContext.native).toBe(div);
|
||||
|
||||
@ -2110,7 +2110,7 @@ describe('render3 integration test', () => {
|
||||
const div1 = hostElm.querySelector('div:first-child') !as any;
|
||||
const div2 = hostElm.querySelector('div:last-child') !as any;
|
||||
const context = getContext(hostElm) !;
|
||||
const elementNode = context.lViewData[context.lNodeIndex];
|
||||
const elementNode = context.lViewData[context.nodeIndex];
|
||||
const elmData = elementNode.data !;
|
||||
const dirs = elmData[DIRECTIVES];
|
||||
|
||||
@ -2134,15 +2134,15 @@ describe('render3 integration test', () => {
|
||||
expect((myDir2Instance as any)[MONKEY_PATCH_KEY_NAME]).toBe(d2Context);
|
||||
expect((myDir3Instance as any)[MONKEY_PATCH_KEY_NAME]).toBe(d3Context);
|
||||
|
||||
expect(d1Context.lNodeIndex).toEqual(HEADER_OFFSET);
|
||||
expect(d1Context.nodeIndex).toEqual(HEADER_OFFSET);
|
||||
expect(d1Context.native).toBe(div1);
|
||||
expect(d1Context.directives as any[]).toEqual([myDir1Instance, myDir2Instance]);
|
||||
|
||||
expect(d2Context.lNodeIndex).toEqual(HEADER_OFFSET);
|
||||
expect(d2Context.nodeIndex).toEqual(HEADER_OFFSET);
|
||||
expect(d2Context.native).toBe(div1);
|
||||
expect(d2Context.directives as any[]).toEqual([myDir1Instance, myDir2Instance]);
|
||||
|
||||
expect(d3Context.lNodeIndex).toEqual(HEADER_OFFSET + 1);
|
||||
expect(d3Context.nodeIndex).toEqual(HEADER_OFFSET + 1);
|
||||
expect(d3Context.native).toBe(div2);
|
||||
expect(d3Context.directives as any[]).toEqual([myDir3Instance]);
|
||||
});
|
||||
@ -2292,14 +2292,14 @@ describe('render3 integration test', () => {
|
||||
const context = getContext(child) !;
|
||||
expect(child[MONKEY_PATCH_KEY_NAME]).toBeTruthy();
|
||||
|
||||
const componentData = context.lViewData[context.lNodeIndex].data;
|
||||
const componentData = context.lViewData[context.nodeIndex].data;
|
||||
const component = componentData[CONTEXT];
|
||||
expect(component instanceof ChildComp).toBeTruthy();
|
||||
expect(component[MONKEY_PATCH_KEY_NAME]).toBe(context.lViewData);
|
||||
|
||||
const componentContext = getContext(component) !;
|
||||
expect(component[MONKEY_PATCH_KEY_NAME]).toBe(componentContext);
|
||||
expect(componentContext.lNodeIndex).toEqual(context.lNodeIndex);
|
||||
expect(componentContext.nodeIndex).toEqual(context.nodeIndex);
|
||||
expect(componentContext.native).toEqual(context.native);
|
||||
expect(componentContext.lViewData).toEqual(context.lViewData);
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user