diff --git a/packages/core/src/render3/context_discovery.ts b/packages/core/src/render3/context_discovery.ts index 492b9470be..fa2af9e6a3 100644 --- a/packages/core/src/render3/context_discovery.ts +++ b/packages/core/src/render3/context_discovery.ts @@ -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,19 +304,18 @@ 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; + let tNode = lViewData[TVIEW].firstChild; + if (directivesAcrossView != null) { while (tNode) { const directiveIndexStart = getDirectiveStartIndex(tNode); const directiveIndexEnd = getDirectiveEndIndex(tNode, directiveIndexStart); - if (directiveIndex >= directiveIndexStart && directiveIndex < directiveIndexEnd) { - return tNode.index; + 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; } /** diff --git a/packages/core/src/render3/debug.ts b/packages/core/src/render3/debug.ts index a3d1240f84..df5c37b117 100644 --- a/packages/core/src/render3/debug.ts +++ b/packages/core/src/render3/debug.ts @@ -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} { diff --git a/packages/core/src/render3/discovery_utils.ts b/packages/core/src/render3/discovery_utils.ts index 0952d7f4ce..42e3e8b8cb 100644 --- a/packages/core/src/render3/discovery_utils.ts +++ b/packages/core/src/render3/discovery_utils.ts @@ -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(target: {}): T|null { */ export function getHostComponent(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,8 +148,8 @@ 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 || {}; -} \ No newline at end of file +} diff --git a/packages/core/src/render3/styling/util.ts b/packages/core/src/render3/styling/util.ts index 1d652bb051..d07896b16c 100644 --- a/packages/core/src/render3/styling/util.ts +++ b/packages/core/src/render3/styling/util.ts @@ -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); } diff --git a/packages/core/test/bundling/animation_world/bundle.golden_symbols.json b/packages/core/test/bundling/animation_world/bundle.golden_symbols.json index 897b025621..139fa30090 100644 --- a/packages/core/test/bundling/animation_world/bundle.golden_symbols.json +++ b/packages/core/test/bundling/animation_world/bundle.golden_symbols.json @@ -446,9 +446,6 @@ { "name": "directiveInject" }, - { - "name": "discoverDirectiveIndices" - }, { "name": "discoverDirectives" }, diff --git a/packages/core/test/render3/integration_spec.ts b/packages/core/test/render3/integration_spec.ts index 3256e03db7..e16eb92630 100644 --- a/packages/core/test/render3/integration_spec.ts +++ b/packages/core/test/render3/integration_spec.ts @@ -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); });