Skip to content

Commit 929fe02

Browse files
committed
refactor(ivy): LView is a proper linked list (angular#28382)
- TView no longer stores childIndex - LView now as CHILD_HEAD and CHILD_TAIL TView used to store the head of the list, therefor all LViews had to have the same head, which is incorrect. PR Close angular#28382
1 parent ba6aa93 commit 929fe02

File tree

7 files changed

+27
-34
lines changed

7 files changed

+27
-34
lines changed

packages/core/src/render3/component_ref.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
190190
component = createRootComponent(
191191
componentView, this.componentDef, rootLView, rootContext, [LifecycleHooksFeature]);
192192

193-
addToViewTree(rootLView, HEADER_OFFSET, componentView);
193+
addToViewTree(rootLView, componentView);
194194
refreshDescendantViews(rootLView);
195195
} finally {
196196
leaveView(oldLView);

packages/core/src/render3/discovery_utils.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,7 @@ export function getViewComponent<T = {}>(element: Element | {}): T|null {
101101
ngDevMode && assertLView(lView);
102102
while (lView[HOST] === null && (parent = getLViewParent(lView) !)) {
103103
// As long as lView[HOST] is null we know we are part of sub-template such as `*ngIf`
104-
if (parent) {
105-
lView = parent;
106-
}
104+
lView = parent;
107105
}
108106
return lView[FLAGS] & LViewFlags.IsRoot ? null : lView[CONTEXT] as T;
109107
}

packages/core/src/render3/instructions.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import {SanitizerFn} from './interfaces/sanitization';
3636
import {StylingContext} from './interfaces/styling';
3737
import {BINDING_INDEX, CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_VIEW, ExpandoInstructions, FLAGS, HEADER_OFFSET, HOST, INJECTOR, InitPhaseState, LView, LViewFlags, NEXT, OpaqueViewState, PARENT, QUERIES, RENDERER, RENDERER_FACTORY, RootContext, RootContextFlags, SANITIZER, TData, TVIEW, TView, T_HOST} from './interfaces/view';
3838
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
39-
import {appendChild, appendProjectedNode, createTextNode, getLViewChild, insertView, removeView} from './node_manipulation';
39+
import {appendChild, appendProjectedNode, createTextNode, insertView, removeView} from './node_manipulation';
4040
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
4141
import {decreaseElementDepthCount, enterView, getBindingsEnabled, getCheckNoChangesMode, getContextLView, getCurrentDirectiveDef, getElementDepthCount, getIsParent, getLView, getPreviousOrParentTNode, increaseElementDepthCount, isCreationMode, leaveView, nextContextImpl, resetComponentState, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setCurrentQueryIndex, setIsParent, setPreviousOrParentTNode,} from './state';
4242
import {getInitialClassNameValue, getInitialStyleStringValue, initializeStaticContext as initializeStaticStylingContext, patchContextWithStaticAttrs, renderInitialClasses, renderInitialStyles, renderStyling, updateClassProp as updateElementClassProp, updateContextWithBindings, updateStyleProp as updateElementStyleProp, updateStylingMap} from './styling/class_and_style_bindings';
@@ -788,7 +788,6 @@ export function createTView(
788788
viewQuery: viewQuery,
789789
node: null !,
790790
data: blueprint.slice().fill(null, bindingStartIndex),
791-
childIndex: -1, // Children set in addToViewTree(), if any
792791
bindingStartIndex: bindingStartIndex,
793792
viewQueryStartIndex: initialViewLength,
794793
expandoStartIndex: initialViewLength,
@@ -2373,7 +2372,7 @@ export function containerRefreshEnd(): void {
23732372
* by executing an associated template function.
23742373
*/
23752374
function refreshDynamicEmbeddedViews(lView: LView) {
2376-
for (let current = getLViewChild(lView); current !== null; current = current[NEXT]) {
2375+
for (let current = lView[CHILD_HEAD]; current !== null; current = current[NEXT]) {
23772376
// Note: current can be an LView or an LContainer instance, but here we are only interested
23782377
// in LContainer. We can tell it's an LContainer because its length is less than the LView
23792378
// header.
@@ -2704,13 +2703,14 @@ export function projection(nodeIndex: number, selectorIndex: number = 0, attrs?:
27042703
*
27052704
* @param lView The view where LView or LContainer should be added
27062705
* @param adjustedHostIndex Index of the view's host node in LView[], adjusted for header
2707-
* @param state The LView or LContainer to add to the view tree
2706+
* @param lViewOrLContainer The LView or LContainer to add to the view tree
27082707
* @returns The state passed in
27092708
*/
27102709
export function addToViewTree<T extends LView|LContainer>(lView: LView, lViewOrLContainer: T): T {
27112710
// TODO(benlesh/misko): This implementation is incorrect, because it always adds the LContainer to
2712-
// the end of the queue, which means if the developer asks for the LContainers out of order, the
2713-
// change detection will run out of order.
2711+
// the end of the queue, which means if the developer retrieves the LContainers from RNodes out of
2712+
// order, the change detection will run out of order, as the act of retrieving the the LContainer
2713+
// from the RNode is what adds it to the queue.
27142714
if (lView[CHILD_HEAD]) {
27152715
lView[CHILD_TAIL] ![NEXT] = lViewOrLContainer;
27162716
} else {

packages/core/src/render3/interfaces/view.ts

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export const CHILD_TAIL = 15;
4646
export const CONTENT_QUERIES = 16;
4747
export const DECLARATION_VIEW = 17;
4848
/** Size of LView's header. Necessary to adjust for it when setting slots. */
49-
export const HEADER_OFFSET = 18;
49+
export const HEADER_OFFSET = 19;
5050

5151

5252
// This interface replaces the real LView interface if it is an arg or a
@@ -79,12 +79,13 @@ export interface LView extends Array<any> {
7979
[FLAGS]: LViewFlags;
8080

8181
/**
82-
* The parent view is needed when we exit the view and must restore the previous
83-
* `LView`. Without this, the render method would have to keep a stack of
82+
* This may store an {@link LView} or {@link LContainer}.
83+
*
84+
* `LView` - The parent view. This is needed when we exit the view and must restore the previous
85+
* LView. Without this, the render method would have to keep a stack of
8486
* views as it is recursively rendering templates.
8587
*
86-
* This is the "insertion" view for embedded views. This allows us to properly
87-
* destroy embedded views.
88+
* `LContainer` - The current view is part of a container, and is an embedded view.
8889
*/
8990
[PARENT]: LView|LContainer|null;
9091

@@ -162,6 +163,15 @@ export interface LView extends Array<any> {
162163
/** An optional custom sanitizer. */
163164
[SANITIZER]: Sanitizer|null;
164165

166+
/**
167+
* Reference to the first LView or LContainer beneath this LView in
168+
* the hierarchy.
169+
*
170+
* Necessary to store this so views can traverse through their nested views
171+
* to remove listeners and call onDestroy callbacks.
172+
*/
173+
[CHILD_HEAD]: LView|LContainer|null;
174+
165175
/**
166176
* The last LView or LContainer beneath this LView in the hierarchy.
167177
*
@@ -393,18 +403,6 @@ export interface TView {
393403
*/
394404
viewQueryStartIndex: number;
395405

396-
/**
397-
* Index of the host node of the first LView or LContainer beneath this LView in
398-
* the hierarchy.
399-
*
400-
* Necessary to store this so views can traverse through their nested views
401-
* to remove listeners and call onDestroy callbacks.
402-
*
403-
* For embedded views, we store the index of an LContainer's host rather than the first
404-
* LView to avoid managing splicing when views are added/removed.
405-
*/
406-
childIndex: number;
407-
408406
/**
409407
* A reference to the first child node located in the view.
410408
*/

packages/core/src/render3/node_manipulation.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -369,12 +369,6 @@ export function removeView(lContainer: LContainer, removeIndex: number) {
369369
destroyLView(view);
370370
}
371371

372-
/** Gets the child of the given LView */
373-
export function getLViewChild(lView: LView): LView|LContainer|null {
374-
const childIndex = lView[TVIEW].childIndex;
375-
return childIndex === -1 ? null : lView[childIndex];
376-
}
377-
378372
/**
379373
* A standalone function which destroys an LView,
380374
* conducting cleanup (e.g. removing listeners, calling onDestroys).

packages/core/src/render3/view_engine_compatibility.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ export function createContainerRef(
314314
hostView[hostTNode.index] = lContainer =
315315
createLContainer(slotValue, hostView, commentNode, true);
316316

317-
addToViewTree(hostView, hostTNode.index as number, lContainer);
317+
addToViewTree(hostView, lContainer);
318318
}
319319

320320
return new R3ViewContainerRef(lContainer, hostTNode, hostView);

packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@
5959
{
6060
"name": "INJECTOR_BLOOM_PARENT_SIZE"
6161
},
62+
{
63+
"name": "LCONTAINER_LENGTH"
64+
},
6265
{
6366
"name": "MONKEY_PATCH_KEY_NAME"
6467
},

0 commit comments

Comments
 (0)