From eb32b6bd6b7eead78aa01e1d81c7dc7c81ca14be Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Mon, 14 Sep 2020 11:21:15 -0700 Subject: [PATCH] refactor(core): Remove reliance on `TNodeType.View`. (#38707) `TNodeType.View` was created to support inline views. That feature did not materialize and we have since removed the instructions for it, leave an unneeded `TNodeType.View` which was still used in a very inconsistent way. This change no longer created `TNodeType.View` (and there will be a follow up chang to completely remove it.) Also simplified the mental model so that `LView[HOST]`/`LView[T_HOST]` always point to the insertion location of the `LView`. PR Close #38707 --- goldens/circular-deps/packages.json | 27 ++---- goldens/size-tracking/aio-payloads.json | 2 +- .../size-tracking/integration-payloads.json | 6 +- packages/core/src/debug/debug_node.ts | 14 ++- .../core/src/di/injector_compatibility.ts | 14 +++ packages/core/src/render3/assert.ts | 18 ++-- packages/core/src/render3/component.ts | 6 +- packages/core/src/render3/component_ref.ts | 16 +--- .../core/src/render3/context_discovery.ts | 16 ++-- packages/core/src/render3/di.ts | 92 +++++++++++++------ packages/core/src/render3/i18n/i18n_apply.ts | 10 +- packages/core/src/render3/instructions/di.ts | 8 +- .../src/render3/instructions/lview_debug.ts | 7 +- .../core/src/render3/instructions/shared.ts | 46 ++++------ .../core/src/render3/instructions/template.ts | 9 +- .../core/src/render3/interfaces/injector.ts | 5 + packages/core/src/render3/interfaces/view.ts | 78 +++++++++------- .../core/src/render3/node_manipulation.ts | 72 +++++++++------ packages/core/src/render3/node_util.ts | 51 ---------- packages/core/src/render3/pipe.ts | 16 ++-- .../core/src/render3/util/discovery_utils.ts | 8 +- .../core/src/render3/util/injector_utils.ts | 14 ++- packages/core/src/render3/util/view_utils.ts | 7 +- .../src/render3/view_engine_compatibility.ts | 84 +++++++++-------- packages/core/src/render3/view_ref.ts | 7 +- packages/core/test/acceptance/di_spec.ts | 2 +- .../core/test/acceptance/integration_spec.ts | 4 +- packages/core/test/acceptance/styling_spec.ts | 2 +- .../bundling/forms/bundle.golden_symbols.json | 12 --- .../hello_world/bundle.golden_symbols.json | 9 +- .../router/bundle.golden_symbols.json | 12 --- .../bundling/todo/bundle.golden_symbols.json | 12 --- packages/core/test/render3/di_spec.ts | 4 +- .../render3/instructions/lview_debug_spec.ts | 2 +- .../test/render3/instructions/shared_spec.ts | 2 +- packages/core/test/render3/is_shape_of.ts | 1 - packages/core/test/render3/matchers_spec.ts | 4 +- .../perf/directive_instantiate/index.ts | 6 +- .../render3/perf/element_text_create/index.ts | 6 +- .../core/test/render3/perf/listeners/index.ts | 6 +- .../test/render3/perf/ng_template/index.ts | 6 +- packages/core/test/render3/perf/setup.ts | 12 ++- .../render3/perf/view_destroy_hook/index.ts | 6 +- packages/core/test/render3/render_util.ts | 6 +- packages/core/test/render3/view_utils_spec.ts | 2 +- 45 files changed, 372 insertions(+), 377 deletions(-) delete mode 100644 packages/core/src/render3/node_util.ts diff --git a/goldens/circular-deps/packages.json b/goldens/circular-deps/packages.json index 9434fcef6c..8c26c0a489 100644 --- a/goldens/circular-deps/packages.json +++ b/goldens/circular-deps/packages.json @@ -389,27 +389,6 @@ "packages/core/src/render3/definition.ts", "packages/core/src/metadata/ng_module.ts" ], - [ - "packages/core/src/application_ref.ts", - "packages/core/src/application_tokens.ts", - "packages/core/src/linker/component_factory.ts", - "packages/core/src/change_detection/change_detection.ts", - "packages/core/src/change_detection/change_detector_ref.ts", - "packages/core/src/render3/view_engine_compatibility.ts", - "packages/core/src/render3/node_manipulation.ts", - "packages/core/src/core.ts", - "packages/core/src/metadata.ts", - "packages/core/src/di.ts", - "packages/core/src/di/index.ts", - "packages/core/src/di/injectable.ts", - "packages/core/src/di/jit/injectable.ts", - "packages/core/src/di/jit/environment.ts", - "packages/core/src/di/injector_compatibility.ts", - "packages/core/src/di/injector.ts", - "packages/core/src/di/r3_injector.ts", - "packages/core/src/render3/definition.ts", - "packages/core/src/metadata/ng_module.ts" - ], [ "packages/core/src/application_ref.ts", "packages/core/src/application_tokens.ts", @@ -904,6 +883,12 @@ "packages/core/src/change_detection/differs/default_keyvalue_differ.ts", "packages/core/src/change_detection/differs/keyvalue_differs.ts" ], + [ + "packages/core/src/debug/debug_node.ts", + "packages/core/src/view/index.ts", + "packages/core/src/view/entrypoint.ts", + "packages/core/src/view/services.ts" + ], [ "packages/core/src/di/injectable.ts", "packages/core/src/di/jit/injectable.ts" diff --git a/goldens/size-tracking/aio-payloads.json b/goldens/size-tracking/aio-payloads.json index e41847d91e..0d8ab13b75 100755 --- a/goldens/size-tracking/aio-payloads.json +++ b/goldens/size-tracking/aio-payloads.json @@ -12,7 +12,7 @@ "master": { "uncompressed": { "runtime-es2015": 3037, - "main-es2015": 448493, + "main-es2015": 447742, "polyfills-es2015": 52415 } } diff --git a/goldens/size-tracking/integration-payloads.json b/goldens/size-tracking/integration-payloads.json index 63282d2d22..60e1524d25 100644 --- a/goldens/size-tracking/integration-payloads.json +++ b/goldens/size-tracking/integration-payloads.json @@ -21,7 +21,7 @@ "master": { "uncompressed": { "runtime-es2015": 1485, - "main-es2015": 146989, + "main-es2015": 146417, "polyfills-es2015": 36571 } } @@ -39,7 +39,7 @@ "master": { "uncompressed": { "runtime-es2015": 2289, - "main-es2015": 242351, + "main-es2015": 241850, "polyfills-es2015": 36938, "5-es2015": 751 } @@ -49,7 +49,7 @@ "master": { "uncompressed": { "runtime-es2015": 2289, - "main-es2015": 218961, + "main-es2015": 218329, "polyfills-es2015": 36723, "5-es2015": 781 } diff --git a/packages/core/src/debug/debug_node.ts b/packages/core/src/debug/debug_node.ts index e258b20e65..2cdbd75585 100644 --- a/packages/core/src/debug/debug_node.ts +++ b/packages/core/src/debug/debug_node.ts @@ -7,6 +7,7 @@ */ import {Injector} from '../di/injector'; +import {assertTNodeForLView} from '../render3/assert'; import {CONTAINER_HEADER_OFFSET, LContainer, NATIVE} from '../render3/interfaces/container'; import {TElementNode, TNode, TNodeFlags, TNodeType} from '../render3/interfaces/node'; import {isComponentHost, isLContainer} from '../render3/interfaces/type_checks'; @@ -15,7 +16,7 @@ import {getComponent, getContext, getInjectionTokens, getInjector, getListeners, import {INTERPOLATION_DELIMITER, renderStringify} from '../render3/util/misc_utils'; import {getComponentLViewByIndex, getNativeByTNodeOrNull} from '../render3/util/view_utils'; import {assertDomNode} from '../util/assert'; -import {DebugContext} from '../view/types'; +import {DebugContext} from '../view/index'; @@ -525,6 +526,7 @@ function _queryAllR3( function _queryNodeChildrenR3( tNode: TNode, lView: LView, predicate: Predicate|Predicate, matches: DebugElement[]|DebugNode[], elementsOnly: boolean, rootNativeNode: any) { + ngDevMode && assertTNodeForLView(tNode, lView); const nativeNode = getNativeByTNodeOrNull(tNode, lView); // For each type of TNode, specific logic is executed. if (tNode.type === TNodeType.Element || tNode.type === TNodeType.ElementContainer) { @@ -616,9 +618,11 @@ function _queryNodeChildrenInContainerR3( lContainer: LContainer, predicate: Predicate|Predicate, matches: DebugElement[]|DebugNode[], elementsOnly: boolean, rootNativeNode: any) { for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) { - const childView = lContainer[i]; - _queryNodeChildrenR3( - childView[TVIEW].node!, childView, predicate, matches, elementsOnly, rootNativeNode); + const childView = lContainer[i] as LView; + const firstChild = childView[TVIEW].firstChild; + if (firstChild) { + _queryNodeChildrenR3(firstChild, childView, predicate, matches, elementsOnly, rootNativeNode); + } } } @@ -658,7 +662,7 @@ function _addQueryMatchR3( * * @param nativeNode the current native node * @param predicate the predicate to match - * @param matches the list of positive matches + * @param matches the list where matches are stored * @param elementsOnly whether only elements should be searched */ function _queryNativeNodeDescendants( diff --git a/packages/core/src/di/injector_compatibility.ts b/packages/core/src/di/injector_compatibility.ts index fc30a8647a..fde76e1801 100644 --- a/packages/core/src/di/injector_compatibility.ts +++ b/packages/core/src/di/injector_compatibility.ts @@ -9,6 +9,7 @@ import '../util/ng_dev_mode'; import {Type} from '../interface/type'; +import {assertNotEqual} from '../util/assert'; import {getClosureSafeProperty} from '../util/property'; import {stringify} from '../util/stringify'; @@ -84,6 +85,19 @@ export function setInjectImplementation( return previous; } +/** + * Assert that `_injectImplementation` is not `fn`. + * + * This is useful, to prevent infinite recursion. + * + * @param fn Function which it should not equal to + */ +export function assertInjectImplementationNot( + fn: ((token: Type|InjectionToken, flags?: InjectFlags) => T | null)) { + ngDevMode && + assertNotEqual(_injectImplementation, fn, 'Calling ɵɵinject would cause infinite recursion'); +} + export function injectInjectorOnly(token: Type|InjectionToken): T; export function injectInjectorOnly(token: Type|InjectionToken, flags?: InjectFlags): T| null; diff --git a/packages/core/src/render3/assert.ts b/packages/core/src/render3/assert.ts index bda945c9d5..e9920df6d0 100644 --- a/packages/core/src/render3/assert.ts +++ b/packages/core/src/render3/assert.ts @@ -6,14 +6,16 @@ * found in the LICENSE file at https://angular.io/license */ -import {assertDefined, assertEqual, assertIndexInRange, assertNumber, throwError} from '../util/assert'; -import {getComponentDef, getNgModuleDef} from './definition'; -import {LContainer} from './interfaces/container'; -import {DirectiveDef} from './interfaces/definition'; +import { assertDefined, assertEqual, assertNumber, throwError } from '../util/assert'; +import { getComponentDef, getNgModuleDef } from './definition'; +import { LContainer } from './interfaces/container'; +import { DirectiveDef } from './interfaces/definition'; import { PARENT_INJECTOR } from './interfaces/injector'; -import {TNode} from './interfaces/node'; -import {isLContainer, isLView} from './interfaces/type_checks'; -import {HEADER_OFFSET, LView, TVIEW, TView} from './interfaces/view'; +import { TNode } from './interfaces/node'; +import { isLContainer, isLView } from './interfaces/type_checks'; +import { HEADER_OFFSET, LView, TVIEW, TView } from './interfaces/view'; + + // [Assert functions do not constraint type when they are guarded by a truthy // expression.](https://github.com/microsoft/TypeScript/issues/37295) @@ -147,4 +149,4 @@ export function assertNodeInjector(lView: LView, injectorIndex: number) { assertNumber( lView[injectorIndex + 8 /*PARENT_INJECTOR*/], 'injectorIndex should point to parent injector'); -} \ No newline at end of file +} diff --git a/packages/core/src/render3/component.ts b/packages/core/src/render3/component.ts index ab01bea194..427e00ad2c 100644 --- a/packages/core/src/render3/component.ts +++ b/packages/core/src/render3/component.ts @@ -129,9 +129,9 @@ export function renderComponent( const rootContext = createRootContext(opts.scheduler, opts.playerHandler); const renderer = rendererFactory.createRenderer(hostRNode, componentDef); - const rootTView = createTView(TViewType.Root, -1, null, 1, 0, null, null, null, null, null); + const rootTView = createTView(TViewType.Root, null, null, 1, 0, null, null, null, null, null); const rootView: LView = createLView( - null, rootTView, rootContext, rootFlags, null, null, rendererFactory, renderer, undefined, + null, rootTView, rootContext, rootFlags, null, null, rendererFactory, renderer, null, opts.injector || null); enterView(rootView); @@ -193,7 +193,7 @@ export function createRootComponentView( const componentView = createLView( rootView, getOrCreateTComponentView(def), null, def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, rootView[HEADER_OFFSET], tNode, - rendererFactory, viewRenderer, sanitizer); + rendererFactory, viewRenderer, sanitizer || null, null); if (tView.firstCreatePass) { diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, rootView), tView, def.type); diff --git a/packages/core/src/render3/component_ref.ts b/packages/core/src/render3/component_ref.ts index aa2b37a26e..8ef0a9b537 100644 --- a/packages/core/src/render3/component_ref.ts +++ b/packages/core/src/render3/component_ref.ts @@ -19,18 +19,16 @@ import {RendererFactory2} from '../render/api'; import {Sanitizer} from '../sanitization/sanitizer'; import {VERSION} from '../version'; import {NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR} from '../view/provider'; - import {assertComponentType} from './assert'; import {createRootComponent, createRootComponentView, createRootContext, LifecycleHooksFeature} from './component'; import {getComponentDef} from './definition'; import {NodeInjector} from './di'; -import {assignTViewNodeToLView, createLView, createTView, elementCreate, locateHostElement, renderView} from './instructions/shared'; +import {createLView, createTView, elementCreate, locateHostElement, renderView} from './instructions/shared'; import {ComponentDef} from './interfaces/definition'; -import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeType} from './interfaces/node'; +import {TContainerNode, TElementContainerNode, TElementNode, TNode} from './interfaces/node'; import {domRendererFactory3, RendererFactory3, RNode} from './interfaces/renderer'; -import {LView, LViewFlags, TVIEW, TViewType} from './interfaces/view'; +import {LView, LViewFlags, TViewType} from './interfaces/view'; import {MATH_ML_NAMESPACE, SVG_NAMESPACE} from './namespaces'; -import {assertNodeOfPossibleTypes} from './node_assert'; import {writeDirectClass} from './node_manipulation'; import {extractAttrsAndClassesFromSelector, stringifyCSSSelectorList} from './node_selector_matcher'; import {enterView, leaveView} from './state'; @@ -158,7 +156,7 @@ export class ComponentFactory extends viewEngine_ComponentFactory { const rootContext = createRootContext(); // Create the root view. Uses empty TView and ContentTemplate. - const rootTView = createTView(TViewType.Root, -1, null, 1, 0, null, null, null, null, null); + const rootTView = createTView(TViewType.Root, null, null, 1, 0, null, null, null, null, null); const rootLView = createLView( null, rootTView, rootContext, rootFlags, null, null, rendererFactory, hostRenderer, sanitizer, rootViewInjector); @@ -223,11 +221,6 @@ export class ComponentFactory extends viewEngine_ComponentFactory { const componentRef = new ComponentRef( this.componentType, component, createElementRef(viewEngine_ElementRef, tElementNode, rootLView), rootLView, tElementNode); - - // The host element of the internal root view is attached to the component's host view node. - ngDevMode && assertNodeOfPossibleTypes(rootTView.node, [TNodeType.View]); - rootTView.node!.child = tElementNode; - return componentRef; } } @@ -267,7 +260,6 @@ export class ComponentRef extends viewEngine_ComponentRef { super(); this.instance = instance; this.hostView = this.changeDetectorRef = new RootViewRef(_rootLView); - assignTViewNodeToLView(_rootLView[TVIEW], null, -1, _rootLView); this.componentType = componentType; } diff --git a/packages/core/src/render3/context_discovery.ts b/packages/core/src/render3/context_discovery.ts index bbd6a62c92..de2211c710 100644 --- a/packages/core/src/render3/context_discovery.ts +++ b/packages/core/src/render3/context_discovery.ts @@ -190,13 +190,11 @@ export function isDirectiveInstance(instance: any): boolean { * Locates the element within the given LView and returns the matching index */ function findViaNativeElement(lView: LView, target: RElement): number { - let tNode = lView[TVIEW].firstChild; - while (tNode) { - const native = getNativeByTNodeOrNull(tNode, lView)!; - if (native === target) { - return tNode.index; + const tView = lView[TVIEW]; + for (let i = HEADER_OFFSET; i < tView.bindingStartIndex; i++) { + if (unwrapRNode(lView[i]) === target) { + return i; } - tNode = traverseNextElement(tNode); } return -1; @@ -206,7 +204,11 @@ function findViaNativeElement(lView: LView, target: RElement): number { * Locates the next tNode (child, sibling or parent). */ function traverseNextElement(tNode: TNode): TNode|null { - if (tNode.child) { + if (tNode.child && tNode.child.parent === tNode) { + // FIXME(misko): checking if `tNode.child.parent === tNode` should not be necessary + // We have added it here because i18n creates TNode's which are not valid, so this is a work + // around. The i18n code is being refactored in #??? and once it lands this extra check can be + // deleted. return tNode.child; } else if (tNode.next) { return tNode.next; diff --git a/packages/core/src/render3/di.ts b/packages/core/src/render3/di.ts index b3b252f416..f7b4043d27 100644 --- a/packages/core/src/render3/di.ts +++ b/packages/core/src/render3/di.ts @@ -13,10 +13,10 @@ import {injectRootLimpMode, setInjectImplementation} from '../di/injector_compat import {getInjectorDef} from '../di/interface/defs'; import {InjectFlags} from '../di/interface/injector'; import {Type} from '../interface/type'; -import {assertDefined, assertEqual} from '../util/assert'; +import {assertDefined, assertEqual, assertIndexInRange, throwError} from '../util/assert'; import {noSideEffects} from '../util/closure'; -import {assertDirectiveDef} from './assert'; +import {assertDirectiveDef, assertNodeInjector, assertTNodeForLView} from './assert'; import {getFactoryDef} from './definition'; import {NG_ELEMENT_ID, NG_FACTORY_DEF} from './fields'; import {registerPreOrderHooks} from './hooks'; @@ -24,7 +24,7 @@ import {DirectiveDef, FactoryFn} from './interfaces/definition'; import {isFactory, NO_PARENT_INJECTOR, NodeInjectorFactory, PARENT_INJECTOR, RelativeInjectorLocation, RelativeInjectorLocationFlags, TNODE} from './interfaces/injector'; import {AttributeMarker, TContainerNode, TDirectiveHostNode, TElementContainerNode, TElementNode, TNode, TNodeProviderIndexes, TNodeType} from './interfaces/node'; import {isComponentDef, isComponentHost} from './interfaces/type_checks'; -import {DECLARATION_COMPONENT_VIEW, DECLARATION_VIEW, INJECTOR, LView, T_HOST, TData, TVIEW, TView} from './interfaces/view'; +import {DECLARATION_COMPONENT_VIEW, DECLARATION_VIEW, INJECTOR, LView, T_HOST, TData, TVIEW, TView, TViewType} from './interfaces/view'; import {assertNodeOfPossibleTypes} from './node_assert'; import {enterDI, leaveDI} from './state'; import {isNameOnlyAttributeMarker} from './util/attrs_utils'; @@ -184,16 +184,17 @@ function insertBloom(arr: any[], footer: TNode|null): void { } -export function getInjectorIndex(tNode: TNode, hostView: LView): number { +export function getInjectorIndex(tNode: TNode, lView: LView): number { if (tNode.injectorIndex === -1 || // If the injector index is the same as its parent's injector index, then the index has been // copied down from the parent node. No injector has been created yet on this node. (tNode.parent && tNode.parent.injectorIndex === tNode.injectorIndex) || // After the first template pass, the injector index might exist but the parent values // might not have been calculated yet for this instance - hostView[tNode.injectorIndex + PARENT_INJECTOR] == null) { + lView[tNode.injectorIndex + PARENT_INJECTOR] == null) { return -1; } else { + ngDevMode && assertIndexInRange(lView, tNode.injectorIndex); return tNode.injectorIndex; } } @@ -204,25 +205,55 @@ export function getInjectorIndex(tNode: TNode, hostView: LView): number { * * Returns a combination of number of `LView` we have to go up and index in that `LView` */ -export function getParentInjectorLocation(tNode: TNode, view: LView): RelativeInjectorLocation { +export function getParentInjectorLocation(tNode: TNode, lView: LView): RelativeInjectorLocation { if (tNode.parent && tNode.parent.injectorIndex !== -1) { + // If we have a parent `TNode` and there is an injector associated with it we are done, because + // the parent injector is within the current `LView`. return tNode.parent.injectorIndex as any; // ViewOffset is 0 } - // For most cases, the parent injector index can be found on the host node (e.g. for component - // or container), so this loop will be skipped, but we must keep the loop here to support - // the rarer case of deeply nested tags or inline views. - let hostTNode = view[T_HOST]; - let viewOffset = 1; - while (hostTNode && hostTNode.injectorIndex === -1) { - view = view[DECLARATION_VIEW]!; - hostTNode = view ? view[T_HOST] : null; - viewOffset++; - } + // When parent injector location is computed it may be outside of the current view. (ie it could + // be pointing to a declared parent location). This variable stores number of declaration parents + // we need to walk up in order to find the parent injector location. + let declarationViewOffset = 0; + let parentTNode: TNode|null = null; + let lViewCursor: LView|null = lView; - return hostTNode ? - hostTNode.injectorIndex | (viewOffset << RelativeInjectorLocationFlags.ViewOffsetShift) : - -1 as any; + // The parent injector is not in the current `LView`. We will have to walk the declared parent + // `LView` hierarchy and look for it. If we walk of the top, that means that there is no parent + // `NodeInjector`. + while (lViewCursor !== null) { + // First determine the `parentTNode` location. The parent pointer differs based on `TView.type`. + const tView = lViewCursor[TVIEW]; + const tViewType = tView.type; + if (tViewType === TViewType.Embedded) { + ngDevMode && assertDefined(tView.node, 'Embedded TNodes should have declaration parents.'); + parentTNode = tView.node; + } else if (tViewType === TViewType.Component) { + // Components don't have `TView.declTNode` because each instance of component could be + // inserted in different location, hence `TView.declTNode` is meaningless. + parentTNode = lViewCursor[T_HOST]; + } else { + ngDevMode && assertEqual(tView.type, TViewType.Root, 'Root type expected'); + parentTNode = null; + } + if (parentTNode === null) { + // If we have no parent, than we are done. + return NO_PARENT_INJECTOR; + } + + ngDevMode && parentTNode && assertTNodeForLView(parentTNode!, lViewCursor[DECLARATION_VIEW]!); + // Every iteration of the loop requires that we go to the declared parent. + declarationViewOffset++; + lViewCursor = lViewCursor[DECLARATION_VIEW]; + + if (parentTNode.injectorIndex !== -1) { + // We found a NodeInjector which points to something. + return (parentTNode.injectorIndex | + (declarationViewOffset << RelativeInjectorLocationFlags.ViewOffsetShift)) as any; + } + } + return NO_PARENT_INJECTOR; } /** @@ -367,13 +398,12 @@ export function getOrCreateInjectable( flags & InjectFlags.Host ? lView[DECLARATION_COMPONENT_VIEW][T_HOST] : null; // If we should skip this injector, or if there is no injector on this node, start by - // searching - // the parent injector. + // searching the parent injector. if (injectorIndex === -1 || flags & InjectFlags.SkipSelf) { parentLocation = injectorIndex === -1 ? getParentInjectorLocation(tNode, lView) : lView[injectorIndex + PARENT_INJECTOR]; - if (!shouldSearchParent(flags, false)) { + if (parentLocation === NO_PARENT_INJECTOR || !shouldSearchParent(flags, false)) { injectorIndex = -1; } else { previousTView = lView[TVIEW]; @@ -385,10 +415,11 @@ export function getOrCreateInjectable( // Traverse up the injector tree until we find a potential match or until we know there // *isn't* a match. while (injectorIndex !== -1) { - parentLocation = lView[injectorIndex + PARENT_INJECTOR]; + ngDevMode && assertNodeInjector(lView, injectorIndex); // Check the current injector. If it matches, see if it contains token. const tView = lView[TVIEW]; + ngDevMode && assertTNodeForLView(tView.data[injectorIndex + TNODE] as TNode, lView); if (bloomHasToken(bloomHash, injectorIndex, tView.data)) { // At this point, we have an injector which *may* contain the token, so we step through // the providers and directives associated with the injector's corresponding node to get @@ -399,7 +430,9 @@ export function getOrCreateInjectable( return instance; } } - if (shouldSearchParent( + parentLocation = lView[injectorIndex + PARENT_INJECTOR]; + if (parentLocation !== NO_PARENT_INJECTOR && + shouldSearchParent( flags, lView[TVIEW].data[injectorIndex + TNODE] === hostTElementNode) && bloomHasToken(bloomHash, injectorIndex, lView)) { // The def wasn't found anywhere on this node, so it was a false positive. @@ -453,7 +486,7 @@ function searchTokensOnInjector( const currentTView = lView[TVIEW]; const tNode = currentTView.data[injectorIndex + TNODE] as TNode; // First, we need to determine if view providers can be accessed by the starting element. - // There are two possibities + // There are two possibilities const canAccessViewProviders = previousTView == null ? // 1) This is the first invocation `previousTView == null` which means that we are at the // `TNode` of where injector is starting to look. In such a case the only time we are allowed @@ -544,10 +577,8 @@ export function getNodeInjectable( } const previousIncludeViewProviders = setIncludeViewProviders(factory.canSeeViewProviders); factory.resolving = true; - let previousInjectImplementation; - if (factory.injectImpl) { - previousInjectImplementation = setInjectImplementation(factory.injectImpl); - } + const previousInjectImplementation = + factory.injectImpl ? setInjectImplementation(factory.injectImpl) : null; enterDI(lView, tNode); try { value = lView[index] = factory.factory(undefined, tData, lView, tNode); @@ -562,7 +593,8 @@ export function getNodeInjectable( registerPreOrderHooks(index, tData[index] as DirectiveDef, tView); } } finally { - if (factory.injectImpl) setInjectImplementation(previousInjectImplementation); + previousInjectImplementation !== null && + setInjectImplementation(previousInjectImplementation); setIncludeViewProviders(previousIncludeViewProviders); factory.resolving = false; leaveDI(); diff --git a/packages/core/src/render3/i18n/i18n_apply.ts b/packages/core/src/render3/i18n/i18n_apply.ts index 5ef1b588bb..81e23b9622 100644 --- a/packages/core/src/render3/i18n/i18n_apply.ts +++ b/packages/core/src/render3/i18n/i18n_apply.ts @@ -354,7 +354,13 @@ function appendI18nNode( // Re-organize node tree to put this node in the correct position. if (previousTNode === parentTNode && tNode !== parentTNode.child) { tNode.next = parentTNode.child; - parentTNode.child = tNode; + // FIXME(misko): Checking `tNode.parent` is a temporary workaround until we properly + // refactor the i18n code in #38707 and this code will be deleted. + if (tNode.parent === null) { + tView.firstChild = tNode; + } else { + parentTNode.child = tNode; + } } else if (previousTNode !== parentTNode && tNode !== previousTNode.next) { tNode.next = previousTNode.next; previousTNode.next = tNode; @@ -446,7 +452,7 @@ function removeNode(tView: TView, lView: LView, index: number, markAsDetached: b } } - if (markAsDetached) { + if (markAsDetached && removedPhTNode) { // Define this node as detached to avoid projecting it later removedPhTNode.flags |= TNodeFlags.isDetached; } diff --git a/packages/core/src/render3/instructions/di.ts b/packages/core/src/render3/instructions/di.ts index 075ed38037..ce010ab126 100644 --- a/packages/core/src/render3/instructions/di.ts +++ b/packages/core/src/render3/instructions/di.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ import {InjectFlags, InjectionToken, resolveForwardRef} from '../../di'; -import {ɵɵinject} from '../../di/injector_compatibility'; +import {assertInjectImplementationNot, ɵɵinject} from '../../di/injector_compatibility'; import {Type} from '../../interface/type'; import {getOrCreateInjectable, injectAttributeImpl} from '../di'; import {TDirectiveHostNode} from '../interfaces/node'; @@ -43,7 +43,11 @@ export function ɵɵdirectiveInject( const lView = getLView(); // Fall back to inject() if view hasn't been created. This situation can happen in tests // if inject utilities are used before bootstrapping. - if (lView == null) return ɵɵinject(token, flags); + if (lView === null) { + // Verify that we will not get into infinite loop. + ngDevMode && assertInjectImplementationNot(ɵɵdirectiveInject); + return ɵɵinject(token, flags); + } const tNode = getPreviousOrParentTNode(); return getOrCreateInjectable( tNode as TDirectiveHostNode, lView, resolveForwardRef(token), flags); diff --git a/packages/core/src/render3/instructions/lview_debug.ts b/packages/core/src/render3/instructions/lview_debug.ts index 5521e3a370..4a71170491 100644 --- a/packages/core/src/render3/instructions/lview_debug.ts +++ b/packages/core/src/render3/instructions/lview_debug.ts @@ -17,7 +17,7 @@ import {getInjectorIndex} from '../di'; import {CONTAINER_HEADER_OFFSET, HAS_TRANSPLANTED_VIEWS, LContainer, MOVED_VIEWS, NATIVE} from '../interfaces/container'; import {ComponentTemplate, DirectiveDef, DirectiveDefList, PipeDefList, ViewQueriesFunction} from '../interfaces/definition'; import {NO_PARENT_INJECTOR, PARENT_INJECTOR, TNODE} from '../interfaces/injector'; -import {AttributeMarker, PropertyAliases, TConstants, TContainerNode, TElementNode, TNode as ITNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TNodeTypeAsString, TViewNode} from '../interfaces/node'; +import {AttributeMarker, PropertyAliases, TConstants, TContainerNode, TElementNode, TNode as ITNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TNodeTypeAsString} from '../interfaces/node'; import {SelectorFlags} from '../interfaces/projection'; import {LQueries, TQueries} from '../interfaces/query'; import {RComment, RElement, Renderer3, RendererFactory3, RNode} from '../interfaces/renderer'; @@ -116,12 +116,11 @@ function nameSuffix(text: string|null|undefined): string { export const TViewConstructor = class TView implements ITView { constructor( public type: TViewType, // - public id: number, // public blueprint: LView, // public template: ComponentTemplate<{}>|null, // public queries: TQueries|null, // public viewQuery: ViewQueriesFunction<{}>|null, // - public node: TViewNode|TElementNode|null, // + public node: ITNode|null, // public data: TData, // public bindingStartIndex: number, // public expandoStartIndex: number, // @@ -493,7 +492,7 @@ export class LViewDebug implements ILViewDebug { get queries(): LQueries|null { return this._raw_lView[QUERIES]; } - get tHost(): TViewNode|TElementNode|null { + get tHost(): ITNode|null { return this._raw_lView[T_HOST]; } diff --git a/packages/core/src/render3/instructions/shared.ts b/packages/core/src/render3/instructions/shared.ts index 837302c179..d4226fe72d 100644 --- a/packages/core/src/render3/instructions/shared.ts +++ b/packages/core/src/render3/instructions/shared.ts @@ -17,7 +17,7 @@ import {createNamedArrayType} from '../../util/named_array_type'; import {initNgDevMode} from '../../util/ng_dev_mode'; import {normalizeDebugBindingName, normalizeDebugBindingValue} from '../../util/ng_reflect'; import {stringify} from '../../util/stringify'; -import {assertFirstCreatePass, assertLContainer, assertLView} from '../assert'; +import {assertFirstCreatePass, assertLContainer, assertLView, assertTNodeForLView} from '../assert'; import {attachPatchData} from '../context_discovery'; import {getFactoryDef} from '../definition'; import {diPublicInInjector, getNodeInjectable, getOrCreateNodeInjectorForNode} from '../di'; @@ -172,13 +172,14 @@ export function elementCreate(name: string, renderer: Renderer3, namespace: stri export function createLView( parentLView: LView|null, tView: TView, context: T|null, flags: LViewFlags, host: RElement|null, - tHostNode: TViewNode|TElementNode|null, rendererFactory?: RendererFactory3|null, - renderer?: Renderer3|null, sanitizer?: Sanitizer|null, injector?: Injector|null): LView { + tHostNode: TNode|null, rendererFactory: RendererFactory3|null, renderer: Renderer3|null, + sanitizer: Sanitizer|null, injector: Injector|null): LView { const lView = ngDevMode ? cloneToLViewFromTViewBlueprint(tView) : tView.blueprint.slice() as LView; lView[HOST] = host; lView[FLAGS] = flags | LViewFlags.CreationMode | LViewFlags.Attached | LViewFlags.FirstLViewPass; resetPreOrderHookFlags(lView); + ngDevMode && tView.node && parentLView && assertTNodeForLView(tView.node, parentLView); lView[PARENT] = lView[DECLARATION_VIEW] = parentLView; lView[CONTEXT] = context; lView[RENDERER_FACTORY] = (rendererFactory || parentLView && parentLView[RENDERER_FACTORY])!; @@ -250,7 +251,7 @@ function createTNodeAtIndex( const parentInSameView = parent !== null && parent.type !== TNodeType.View; const tParentNode = parentInSameView ? parent as TElementNode | TContainerNode : null; const tNode = tView.data[adjustedIndex] = - createTNode(tView, tParentNode, type, adjustedIndex, name, attrs); + createTNode(tView, parent as TElementNode | TContainerNode, type, adjustedIndex, name, attrs); // Assign a pointer to the first child node of a given view. The first node is not always the one // at index 0, in case of i18n, index 0 can be the instruction `i18nStart` and the first node has // the index 1 or more, so we can't just check node index. @@ -269,23 +270,6 @@ function createTNodeAtIndex( return tNode; } -export function assignTViewNodeToLView( - tView: TView, tParentNode: TNode|null, index: number, lView: LView): void { - // View nodes are not stored in data because they can be added / removed at runtime (which - // would cause indices to change). Their TNodes are instead stored in tView.node. - let tNode = tView.node; - if (tNode == null) { - ngDevMode && tParentNode && - assertNodeOfPossibleTypes(tParentNode, [TNodeType.Element, TNodeType.Container]); - tView.node = tNode = createTNode( - tView, - tParentNode as TElementNode | TContainerNode | null, // - TNodeType.View, index, null, null) as TViewNode; - } - - lView[T_HOST] = tNode as TViewNode; -} - /** * When elements are created dynamically after a view blueprint is created (e.g. through @@ -623,8 +607,11 @@ export function getOrCreateTComponentView(def: ComponentDef): TView { // Create a TView if there isn't one, or recreate it if the first create pass didn't // complete successfully since we can't know for sure whether it's in a usable shape. if (tView === null || tView.incompleteFirstPass) { + // Declaration node here is null since this function is called when we dynamically create a + // component and hence there is no declaration. + const declTNode = null; return def.tView = createTView( - TViewType.Component, -1, def.template, def.decls, def.vars, def.directiveDefs, + TViewType.Component, declTNode, def.template, def.decls, def.vars, def.directiveDefs, def.pipeDefs, def.viewQuery, def.schemas, def.consts); } @@ -635,7 +622,8 @@ export function getOrCreateTComponentView(def: ComponentDef): TView { /** * Creates a TView instance * - * @param viewIndex The viewBlockId for inline views, or -1 if it's a component/dynamic + * @param type Type of `TView`. + * @param declTNode Declaration location of this `TView`. * @param templateFn Template function * @param decls The number of nodes, local refs, and pipes in this template * @param directives Registry of directives for this view @@ -645,7 +633,7 @@ export function getOrCreateTComponentView(def: ComponentDef): TView { * @param consts Constants for this view */ export function createTView( - type: TViewType, viewIndex: number, templateFn: ComponentTemplate|null, decls: number, + type: TViewType, declTNode: TNode|null, templateFn: ComponentTemplate|null, decls: number, vars: number, directives: DirectiveDefListOrFactory|null, pipes: PipeDefListOrFactory|null, viewQuery: ViewQueriesFunction|null, schemas: SchemaMetadata[]|null, constsOrFactory: TConstantsOrFactory|null): TView { @@ -660,12 +648,11 @@ export function createTView( const tView = blueprint[TVIEW as any] = ngDevMode ? new TViewConstructor( type, - viewIndex, // id: number, blueprint, // blueprint: LView, templateFn, // template: ComponentTemplate<{}>|null, null, // queries: TQueries|null viewQuery, // viewQuery: ViewQueriesFunction<{}>|null, - null!, // node: TViewNode|TElementNode|null, + declTNode, // declTNode: TNode|null, cloneToTViewData(blueprint).fill(null, bindingStartIndex), // data: TData, bindingStartIndex, // bindingStartIndex: number, initialViewLength, // expandoStartIndex: number, @@ -697,12 +684,11 @@ export function createTView( ) : { type: type, - id: viewIndex, blueprint: blueprint, template: templateFn, queries: null, viewQuery: viewQuery, - node: null!, + node: declTNode, data: blueprint.slice().fill(null, bindingStartIndex), bindingStartIndex: bindingStartIndex, expandoStartIndex: initialViewLength, @@ -812,6 +798,7 @@ export function storeCleanupWithContext( * Constructs a TNode object from the arguments. * * @param tView `TView` to which this `TNode` belongs (used only in `ngDevMode`) + * @param tParent Parent `TNode` * @param type The type of the node * @param adjustedIndex The index of the TNode in TView.data, adjusted for HEADER_OFFSET * @param tagName The tag name of the node @@ -1488,7 +1475,8 @@ function addComponentLogic(lView: LView, hostTNode: TElementNode, def: Compon lView, createLView( lView, tView, null, def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, native, - hostTNode as TElementNode, rendererFactory, rendererFactory.createRenderer(native, def))); + hostTNode as TElementNode, rendererFactory, rendererFactory.createRenderer(native, def), + null, null)); // Component view will always be created before any injected LContainers, // so this is a regular element, wrap it with the component view diff --git a/packages/core/src/render3/instructions/template.ts b/packages/core/src/render3/instructions/template.ts index 669b3c4a60..1bfacec8d7 100644 --- a/packages/core/src/render3/instructions/template.ts +++ b/packages/core/src/render3/instructions/template.ts @@ -35,13 +35,8 @@ function templateFirstCreatePass( registerPostOrderHooks(tView, tNode); const embeddedTView = tNode.tViews = createTView( - TViewType.Embedded, -1, templateFn, decls, vars, tView.directiveRegistry, tView.pipeRegistry, - null, tView.schemas, tViewConsts); - const embeddedTViewNode = - createTNode(embeddedTView, null, TNodeType.View, -1, null, null) as TViewNode; - embeddedTViewNode.injectorIndex = tNode.injectorIndex; - // FIXME(misko): remove `embeddedTView.node' - embeddedTView.node = embeddedTView.firstChild = embeddedTViewNode; + TViewType.Embedded, tNode, templateFn, decls, vars, tView.directiveRegistry, + tView.pipeRegistry, null, tView.schemas, tViewConsts); if (tView.queries !== null) { tView.queries.template(tView, tNode); diff --git a/packages/core/src/render3/interfaces/injector.ts b/packages/core/src/render3/interfaces/injector.ts index 1c5da2b9a4..566775cfa1 100644 --- a/packages/core/src/render3/interfaces/injector.ts +++ b/packages/core/src/render3/interfaces/injector.ts @@ -14,6 +14,11 @@ import {assertDefined, assertEqual} from '../../util/assert'; import {TDirectiveHostNode} from './node'; import {LView, TData} from './view'; +/** + * Offset from 'injectorIndex' where: + * - `TViewData[injectorIndex + TNODE]` => `TNode` associated with the current injector. + * - `LView[injectorIndex + TNODE]` => index to the parent injector. + */ export const TNODE = 8; export const PARENT_INJECTOR = 8; export const INJECTOR_BLOOM_PARENT_SIZE = 9; diff --git a/packages/core/src/render3/interfaces/view.ts b/packages/core/src/render3/interfaces/view.ts index 2c96f100bb..8ee233cb7b 100644 --- a/packages/core/src/render3/interfaces/view.ts +++ b/packages/core/src/render3/interfaces/view.ts @@ -41,6 +41,7 @@ export const RENDERER = 11; export const SANITIZER = 12; export const CHILD_HEAD = 13; export const CHILD_TAIL = 14; +// FIXME(misko): Investigate if the three declarations aren't all same thing. export const DECLARATION_VIEW = 15; export const DECLARATION_COMPONENT_VIEW = 16; export const DECLARATION_LCONTAINER = 17; @@ -79,8 +80,7 @@ export interface LView extends Array { debug?: LViewDebug; /** - * The host node for this LView instance, if this is a component view. - * If this is an embedded view, HOST will be null. + * The node into which this `LView` is inserted. */ [HOST]: RElement|null; @@ -120,16 +120,35 @@ export interface LView extends Array { [QUERIES]: LQueries|null; /** - * Pointer to the `TViewNode` or `TElementNode` which represents the root of the view. + * Store the `TNode` of the location where the current `LView` is inserted into. * - * If `TViewNode`, this is an embedded view of a container. We need this to be able to - * efficiently find the `LViewNode` when inserting the view into an anchor. + * Given: + * ``` + *
+ * + *
+ * ``` * - * If `TElementNode`, this is the LView of a component. + * We end up with two `TView`s. + * - `parent` `TView` which contains `
` + * - `child` `TView` which contains `` * - * If null, this is the root view of an application (root component is in this view). + * Typically the `child` is inserted into the declaration location of the `parent`, but it can be + * inserted anywhere. Because it can be inserted anywhere it is not possible to store the + * insertion information in the `TView` and instead we must store it in the `LView[T_HOST]`. + * + * So to determine where is our insertion parent we would execute: + * ``` + * const parentLView = lView[PARENT]; + * const parentTNode = lView[T_HOST]; + * const insertionParent = parentLView[parentTNode.index]; + * ``` + * + * + * If `null`, this is the root view of an application (root component is in this view) and it has + * no parents. */ - [T_HOST]: TViewNode|TElementNode|null; + [T_HOST]: TNode|null; /** * When a view is destroyed, listeners need to be released and outputs need to be @@ -183,8 +202,6 @@ export interface LView extends Array { /** * View where this view's template was declared. * - * Only applicable for dynamically created views. Will be null for inline/component views. - * * The template for a dynamically created view may be declared in a different view than * it is inserted. We already track the "insertion view" (view where the template was * inserted) in LView[PARENT], but we also need access to the "declaration view" @@ -439,6 +456,17 @@ export const enum TViewType { Embedded = 2, } +/** + * Converts `TViewType` into human readable text. + * Make sure this matches with `TViewType` + */ +export const TViewTypeAsString = [ + 'Root', // 0 + 'Component', // 1 + 'Embedded', // 2 +] as const; + + /** * The static data for an LView (shared between all templates of a * given type). @@ -451,15 +479,6 @@ export interface TView { */ type: TViewType; - /** - * ID for inline views to determine whether a view is the same as the previous view - * in a certain position. If it's not, we know the new view needs to be inserted - * and the one that exists needs to be removed (e.g. if/else statements) - * - * If this is -1, then this is a component view or a dynamically created view. - */ - readonly id: number; - /** * This is a blueprint used to generate LView instances for this TView. Copying this * blueprint is faster than creating a new LView from scratch. @@ -478,21 +497,12 @@ export interface TView { viewQuery: ViewQueriesFunction<{}>|null; /** - * Pointer to the host `TNode` (not part of this TView). - * - * If this is a `TViewNode` for an `LViewNode`, this is an embedded view of a container. - * We need this pointer to be able to efficiently find this node when inserting the view - * into an anchor. - * - * If this is a `TElementNode`, this is the view of a root component. It has exactly one - * root TNode. - * - * If this is null, this is the view of a component that is not at root. We do not store - * the host TNodes for child component views because they can potentially have several - * different host TNodes, depending on where the component is being used. These host - * TNodes cannot be shared (due to different indices, etc). + * A `TNode` representing the declaration location of this `TView` (not part of this TView). */ - node: TViewNode|TElementNode|null; + // FIXME(misko): Rename `node` to `declTNode` + node: TNode|null; + + // FIXME(misko): Why does `TView` not have `declarationTView` property? /** Whether or not this template has been processed in creation mode. */ firstCreatePass: boolean; @@ -1038,7 +1048,7 @@ export interface DebugNode { injector: NodeInjectorDebug; } -interface NodeInjectorDebug { +export interface NodeInjectorDebug { /** * Instance bloom. Does the current injector have a provider with a given bloom mask. */ diff --git a/packages/core/src/render3/node_manipulation.ts b/packages/core/src/render3/node_manipulation.ts index ef5985973b..3d5aa10979 100644 --- a/packages/core/src/render3/node_manipulation.ts +++ b/packages/core/src/render3/node_manipulation.ts @@ -6,8 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {Renderer2} from '../core'; import {ViewEncapsulation} from '../metadata/view'; +import {Renderer2} from '../render/api'; import {addToArray, removeFromArray} from '../util/array_utils'; import {assertDefined, assertDomNode, assertEqual, assertSame, assertString} from '../util/assert'; @@ -20,7 +20,7 @@ import {TElementNode, TNode, TNodeFlags, TNodeType, TProjectionNode, TViewNode, import {unusedValueExportToPlacateAjd as unused3} from './interfaces/projection'; import {isProceduralRenderer, ProceduralRenderer3, RElement, Renderer3, RNode, RText, unusedValueExportToPlacateAjd as unused4} from './interfaces/renderer'; import {isLContainer, isLView} from './interfaces/type_checks'; -import {CHILD_HEAD, CLEANUP, DECLARATION_COMPONENT_VIEW, DECLARATION_LCONTAINER, DestroyHookData, FLAGS, HookData, HookFn, HOST, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, T_HOST, TVIEW, TView, unusedValueExportToPlacateAjd as unused5} from './interfaces/view'; +import {CHILD_HEAD, CLEANUP, DECLARATION_COMPONENT_VIEW, DECLARATION_LCONTAINER, DestroyHookData, FLAGS, HookData, HookFn, HOST, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, T_HOST, TVIEW, TView, TViewType, unusedValueExportToPlacateAjd as unused5} from './interfaces/view'; import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert'; import {getLViewParent} from './util/view_traversal_utils'; import {getNativeByTNode, getNonViewFirstChild, unwrapRNode, updateTransplantedViewCount} from './util/view_utils'; @@ -125,7 +125,7 @@ export function createTextNode(value: string, renderer: Renderer3): RText { } /** - * Adds or removes all DOM elements associated with a view. + * Removes all DOM elements associated with a view. * * Because some root nodes of the view may be containers, we sometimes need * to propagate deeply into the nested containers to remove all elements in the @@ -133,24 +133,37 @@ export function createTextNode(value: string, renderer: Renderer3): RText { * * @param tView The `TView' of the `LView` from which elements should be added or removed * @param lView The view from which elements should be added or removed - * @param insertMode Whether or not elements should be added (if false, removing) + */ +export function removeViewFromContainer(tView: TView, lView: LView): void { + const renderer = lView[RENDERER]; + applyView(tView, lView, renderer, WalkTNodeTreeAction.Detach, null, null); + lView[HOST] = null; + lView[T_HOST] = null; +} + +/** + * Adds all DOM elements associated with a view. + * + * Because some root nodes of the view may be containers, we sometimes need + * to propagate deeply into the nested containers to add all elements in the + * views beneath it. + * + * @param tView The `TView' of the `LView` from which elements should be added or removed + * @param parentTNode The `TNode` where the `LView` should be attached to. + * @param renderer Current renderer to use for DOM manipulations. + * @param lView The view from which elements should be added or removed + * @param parentNativeNode The parent `RElement` where it should be inserted into. * @param beforeNode The node before which elements should be added, if insert mode */ -export function addRemoveViewFromContainer( - tView: TView, lView: LView, insertMode: true, beforeNode: RNode|null): void; -export function addRemoveViewFromContainer( - tView: TView, lView: LView, insertMode: false, beforeNode: null): void; -export function addRemoveViewFromContainer( - tView: TView, lView: LView, insertMode: boolean, beforeNode: RNode|null): void { - const renderParent = getContainerRenderParent(tView.node as TViewNode, lView); - ngDevMode && assertNodeType(tView.node as TNode, TNodeType.View); - if (renderParent) { - const renderer = lView[RENDERER]; - const action = insertMode ? WalkTNodeTreeAction.Insert : WalkTNodeTreeAction.Detach; - applyView(tView, lView, renderer, action, renderParent, beforeNode); - } +export function addViewToContainer( + tView: TView, parentTNode: TNode, renderer: Renderer3, lView: LView, parentNativeNode: RElement, + beforeNode: RNode|null): void { + lView[HOST] = parentNativeNode; + lView[T_HOST] = parentTNode; + applyView(tView, lView, renderer, WalkTNodeTreeAction.Insert, parentNativeNode, beforeNode); } + /** * Detach a `LView` from the DOM by detaching its nodes. * @@ -334,7 +347,7 @@ export function detachView(lContainer: LContainer, removeIndex: number): LView|u lContainer[indexInContainer - 1][NEXT] = viewToDetach[NEXT] as LView; } const removedLView = removeFromArray(lContainer, CONTAINER_HEADER_OFFSET + removeIndex); - addRemoveViewFromContainer(viewToDetach[TVIEW], viewToDetach, false, null); + removeViewFromContainer(viewToDetach[TVIEW], viewToDetach); // notify query that a view has been removed const lQueries = removedLView[QUERIES]; @@ -417,10 +430,8 @@ function cleanUpView(tView: TView, lView: LView): void { executeOnDestroys(tView, lView); removeListeners(tView, lView); - const hostTNode = lView[T_HOST]; - // For component views only, the local renderer is destroyed as clean up time. - if (hostTNode && hostTNode.type === TNodeType.Element && - isProceduralRenderer(lView[RENDERER])) { + // For component views only, the local renderer is destroyed at clean up time. + if (lView[TVIEW].type === TViewType.Component && isProceduralRenderer(lView[RENDERER])) { ngDevMode && ngDevMode.rendererDestroy++; (lView[RENDERER] as ProceduralRenderer3).destroy(); } @@ -530,7 +541,7 @@ function getRenderParent(tView: TView, tNode: TNode, currentView: LView): REleme // or a component view. if (parentTNode == null) { const hostTNode = currentView[T_HOST]!; - if (hostTNode.type === TNodeType.View) { + if (hostTNode && hostTNode.type === TNodeType.View) { // We are inserting a root element of an embedded view We might delay insertion of children // for a given view if it is disconnected. This might happen for 2 main reasons: // - view is not inserted into any container(view was created but not inserted yet) @@ -542,7 +553,6 @@ function getRenderParent(tView: TView, tNode: TNode, currentView: LView): REleme } else { // We are inserting a root element of the component view into the component host element and // it should always be eager. - ngDevMode && assertNodeOfPossibleTypes(hostTNode, [TNodeType.Element]); return currentView[HOST]; } } else { @@ -817,15 +827,19 @@ function applyNodes( * @param lView The LView which needs to be inserted, detached, destroyed. * @param renderer Renderer to use * @param action action to perform (insert, detach, destroy) - * @param renderParent parent DOM element for insertion/removal. + * @param renderParent parent DOM element for insertion (Removal does not need it). * @param beforeNode Before which node the insertions should happen. */ +function applyView( + tView: TView, lView: LView, renderer: Renderer3, action: WalkTNodeTreeAction.Destroy, + renderParent: null, beforeNode: null): void; function applyView( tView: TView, lView: LView, renderer: Renderer3, action: WalkTNodeTreeAction, - renderParent: RElement|null, beforeNode: RNode|null) { - ngDevMode && assertNodeType(tView.node!, TNodeType.View); - const viewRootTNode: TNode|null = getNonViewFirstChild(tView); - applyNodes(renderer, action, viewRootTNode, lView, renderParent, beforeNode, false); + renderParent: RElement|null, beforeNode: RNode|null): void; +function applyView( + tView: TView, lView: LView, renderer: Renderer3, action: WalkTNodeTreeAction, + renderParent: RElement|null, beforeNode: RNode|null): void { + applyNodes(renderer, action, tView.firstChild, lView, renderParent, beforeNode, false); } /** diff --git a/packages/core/src/render3/node_util.ts b/packages/core/src/render3/node_util.ts deleted file mode 100644 index ab98bbacea..0000000000 --- a/packages/core/src/render3/node_util.ts +++ /dev/null @@ -1,51 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {RelativeInjectorLocation} from './interfaces/injector'; -import {TContainerNode, TElementNode, TNode} from './interfaces/node'; -import {DECLARATION_VIEW, LView, T_HOST} from './interfaces/view'; -import {getParentInjectorViewOffset} from './util/injector_utils'; - -/** - * If `startTNode.parent` exists and has an injector, returns TNode for that injector. - * Otherwise, unwraps a parent injector location number to find the view offset from the current - * injector, then walks up the declaration view tree until the TNode of the parent injector is - * found. - * - * @param location The location of the parent injector, which contains the view offset - * @param startView The LView instance from which to start walking up the view tree - * @param startTNode The TNode instance of the starting element - * @returns The TNode of the parent injector - */ -export function getParentInjectorTNode( - location: RelativeInjectorLocation, startView: LView, startTNode: TNode): TElementNode| - TContainerNode|null { - // If there is an injector on the parent TNode, retrieve the TNode for that injector. - if (startTNode.parent && startTNode.parent.injectorIndex !== -1) { - // view offset is 0 - const injectorIndex = startTNode.parent.injectorIndex; - let tNode = startTNode.parent; - // If tNode.injectorIndex === tNode.parent.injectorIndex, then the index belongs to a parent - // injector. - while (tNode.parent != null && injectorIndex == tNode.parent.injectorIndex) { - tNode = tNode.parent; - } - return tNode; - } - let viewOffset = getParentInjectorViewOffset(location); - // view offset is 1 - let parentView = startView; - let parentTNode = startView[T_HOST] as TElementNode; - // view offset is superior to 1 - while (viewOffset > 1) { - parentView = parentView[DECLARATION_VIEW]!; - parentTNode = parentView[T_HOST] as TElementNode; - viewOffset--; - } - return parentTNode; -} diff --git a/packages/core/src/render3/pipe.ts b/packages/core/src/render3/pipe.ts index 277268fa04..55cfe61864 100644 --- a/packages/core/src/render3/pipe.ts +++ b/packages/core/src/render3/pipe.ts @@ -48,13 +48,17 @@ export function ɵɵpipe(index: number, pipeName: string): any { const pipeFactory = pipeDef.factory || (pipeDef.factory = getFactoryDef(pipeDef.type, true)); const previousInjectImplementation = setInjectImplementation(ɵɵdirectiveInject); + let pipeInstance: any; - // DI for pipes is supposed to behave like directives when placed on a component - // host node, which means that we have to disable access to `viewProviders`. - const previousIncludeViewProviders = setIncludeViewProviders(false); - const pipeInstance = pipeFactory(); - setIncludeViewProviders(previousIncludeViewProviders); - setInjectImplementation(previousInjectImplementation); + try { + // DI for pipes is supposed to behave like directives when placed on a component + // host node, which means that we have to disable access to `viewProviders`. + const previousIncludeViewProviders = setIncludeViewProviders(false); + pipeInstance = pipeFactory(); + setIncludeViewProviders(previousIncludeViewProviders); + } finally { + setInjectImplementation(previousInjectImplementation); + } store(tView, getLView(), index, pipeInstance); return pipeInstance; } diff --git a/packages/core/src/render3/util/discovery_utils.ts b/packages/core/src/render3/util/discovery_utils.ts index 1c66981fc8..ec6913f145 100644 --- a/packages/core/src/render3/util/discovery_utils.ts +++ b/packages/core/src/render3/util/discovery_utils.ts @@ -7,6 +7,7 @@ */ import {Injector} from '../../di/injector'; +import {assertEqual} from '../../util/assert'; import {assertLView} from '../assert'; import {discoverLocalRefs, getComponentAtNodeIndex, getDirectivesAtNodeIndex, getLContext} from '../context_discovery'; import {NodeInjector} from '../di'; @@ -15,7 +16,7 @@ import {LContext} from '../interfaces/context'; import {DirectiveDef} from '../interfaces/definition'; import {TElementNode, TNode, TNodeProviderIndexes} from '../interfaces/node'; import {isLView} from '../interfaces/type_checks'; -import {CLEANUP, CONTEXT, DebugNode, FLAGS, HEADER_OFFSET, HOST, LView, LViewFlags, T_HOST, TVIEW} from '../interfaces/view'; +import {CLEANUP, CONTEXT, DebugNode, FLAGS, HEADER_OFFSET, HOST, LView, LViewFlags, T_HOST, TVIEW, TViewType} from '../interfaces/view'; import {stringifyForError} from './misc_utils'; import {getLViewParent, getRootContext} from './view_traversal_utils'; @@ -101,8 +102,7 @@ export function getOwningComponent(elementOrDir: Element|{}): T|null { let lView = context.lView; let parent: LView|null; ngDevMode && assertLView(lView); - while (lView[HOST] === null && (parent = getLViewParent(lView)!)) { - // As long as lView[HOST] is null we know we are part of sub-template such as `*ngIf` + while (lView[TVIEW].type === TViewType.Embedded && (parent = getLViewParent(lView)!)) { lView = parent; } return lView[FLAGS] & LViewFlags.IsRoot ? null : lView[CONTEXT] as T; @@ -389,6 +389,8 @@ export function getDebugNode(element: Element): DebugNode|null { // data. In this situation the TNode is not accessed at the same spot. const tNode = isLView(valueInLView) ? (valueInLView[T_HOST] as TNode) : getTNode(lView[TVIEW], nodeIndex - HEADER_OFFSET); + ngDevMode && + assertEqual(tNode.index, nodeIndex, 'Expecting that TNode at index is same as index'); debugNode = buildDebugNode(tNode, lView); } diff --git a/packages/core/src/render3/util/injector_utils.ts b/packages/core/src/render3/util/injector_utils.ts index a9e3a94fb2..c8b6835b14 100644 --- a/packages/core/src/render3/util/injector_utils.ts +++ b/packages/core/src/render3/util/injector_utils.ts @@ -5,14 +5,26 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ + +import {assertGreaterThan, assertNotEqual, assertNumber} from '../../util/assert'; import {NO_PARENT_INJECTOR, RelativeInjectorLocation, RelativeInjectorLocationFlags} from '../interfaces/injector'; -import {DECLARATION_VIEW, LView} from '../interfaces/view'; +import {DECLARATION_VIEW, HEADER_OFFSET, LView} from '../interfaces/view'; + + /// Parent Injector Utils /////////////////////////////////////////////////////////////// export function hasParentInjector(parentLocation: RelativeInjectorLocation): boolean { return parentLocation !== NO_PARENT_INJECTOR; } export function getParentInjectorIndex(parentLocation: RelativeInjectorLocation): number { + ngDevMode && assertNumber(parentLocation, 'Number expected'); + ngDevMode && assertNotEqual(parentLocation as any, -1, 'Not a valid state.'); + const parentInjectorIndex = + (parentLocation as any as number) & RelativeInjectorLocationFlags.InjectorIndexMask; + ngDevMode && + assertGreaterThan( + parentInjectorIndex, HEADER_OFFSET, + 'Parent injector must be pointing past HEADER_OFFSET.'); return (parentLocation as any as number) & RelativeInjectorLocationFlags.InjectorIndexMask; } diff --git a/packages/core/src/render3/util/view_utils.ts b/packages/core/src/render3/util/view_utils.ts index 6ead6bb400..f86c85d019 100644 --- a/packages/core/src/render3/util/view_utils.ts +++ b/packages/core/src/render3/util/view_utils.ts @@ -105,10 +105,10 @@ export function getNativeByTNode(tNode: TNode, lView: LView): RNode { * @param tNode * @param lView */ -export function getNativeByTNodeOrNull(tNode: TNode, lView: LView): RNode|null { - const index = tNode.index; +export function getNativeByTNodeOrNull(tNode: TNode|null, lView: LView): RNode|null { + const index = tNode === null ? -1 : tNode.index; if (index !== -1) { - ngDevMode && assertTNodeForLView(tNode, lView); + ngDevMode && assertTNodeForLView(tNode!, lView); const node: RNode|null = unwrapRNode(lView[index]); ngDevMode && node !== null && !isProceduralRenderer(lView[RENDERER]) && assertDomNode(node); return node; @@ -216,6 +216,7 @@ export function updateTransplantedViewCount(lContainer: LContainer, amount: 1|- * crashes on it, so we unwrap it. */ export function getNonViewFirstChild(tView: TView): TNode|null { + // FIXME(misko): Delete me! (as TNodeType.View no longer exists) const firstChild = tView.firstChild; return firstChild === null ? null : (firstChild.type === TNodeType.View ? firstChild.child : firstChild); diff --git a/packages/core/src/render3/view_engine_compatibility.ts b/packages/core/src/render3/view_engine_compatibility.ts index e1958ebf43..6ec59301f2 100644 --- a/packages/core/src/render3/view_engine_compatibility.ts +++ b/packages/core/src/render3/view_engine_compatibility.ts @@ -6,33 +6,33 @@ * found in the LICENSE file at https://angular.io/license */ -import {ChangeDetectorRef as ViewEngine_ChangeDetectorRef} from '../change_detection/change_detector_ref'; -import {Injector} from '../di/injector'; -import {ComponentFactory as viewEngine_ComponentFactory, ComponentRef as viewEngine_ComponentRef} from '../linker/component_factory'; -import {ElementRef as ViewEngine_ElementRef} from '../linker/element_ref'; -import {NgModuleRef as viewEngine_NgModuleRef} from '../linker/ng_module_factory'; -import {TemplateRef as ViewEngine_TemplateRef} from '../linker/template_ref'; -import {ViewContainerRef as ViewEngine_ViewContainerRef} from '../linker/view_container_ref'; -import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, ViewRef as viewEngine_ViewRef} from '../linker/view_ref'; -import {Renderer2} from '../render/api'; -import {addToArray, removeFromArray} from '../util/array_utils'; -import {assertDefined, assertEqual, assertGreaterThan, assertLessThan} from '../util/assert'; +import { ChangeDetectorRef as ViewEngine_ChangeDetectorRef } from '../change_detection/change_detector_ref'; +import { Injector } from '../di/injector'; +import { ComponentFactory as viewEngine_ComponentFactory, ComponentRef as viewEngine_ComponentRef } from '../linker/component_factory'; +import { ElementRef as ViewEngine_ElementRef } from '../linker/element_ref'; +import { NgModuleRef as viewEngine_NgModuleRef } from '../linker/ng_module_factory'; +import { TemplateRef as ViewEngine_TemplateRef } from '../linker/template_ref'; +import { ViewContainerRef as ViewEngine_ViewContainerRef } from '../linker/view_container_ref'; +import { EmbeddedViewRef as viewEngine_EmbeddedViewRef, ViewRef as viewEngine_ViewRef } from '../linker/view_ref'; +import { Renderer2 } from '../render/api'; +import { addToArray, removeFromArray } from '../util/array_utils'; +import { assertDefined, assertEqual, assertGreaterThan, assertLessThan } from '../util/assert'; +import { assertLContainer, assertNodeInjector } from './assert'; +import { getParentInjectorLocation, NodeInjector } from './di'; +import { addToViewTree, createLContainer, createLView, renderView } from './instructions/shared'; +import { CONTAINER_HEADER_OFFSET, LContainer, NATIVE, VIEW_REFS } from './interfaces/container'; +import { TNODE } from './interfaces/injector'; +import { TContainerNode, TDirectiveHostNode, TElementContainerNode, TElementNode, TNode, TNodeType } from './interfaces/node'; +import { isProceduralRenderer, RComment, RElement } from './interfaces/renderer'; +import { isComponentHost, isLContainer, isLView, isRootView } from './interfaces/type_checks'; +import { DECLARATION_COMPONENT_VIEW, DECLARATION_LCONTAINER, LView, LViewFlags, PARENT, QUERIES, RENDERER, TVIEW, TView, T_HOST } from './interfaces/view'; +import { assertNodeOfPossibleTypes } from './node_assert'; +import { addViewToContainer, appendChild, destroyLView, detachView, getBeforeNodeForView, insertView, nativeInsertBefore, nativeNextSibling, nativeParentNode } from './node_manipulation'; +import { getLView, getPreviousOrParentTNode } from './state'; +import { getParentInjectorIndex, getParentInjectorView, hasParentInjector } from './util/injector_utils'; +import { getComponentLViewByIndex, getNativeByTNode, unwrapRNode, viewAttachedToContainer } from './util/view_utils'; +import { ViewRef } from './view_ref'; -import {assertLContainer} from './assert'; -import {getParentInjectorLocation, NodeInjector} from './di'; -import {addToViewTree, createLContainer, createLView, renderView} from './instructions/shared'; -import {CONTAINER_HEADER_OFFSET, LContainer, VIEW_REFS} from './interfaces/container'; -import {TContainerNode, TDirectiveHostNode, TElementContainerNode, TElementNode, TNode, TNodeType} from './interfaces/node'; -import {isProceduralRenderer, RComment, RElement} from './interfaces/renderer'; -import {isComponentHost, isLContainer, isLView, isRootView} from './interfaces/type_checks'; -import {DECLARATION_COMPONENT_VIEW, DECLARATION_LCONTAINER, LView, LViewFlags, PARENT, QUERIES, RENDERER, T_HOST, TVIEW, TView} from './interfaces/view'; -import {assertNodeOfPossibleTypes} from './node_assert'; -import {addRemoveViewFromContainer, appendChild, destroyLView, detachView, getBeforeNodeForView, insertView, nativeInsertBefore, nativeNextSibling, nativeParentNode} from './node_manipulation'; -import {getParentInjectorTNode} from './node_util'; -import {getLView, getPreviousOrParentTNode} from './state'; -import {getParentInjectorView, hasParentInjector} from './util/injector_utils'; -import {getComponentLViewByIndex, getNativeByTNode, unwrapRNode, viewAttachedToContainer} from './util/view_utils'; -import {ViewRef} from './view_ref'; @@ -106,7 +106,7 @@ export function createTemplateRef( const embeddedTView = this._declarationTContainer.tViews as TView; const embeddedLView = createLView( this._declarationView, embeddedTView, context, LViewFlags.CheckAlways, null, - embeddedTView.node); + embeddedTView.node, null, null, null, null); const declarationLContainer = this._declarationView[this._declarationTContainer.index]; ngDevMode && assertLContainer(declarationLContainer); @@ -188,12 +188,15 @@ export function createContainerRef( /** @deprecated No replacement */ get parentInjector(): Injector { const parentLocation = getParentInjectorLocation(this._hostTNode, this._hostView); - const parentView = getParentInjectorView(parentLocation, this._hostView); - const parentTNode = getParentInjectorTNode(parentLocation, this._hostView, this._hostTNode); - - return !hasParentInjector(parentLocation) || parentTNode == null ? - new NodeInjector(null, this._hostView) : - new NodeInjector(parentTNode, parentView); + if (hasParentInjector(parentLocation)) { + const parentView = getParentInjectorView(parentLocation, this._hostView); + const injectorIndex = getParentInjectorIndex(parentLocation); + ngDevMode && assertNodeInjector(parentView, injectorIndex); + const parentTNode = parentView[TVIEW].data[injectorIndex + TNODE] as TElementNode; + return new NodeInjector(parentTNode, parentView); + } else { + return new NodeInjector(null, this._hostView); + } } clear(): void { @@ -277,14 +280,21 @@ export function createContainerRef( } } + // Logical operation of adding `LView` to `LContainer` const adjustedIdx = this._adjustIndex(index); - insertView(tView, lView, this._lContainer, adjustedIdx); + const lContainer = this._lContainer; + insertView(tView, lView, lContainer, adjustedIdx); - const beforeNode = getBeforeNodeForView(adjustedIdx, this._lContainer); - addRemoveViewFromContainer(tView, lView, true, beforeNode); + // Physical operation of adding the DOM nodes. + const beforeNode = getBeforeNodeForView(adjustedIdx, lContainer); + const renderer = lView[RENDERER]; + const renderParent = nativeParentNode(renderer, lContainer[NATIVE] as RElement | RComment); + if (renderParent !== null) { + addViewToContainer(tView, lContainer[T_HOST], renderer, lView, renderParent, beforeNode); + } (viewRef as ViewRef).attachToViewContainerRef(this); - addToArray(this._lContainer[VIEW_REFS]!, adjustedIdx, viewRef); + addToArray(lContainer[VIEW_REFS]!, adjustedIdx, viewRef); return viewRef; } diff --git a/packages/core/src/render3/view_ref.ts b/packages/core/src/render3/view_ref.ts index 6641177f9f..688d4774de 100644 --- a/packages/core/src/render3/view_ref.ts +++ b/packages/core/src/render3/view_ref.ts @@ -35,11 +35,8 @@ export class ViewRef implements viewEngine_EmbeddedViewRef, viewEngine_Int get rootNodes(): any[] { const lView = this._lView; - if (lView[HOST] == null) { - const hostTView = lView[T_HOST] as TViewNode; - return collectNativeNodes(lView[TVIEW], lView, hostTView.child, []); - } - return []; + const tView = lView[TVIEW]; + return collectNativeNodes(tView, lView, tView.firstChild, []); } constructor( diff --git a/packages/core/test/acceptance/di_spec.ts b/packages/core/test/acceptance/di_spec.ts index 4541db80cb..27b0e15c08 100644 --- a/packages/core/test/acceptance/di_spec.ts +++ b/packages/core/test/acceptance/di_spec.ts @@ -7,7 +7,7 @@ */ import {CommonModule} from '@angular/common'; -import {Attribute, ChangeDetectorRef, Component, ComponentFactoryResolver, ComponentRef, Directive, ElementRef, EventEmitter, forwardRef, Host, HostBinding, Inject, Injectable, InjectionToken, INJECTOR, Injector, Input, LOCALE_ID, ModuleWithProviders, NgModule, NgZone, Optional, Output, Pipe, PipeTransform, Self, SkipSelf, TemplateRef, ViewChild, ViewContainerRef, ViewRef, ɵDEFAULT_LOCALE_ID as DEFAULT_LOCALE_ID} from '@angular/core'; +import {Attribute, ChangeDetectorRef, Component, ComponentFactoryResolver, ComponentRef, Directive, ElementRef, EventEmitter, forwardRef, Host, HostBinding, Inject, Injectable, InjectionToken, INJECTOR, Injector, Input, LOCALE_ID, NgModule, NgZone, Optional, Output, Pipe, PipeTransform, Self, SkipSelf, TemplateRef, ViewChild, ViewContainerRef, ViewRef, ɵDEFAULT_LOCALE_ID as DEFAULT_LOCALE_ID} from '@angular/core'; import {ɵINJECTOR_SCOPE} from '@angular/core/src/core'; import {ViewRef as ViewRefInternal} from '@angular/core/src/render3/view_ref'; import {TestBed} from '@angular/core/testing'; diff --git a/packages/core/test/acceptance/integration_spec.ts b/packages/core/test/acceptance/integration_spec.ts index 9cef7b2cf6..5d07cbcefa 100644 --- a/packages/core/test/acceptance/integration_spec.ts +++ b/packages/core/test/acceptance/integration_spec.ts @@ -53,7 +53,7 @@ describe('acceptance integration tests', () => { expect(fixture.nativeElement.innerHTML).toEqual('

Hello, World!

'); onlyInIvy('perf counters').expectPerfCounters({ tView: 2, // Host view + App - tNode: 4, // Host Node + App Node + + #text + tNode: 3, // Host Node +

+ #text }); fixture.componentInstance.name = 'New World'; @@ -63,7 +63,7 @@ describe('acceptance integration tests', () => { // Assert that the tView/tNode count does not increase (they are correctly cached) onlyInIvy('perf counters').expectPerfCounters({ tView: 2, - tNode: 4, + tNode: 3, }); }); }); diff --git a/packages/core/test/acceptance/styling_spec.ts b/packages/core/test/acceptance/styling_spec.ts index bea42b397c..42e4934f9b 100644 --- a/packages/core/test/acceptance/styling_spec.ts +++ b/packages/core/test/acceptance/styling_spec.ts @@ -797,7 +797,7 @@ describe('styling', () => { onlyInIvy('perf counters').expectPerfCounters({ rendererSetStyle: 1, - tNode: 3, + tNode: 2, }); }); diff --git a/packages/core/test/bundling/forms/bundle.golden_symbols.json b/packages/core/test/bundling/forms/bundle.golden_symbols.json index 68600167d0..f474a0e612 100644 --- a/packages/core/test/bundling/forms/bundle.golden_symbols.json +++ b/packages/core/test/bundling/forms/bundle.golden_symbols.json @@ -704,9 +704,6 @@ { "name": "addHostBindingsToExpandoInstructions" }, - { - "name": "addRemoveViewFromContainer" - }, { "name": "addToArray" }, @@ -821,9 +818,6 @@ { "name": "createPlatformFactory" }, - { - "name": "createTNode" - }, { "name": "createTView" }, @@ -974,9 +968,6 @@ { "name": "getConstant" }, - { - "name": "getContainerRenderParent" - }, { "name": "getDOM" }, @@ -1064,9 +1055,6 @@ { "name": "getParentInjectorView" }, - { - "name": "getParentInjectorViewOffset" - }, { "name": "getParentState" }, diff --git a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json index 0777fe91e6..afbed9a415 100644 --- a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json @@ -59,9 +59,6 @@ { "name": "allocLFrame" }, - { - "name": "appendChild" - }, { "name": "attachPatchData" }, @@ -110,6 +107,9 @@ { "name": "extractPipeDef" }, + { + "name": "getBeforeNodeForView" + }, { "name": "getCheckNoChangesMode" }, @@ -235,5 +235,8 @@ }, { "name": "viewAttachedToChangeDetector" + }, + { + "name": "ɵɵtext" } ] \ No newline at end of file diff --git a/packages/core/test/bundling/router/bundle.golden_symbols.json b/packages/core/test/bundling/router/bundle.golden_symbols.json index 5eeba4b3f5..88b6a1db64 100644 --- a/packages/core/test/bundling/router/bundle.golden_symbols.json +++ b/packages/core/test/bundling/router/bundle.golden_symbols.json @@ -926,9 +926,6 @@ { "name": "addHostBindingsToExpandoInstructions" }, - { - "name": "addRemoveViewFromContainer" - }, { "name": "addToArray" }, @@ -1070,9 +1067,6 @@ { "name": "createRouterScroller" }, - { - "name": "createTNode" - }, { "name": "createTView" }, @@ -1286,9 +1280,6 @@ { "name": "getConstant" }, - { - "name": "getContainerRenderParent" - }, { "name": "getCurrentQueryIndex" }, @@ -1394,9 +1385,6 @@ { "name": "getParentInjectorView" }, - { - "name": "getParentInjectorViewOffset" - }, { "name": "getParentState" }, diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json index 96dee27a67..30cd8d19a4 100644 --- a/packages/core/test/bundling/todo/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json @@ -179,9 +179,6 @@ { "name": "addHostBindingsToExpandoInstructions" }, - { - "name": "addRemoveViewFromContainer" - }, { "name": "addToArray" }, @@ -260,9 +257,6 @@ { "name": "createLView" }, - { - "name": "createTNode" - }, { "name": "createTView" }, @@ -353,9 +347,6 @@ { "name": "getConstant" }, - { - "name": "getContainerRenderParent" - }, { "name": "getDebugContext" }, @@ -425,9 +416,6 @@ { "name": "getParentInjectorView" }, - { - "name": "getParentInjectorViewOffset" - }, { "name": "getParentState" }, diff --git a/packages/core/test/render3/di_spec.ts b/packages/core/test/render3/di_spec.ts index 4343611162..2aa775ecc0 100644 --- a/packages/core/test/render3/di_spec.ts +++ b/packages/core/test/render3/di_spec.ts @@ -226,8 +226,8 @@ describe('di', () => { describe('getOrCreateNodeInjector', () => { it('should handle initial undefined state', () => { const contentView = createLView( - null, createTView(TViewType.Component, -1, null, 1, 0, null, null, null, null, null), {}, - LViewFlags.CheckAlways, null, null, {} as any, {} as any); + null, createTView(TViewType.Component, null, null, 1, 0, null, null, null, null, null), + {}, LViewFlags.CheckAlways, null, null, {} as any, {} as any, null, null); enterView(contentView); try { const parentTNode = getOrCreateTNode(contentView[TVIEW], 0, TNodeType.Element, null, null); diff --git a/packages/core/test/render3/instructions/lview_debug_spec.ts b/packages/core/test/render3/instructions/lview_debug_spec.ts index bd35fa870e..5205e3e136 100644 --- a/packages/core/test/render3/instructions/lview_debug_spec.ts +++ b/packages/core/test/render3/instructions/lview_debug_spec.ts @@ -26,7 +26,7 @@ describe('lView_debug', () => { let tNode!: TNodeDebug; let tView!: TView; beforeEach(() => { - tView = createTView(TViewType.Component, 0, null, 0, 0, null, null, null, null, null); + tView = createTView(TViewType.Component, null, null, 0, 0, null, null, null, null, null); tNode = createTNode(tView, null!, TNodeType.Element, 0, '', null) as TNodeDebug; }); afterEach(() => tNode = tView = null!); diff --git a/packages/core/test/render3/instructions/shared_spec.ts b/packages/core/test/render3/instructions/shared_spec.ts index 5b3c2af38c..8af4ecc973 100644 --- a/packages/core/test/render3/instructions/shared_spec.ts +++ b/packages/core/test/render3/instructions/shared_spec.ts @@ -36,7 +36,7 @@ export function enterViewWithOneDiv() { const vars = 60; // Space for directive expando, template, component + 3 directives if we assume // that each consume 10 slots. const tView = createTView( - TViewType.Component, -1, emptyTemplate, consts, vars, null, null, null, null, null); + TViewType.Component, null, emptyTemplate, consts, vars, null, null, null, null, null); // Just assume that the expando starts after 10 initial bindings. tView.expandoStartIndex = HEADER_OFFSET + 10; const tNode = tView.firstChild = createTNode(tView, null!, TNodeType.Element, 0, 'div', null); diff --git a/packages/core/test/render3/is_shape_of.ts b/packages/core/test/render3/is_shape_of.ts index 86bbf66b86..7096136f55 100644 --- a/packages/core/test/render3/is_shape_of.ts +++ b/packages/core/test/render3/is_shape_of.ts @@ -91,7 +91,6 @@ export function isTView(obj: any): obj is TView { } const ShapeOfTView: ShapeOf = { type: true, - id: true, blueprint: true, template: true, viewQuery: true, diff --git a/packages/core/test/render3/matchers_spec.ts b/packages/core/test/render3/matchers_spec.ts index 10170064ee..f873a124bf 100644 --- a/packages/core/test/render3/matchers_spec.ts +++ b/packages/core/test/render3/matchers_spec.ts @@ -52,7 +52,7 @@ describe('render3 matchers', () => { }); describe('matchTView', () => { - const tView = createTView(TViewType.Root, 1, null, 2, 3, null, null, null, null, null); + const tView = createTView(TViewType.Root, null, null, 2, 3, null, null, null, null, null); it('should match', () => { expect(tView).toEqual(matchTView()); expect(tView).toEqual(matchTView({type: TViewType.Root})); @@ -60,7 +60,7 @@ describe('render3 matchers', () => { }); }); describe('matchTNode', () => { - const tView = createTView(TViewType.Root, 1, null, 2, 3, null, null, null, null, null); + const tView = createTView(TViewType.Root, null, null, 2, 3, null, null, null, null, null); const tNode = createTNode(tView, null, TNodeType.Element, 1, 'tagName', []); it('should match', () => { diff --git a/packages/core/test/render3/perf/directive_instantiate/index.ts b/packages/core/test/render3/perf/directive_instantiate/index.ts index dd3bb779dc..d27206f88a 100644 --- a/packages/core/test/render3/perf/directive_instantiate/index.ts +++ b/packages/core/test/render3/perf/directive_instantiate/index.ts @@ -75,12 +75,12 @@ function testTemplate(rf: RenderFlags, ctx: any) { } const rootLView = createLView( - null, createTView(TViewType.Root, -1, null, 0, 0, null, null, null, null, null), {}, - LViewFlags.IsRoot, null, null); + null, createTView(TViewType.Root, null, null, 0, 0, null, null, null, null, null), {}, + LViewFlags.IsRoot, null, null, null, null, null, null); const viewTNode = createTNode(null!, null, TNodeType.View, -1, null, null) as TViewNode; const embeddedTView = createTView( - TViewType.Embedded, -1, testTemplate, 21, 10, [Tooltip.ɵdir], null, null, null, + TViewType.Embedded, null, testTemplate, 21, 10, [Tooltip.ɵdir], null, null, null, [['position', 'top', 3, 'tooltip']]); // create view once so we don't profile the first create pass diff --git a/packages/core/test/render3/perf/element_text_create/index.ts b/packages/core/test/render3/perf/element_text_create/index.ts index fe77846e4c..b076c8ebd7 100644 --- a/packages/core/test/render3/perf/element_text_create/index.ts +++ b/packages/core/test/render3/perf/element_text_create/index.ts @@ -64,12 +64,12 @@ function testTemplate(rf: RenderFlags, ctx: any) { } const rootLView = createLView( - null, createTView(TViewType.Root, -1, null, 0, 0, null, null, null, null, null), {}, - LViewFlags.IsRoot, null, null); + null, createTView(TViewType.Root, null, null, 0, 0, null, null, null, null, null), {}, + LViewFlags.IsRoot, null, null, null, null, null, null); const viewTNode = createTNode(null!, null, TNodeType.View, -1, null, null) as TViewNode; const embeddedTView = createTView( - TViewType.Embedded, -1, testTemplate, 21, 0, null, null, null, null, [[ + TViewType.Embedded, null, testTemplate, 21, 0, null, null, null, null, [[ 'name1', 'value1', 'name2', 'value2', 'name3', 'value3', 'name4', 'value4', 'name5', 'value5' ]]); diff --git a/packages/core/test/render3/perf/listeners/index.ts b/packages/core/test/render3/perf/listeners/index.ts index 43730d6d11..3bd64a4346 100644 --- a/packages/core/test/render3/perf/listeners/index.ts +++ b/packages/core/test/render3/perf/listeners/index.ts @@ -66,12 +66,12 @@ function testTemplate(rf: RenderFlags, ctx: any) { } const rootLView = createLView( - null, createTView(TViewType.Root, -1, null, 0, 0, null, null, null, null, null), {}, - LViewFlags.IsRoot, null, null); + null, createTView(TViewType.Root, null, null, 0, 0, null, null, null, null, null), {}, + LViewFlags.IsRoot, null, null, null, null, null, null); const viewTNode = createTNode(null!, null, TNodeType.View, -1, null, null) as TViewNode; const embeddedTView = createTView( - TViewType.Embedded, -1, testTemplate, 11, 0, null, null, null, null, [[3, 'click', 'input']]); + TViewType.Embedded, null, testTemplate, 11, 0, null, null, null, null, [[3, 'click', 'input']]); // create view once so we don't profile the first create pass createAndRenderLView(rootLView, embeddedTView, viewTNode); diff --git a/packages/core/test/render3/perf/ng_template/index.ts b/packages/core/test/render3/perf/ng_template/index.ts index ecc268f6e4..fe8153dc64 100644 --- a/packages/core/test/render3/perf/ng_template/index.ts +++ b/packages/core/test/render3/perf/ng_template/index.ts @@ -59,12 +59,12 @@ function testTemplate(rf: RenderFlags, ctx: any) { } const rootLView = createLView( - null, createTView(TViewType.Root, -1, null, 0, 0, null, null, null, null, null), {}, - LViewFlags.IsRoot, null, null); + null, createTView(TViewType.Root, null, null, 0, 0, null, null, null, null, null), {}, + LViewFlags.IsRoot, null, null, null, null, null, null); const viewTNode = createTNode(null!, null, TNodeType.View, -1, null, null) as TViewNode; const embeddedTView = createTView( - TViewType.Root, -1, testTemplate, 2, 0, [NgIfLike.ɵdir], null, null, null, + TViewType.Root, null, testTemplate, 2, 0, [NgIfLike.ɵdir], null, null, null, [['viewManipulation', '']]); // create view once so we don't profile first template pass diff --git a/packages/core/test/render3/perf/setup.ts b/packages/core/test/render3/perf/setup.ts index 9ad6fd2a8e..6ac22272e2 100644 --- a/packages/core/test/render3/perf/setup.ts +++ b/packages/core/test/render3/perf/setup.ts @@ -22,7 +22,8 @@ const renderer = rendererFactory.createRenderer(null, null); export function createAndRenderLView( parentLView: LView, tView: TView, hostTNode: TViewNode): LView { const embeddedLView = createLView( - parentLView, tView, {}, LViewFlags.CheckAlways, null, hostTNode, rendererFactory, renderer); + parentLView, tView, {}, LViewFlags.CheckAlways, null, hostTNode, rendererFactory, renderer, + null, null); renderView(tView, embeddedLView, null); return embeddedLView; } @@ -49,12 +50,12 @@ export function setupTestHarness( embeddedViewContext: any = {}, consts: TAttributes[]|null = null, directiveRegistry: DirectiveDefList|null = null): TestHarness { // Create a root view with a container - const hostTView = createTView(TViewType.Root, -1, null, 1, 0, null, null, null, null, consts); + const hostTView = createTView(TViewType.Root, null, null, 1, 0, null, null, null, null, consts); const tContainerNode = getOrCreateTNode(hostTView, 0, TNodeType.Container, null, null); const hostNode = renderer.createElement('div'); const hostLView = createLView( null, hostTView, {}, LViewFlags.CheckAlways | LViewFlags.IsRoot, hostNode, null, - rendererFactory, renderer); + rendererFactory, renderer, null, null); const mockRCommentNode = renderer.createComment(''); const lContainer = createLContainer(mockRCommentNode, hostLView, mockRCommentNode, tContainerNode); @@ -63,13 +64,14 @@ export function setupTestHarness( // create test embedded views const embeddedTView = createTView( - TViewType.Embedded, -1, templateFn, decls, vars, directiveRegistry, null, null, null, consts); + TViewType.Embedded, null, templateFn, decls, vars, directiveRegistry, null, null, null, + consts); const viewTNode = createTNode(hostTView, null, TNodeType.View, -1, null, null) as TViewNode; function createEmbeddedLView(): LView { const embeddedLView = createLView( hostLView, embeddedTView, embeddedViewContext, LViewFlags.CheckAlways, null, viewTNode, - rendererFactory, renderer); + rendererFactory, renderer, null, null); renderView(embeddedTView, embeddedLView, embeddedViewContext); return embeddedLView; } diff --git a/packages/core/test/render3/perf/view_destroy_hook/index.ts b/packages/core/test/render3/perf/view_destroy_hook/index.ts index fb84ea4831..b0dfd5059a 100644 --- a/packages/core/test/render3/perf/view_destroy_hook/index.ts +++ b/packages/core/test/render3/perf/view_destroy_hook/index.ts @@ -52,12 +52,12 @@ function testTemplate(rf: RenderFlags, ctx: any) { } const rootLView = createLView( - null, createTView(TViewType.Root, -1, null, 0, 0, null, null, null, null, null), {}, - LViewFlags.IsRoot, null, null); + null, createTView(TViewType.Root, null, null, 0, 0, null, null, null, null, null), {}, + LViewFlags.IsRoot, null, null, null, null, null, null); const viewTNode = createTNode(null!, null, TNodeType.View, -1, null, null) as TViewNode; const embeddedTView = createTView( - TViewType.Embedded, -1, testTemplate, 21, 10, [ToDestroy.ɵdir], null, null, null, + TViewType.Embedded, null, testTemplate, 21, 10, [ToDestroy.ɵdir], null, null, null, [['to-destroy']]); // create view once so we don't profile the first create pass diff --git a/packages/core/test/render3/render_util.ts b/packages/core/test/render3/render_util.ts index 342e790d7f..9b3de3fa2a 100644 --- a/packages/core/test/render3/render_util.ts +++ b/packages/core/test/render3/render_util.ts @@ -261,10 +261,10 @@ export function renderTemplate( const renderer = providedRendererFactory.createRenderer(null, null); // We need to create a root view so it's possible to look up the host element through its index - const tView = createTView(TViewType.Root, -1, null, 1, 0, null, null, null, null, null); + const tView = createTView(TViewType.Root, null, null, 1, 0, null, null, null, null, null); const hostLView = createLView( null, tView, {}, LViewFlags.CheckAlways | LViewFlags.IsRoot, null, null, - providedRendererFactory, renderer); + providedRendererFactory, renderer, null, null); enterView(hostLView); const def: ComponentDef = ɵɵdefineComponent({ @@ -282,7 +282,7 @@ export function renderTemplate( hostLView[hostTNode.index] = hostNode; componentView = createLView( hostLView, componentTView, context, LViewFlags.CheckAlways, hostNode, hostTNode, - providedRendererFactory, renderer, sanitizer); + providedRendererFactory, renderer, sanitizer || null, null); } renderComponentOrTemplate(componentView[TVIEW], componentView, templateFn, context); return componentView; diff --git a/packages/core/test/render3/view_utils_spec.ts b/packages/core/test/render3/view_utils_spec.ts index a7abf74d78..b7f30fdc36 100644 --- a/packages/core/test/render3/view_utils_spec.ts +++ b/packages/core/test/render3/view_utils_spec.ts @@ -13,7 +13,7 @@ import {TViewType} from '@angular/core/src/render3/interfaces/view'; describe('view_utils', () => { it('should verify unwrap methods', () => { const div = document.createElement('div'); - const tView = createTView(TViewType.Root, 0, null, 0, 0, null, null, null, null, null); + const tView = createTView(TViewType.Root, null, null, 0, 0, null, null, null, null, null); const lView = createLView(null, tView, {}, 0, div, null, {} as any, {} as any, null, null); const tNode = createTNode(null!, null, 3, 0, 'div', []); const lContainer = createLContainer(lView, lView, div, tNode);