From c25503b142e3f66bf8f68cd159c6c0160a35fcea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=A1ko=20Hevery?= Date: Mon, 28 Oct 2019 12:08:17 -0700 Subject: [PATCH] test(ivy): Have more descriptive names for `LView` (#33449) When debugging `LView`s it is easy to get lost since all of them have the same name. This change does three things: 1. It makes `TView` have an explicit type: - `Host`: for the top level `TView` for bootstrap - `Component`: for the `TView` which represents components template - `Embedded`: for the `TView` which represents an embedded template 2. It changes the name of `LView` to `LHostView`, `LComponentView`, and `LEmbeddedView` depending on the `TView` type. 3. For `LComponentView` and `LEmbeddedView` we also append the name of of the `context` constructor. The result is that we have `LView`s which are name as: `LComponentView_MyComponent` and `LEmbeddedView_NgIfContext`. The above changes will make it easier to understand the structure of the application when debugging. NOTE: All of these are behind `ngDevMode` and will get removed in production application. PR Close #33449 --- packages/core/src/render3/component.ts | 11 ++-- packages/core/src/render3/component_ref.ts | 4 +- .../src/render3/instructions/container.ts | 6 +- .../src/render3/instructions/embedded_view.ts | 6 +- .../src/render3/instructions/lview_debug.ts | 66 +++++++++++++++---- .../core/src/render3/instructions/shared.ts | 23 ++++--- packages/core/src/render3/interfaces/view.ts | 29 ++++++++ .../core/src/render3/util/discovery_utils.ts | 17 +++++ packages/core/src/util/named_array_type.ts | 1 - .../test/acceptance/discover_utils_spec.ts | 18 ++++- .../test/acceptance/ngdevmode_debug_spec.ts | 49 ++++++++++++++ .../cyclic_import/bundle.golden_symbols.json | 4 +- .../hello_world/bundle.golden_symbols.json | 4 +- .../bundling/todo/bundle.golden_symbols.json | 4 +- packages/core/test/render3/di_spec.ts | 4 +- .../perf/directive_instantiate/index.ts | 3 +- .../render3/perf/element_text_create/index.ts | 8 ++- .../core/test/render3/perf/listeners/index.ts | 5 +- .../test/render3/perf/ng_template/index.ts | 5 +- packages/core/test/render3/perf/setup.ts | 7 +- packages/core/test/render3/render_util.ts | 12 ++-- packages/core/test/render3/view_utils_spec.ts | 3 +- 22 files changed, 227 insertions(+), 62 deletions(-) create mode 100644 packages/core/test/acceptance/ngdevmode_debug_spec.ts diff --git a/packages/core/src/render3/component.ts b/packages/core/src/render3/component.ts index 58e5491c75..8fc900c9e0 100644 --- a/packages/core/src/render3/component.ts +++ b/packages/core/src/render3/component.ts @@ -17,12 +17,12 @@ import {assertComponentType} from './assert'; import {getComponentDef} from './definition'; import {diPublicInInjector, getOrCreateNodeInjectorForNode} from './di'; import {registerPostOrderHooks, registerPreOrderHooks} from './hooks'; -import {CLEAN_PROMISE, addToViewTree, createLView, createTView, getOrCreateTNode, getOrCreateTView, initNodeFlags, instantiateRootComponent, invokeHostBindingsInCreationMode, locateHostElement, markAsComponentHost, refreshView, renderView} from './instructions/shared'; +import {CLEAN_PROMISE, addToViewTree, createLView, createTView, getOrCreateTComponentView, getOrCreateTNode, initNodeFlags, instantiateRootComponent, invokeHostBindingsInCreationMode, locateHostElement, markAsComponentHost, refreshView, renderView} from './instructions/shared'; import {ComponentDef, ComponentType, RenderFlags} from './interfaces/definition'; import {TElementNode, TNode, TNodeType} from './interfaces/node'; import {PlayerHandler} from './interfaces/player'; import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from './interfaces/renderer'; -import {CONTEXT, HEADER_OFFSET, LView, LViewFlags, RootContext, RootContextFlags, TVIEW} from './interfaces/view'; +import {CONTEXT, HEADER_OFFSET, LView, LViewFlags, RootContext, RootContextFlags, TVIEW, TViewType} from './interfaces/view'; import {enterView, getPreviousOrParentTNode, incrementActiveDirectiveId, leaveView, setActiveHostElement} from './state'; import {publishDefaultGlobalUtils} from './util/global_utils'; import {defaultScheduler, stringifyForError} from './util/misc_utils'; @@ -124,7 +124,7 @@ export function renderComponent( const rootContext = createRootContext(opts.scheduler, opts.playerHandler); const renderer = rendererFactory.createRenderer(hostRNode, componentDef); - const rootTView = createTView(-1, null, 1, 0, null, null, null, null, null); + const rootTView = createTView(TViewType.Root, -1, null, 1, 0, null, null, null, null, null); const rootView: LView = createLView( null, rootTView, rootContext, rootFlags, null, null, rendererFactory, renderer, undefined, opts.injector || null); @@ -171,8 +171,9 @@ export function createRootComponentView( rootView[0 + HEADER_OFFSET] = rNode; const tNode: TElementNode = getOrCreateTNode(tView, null, 0, TNodeType.Element, null, null); const componentView = createLView( - rootView, getOrCreateTView(def), null, def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, - rootView[HEADER_OFFSET], tNode, rendererFactory, renderer, sanitizer); + rootView, getOrCreateTComponentView(def), null, + def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, rootView[HEADER_OFFSET], tNode, + rendererFactory, renderer, sanitizer); 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 3fdb9fcaa1..287498c0d9 100644 --- a/packages/core/src/render3/component_ref.ts +++ b/packages/core/src/render3/component_ref.ts @@ -28,7 +28,7 @@ import {assignTViewNodeToLView, createLView, createTView, elementCreate, locateH import {ComponentDef} from './interfaces/definition'; import {TContainerNode, TElementContainerNode, TElementNode} from './interfaces/node'; import {RNode, RendererFactory3, domRendererFactory3, isProceduralRenderer} from './interfaces/renderer'; -import {LView, LViewFlags, TVIEW} from './interfaces/view'; +import {LView, LViewFlags, TVIEW, TViewType} from './interfaces/view'; import {enterView, leaveView} from './state'; import {defaultScheduler} from './util/misc_utils'; import {getTNode} from './util/view_utils'; @@ -158,7 +158,7 @@ export class ComponentFactory extends viewEngine_ComponentFactory { } // Create the root view. Uses empty TView and ContentTemplate. - const rootTView = createTView(-1, null, 1, 0, null, null, null, null, null); + const rootTView = createTView(TViewType.Root, -1, null, 1, 0, null, null, null, null, null); const rootLView = createLView( null, rootTView, rootContext, rootFlags, null, null, rendererFactory, renderer, sanitizer, rootViewInjector); diff --git a/packages/core/src/render3/instructions/container.ts b/packages/core/src/render3/instructions/container.ts index 7cf718e86d..d481f8ebe2 100644 --- a/packages/core/src/render3/instructions/container.ts +++ b/packages/core/src/render3/instructions/container.ts @@ -13,7 +13,7 @@ import {ACTIVE_INDEX, CONTAINER_HEADER_OFFSET, LContainer} from '../interfaces/c import {ComponentTemplate} from '../interfaces/definition'; import {LocalRefExtractor, TAttributes, TContainerNode, TNode, TNodeType, TViewNode} from '../interfaces/node'; import {isDirectiveHost} from '../interfaces/type_checks'; -import {FLAGS, HEADER_OFFSET, InitPhaseState, LView, LViewFlags, RENDERER, TVIEW, T_HOST} from '../interfaces/view'; +import {FLAGS, HEADER_OFFSET, InitPhaseState, LView, LViewFlags, RENDERER, TVIEW, TViewType, T_HOST} from '../interfaces/view'; import {assertNodeType} from '../node_assert'; import {appendChild, removeView} from '../node_manipulation'; import {getBindingIndex, getCheckNoChangesMode, getIsParent, getLView, getPreviousOrParentTNode, setIsNotParent, setPreviousOrParentTNode} from '../state'; @@ -81,8 +81,8 @@ export function ɵɵtemplate( registerPostOrderHooks(tView, tContainerNode); const embeddedTView = tContainerNode.tViews = createTView( - -1, templateFn, decls, vars, tView.directiveRegistry, tView.pipeRegistry, null, - tView.schemas, tViewConsts); + TViewType.Embedded, -1, templateFn, decls, vars, tView.directiveRegistry, + tView.pipeRegistry, null, tView.schemas, tViewConsts); const embeddedTViewNode = createTNode(tView, null, TNodeType.View, -1, null, null) as TViewNode; embeddedTViewNode.injectorIndex = tContainerNode.injectorIndex; embeddedTView.node = embeddedTViewNode; diff --git a/packages/core/src/render3/instructions/embedded_view.ts b/packages/core/src/render3/instructions/embedded_view.ts index 72bafd6a35..c94a38e752 100644 --- a/packages/core/src/render3/instructions/embedded_view.ts +++ b/packages/core/src/render3/instructions/embedded_view.ts @@ -11,7 +11,7 @@ import {assertLContainerOrUndefined} from '../assert'; import {ACTIVE_INDEX, CONTAINER_HEADER_OFFSET, LContainer} from '../interfaces/container'; import {RenderFlags} from '../interfaces/definition'; import {TContainerNode, TNodeType} from '../interfaces/node'; -import {CONTEXT, LView, LViewFlags, PARENT, TVIEW, TView, T_HOST} from '../interfaces/view'; +import {CONTEXT, LView, LViewFlags, PARENT, TVIEW, TView, TViewType, T_HOST} from '../interfaces/view'; import {assertNodeType} from '../node_assert'; import {insertView, removeView} from '../node_manipulation'; import {enterView, getIsParent, getLView, getPreviousOrParentTNode, leaveViewProcessExit, setIsParent, setPreviousOrParentTNode} from '../state'; @@ -87,8 +87,8 @@ function getOrCreateEmbeddedTView( ngDevMode && assertEqual(Array.isArray(containerTViews), true, 'TViews should be in an array'); if (viewIndex >= containerTViews.length || containerTViews[viewIndex] == null) { containerTViews[viewIndex] = createTView( - viewIndex, null, decls, vars, tView.directiveRegistry, tView.pipeRegistry, null, null, - tView.consts); + TViewType.Embedded, viewIndex, null, decls, vars, tView.directiveRegistry, + tView.pipeRegistry, null, null, tView.consts); } return containerTViews[viewIndex]; } diff --git a/packages/core/src/render3/instructions/lview_debug.ts b/packages/core/src/render3/instructions/lview_debug.ts index 78ce66d8e4..3f3e420f3c 100644 --- a/packages/core/src/render3/instructions/lview_debug.ts +++ b/packages/core/src/render3/instructions/lview_debug.ts @@ -7,7 +7,7 @@ */ import {AttributeMarker, ComponentTemplate} from '..'; -import {SchemaMetadata} from '../../core'; +import {SchemaMetadata, Type} from '../../core'; import {assertDefined} from '../../util/assert'; import {createNamedArrayType} from '../../util/named_array_type'; import {initNgDevMode} from '../../util/ng_dev_mode'; @@ -19,7 +19,7 @@ import {SelectorFlags} from '../interfaces/projection'; import {TQueries} from '../interfaces/query'; import {RComment, RElement, RNode} from '../interfaces/renderer'; import {TStylingContext} from '../interfaces/styling'; -import {CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_VIEW, ExpandoInstructions, FLAGS, HEADER_OFFSET, HOST, HookData, INJECTOR, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, RENDERER_FACTORY, SANITIZER, TData, TVIEW, TView as ITView, TView, T_HOST} from '../interfaces/view'; +import {CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_VIEW, ExpandoInstructions, FLAGS, HEADER_OFFSET, HOST, HookData, INJECTOR, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, RENDERER_FACTORY, SANITIZER, TData, TVIEW, TView as ITView, TView, TViewType, T_HOST} from '../interfaces/view'; import {DebugNodeStyling, NodeStylingDebug} from '../styling/styling_debug'; import {attachDebugObject} from '../util/debug_utils'; import {isStylingContext} from '../util/styling_utils'; @@ -56,25 +56,64 @@ const NG_DEV_MODE = ((typeof ngDevMode === 'undefined' || !!ngDevMode) && initNg * ``` */ -export const LViewArray = NG_DEV_MODE && createNamedArrayType('LView') || null !as ArrayConstructor; -let LVIEW_EMPTY: unknown[]; // can't initialize here or it will not be tree shaken, because `LView` - // constructor could have side-effects. +let LVIEW_COMPONENT_CACHE !: Map>; +let LVIEW_EMBEDDED_CACHE !: Map>; +let LVIEW_ROOT !: Array; + +interface TViewDebug extends ITView { + type: TViewType; +} + /** * This function clones a blueprint and creates LView. * * Simple slice will keep the same type, and we need it to be LView */ -export function cloneToLView(list: any[]): LView { - if (LVIEW_EMPTY === undefined) LVIEW_EMPTY = new LViewArray(); - return LVIEW_EMPTY.concat(list) as any; +export function cloneToLViewFromTViewBlueprint(tView: TView): LView { + const debugTView = tView as TViewDebug; + const lView = getLViewToClone(debugTView.type, tView.template && tView.template.name); + return lView.concat(tView.blueprint) as any; +} + +function getLViewToClone(type: TViewType, name: string | null): Array { + switch (type) { + case TViewType.Root: + if (LVIEW_ROOT === undefined) LVIEW_ROOT = new (createNamedArrayType('LRootView'))(); + return LVIEW_ROOT; + case TViewType.Component: + if (LVIEW_COMPONENT_CACHE === undefined) LVIEW_COMPONENT_CACHE = new Map(); + let componentArray = LVIEW_COMPONENT_CACHE.get(name); + if (componentArray === undefined) { + componentArray = new (createNamedArrayType('LComponentView' + nameSuffix(name)))(); + LVIEW_COMPONENT_CACHE.set(name, componentArray); + } + return componentArray; + case TViewType.Embedded: + if (LVIEW_EMBEDDED_CACHE === undefined) LVIEW_EMBEDDED_CACHE = new Map(); + let embeddedArray = LVIEW_EMBEDDED_CACHE.get(name); + if (embeddedArray === undefined) { + embeddedArray = new (createNamedArrayType('LEmbeddedView' + nameSuffix(name)))(); + LVIEW_EMBEDDED_CACHE.set(name, embeddedArray); + } + return embeddedArray; + } + throw new Error('unreachable code'); +} + +function nameSuffix(text: string | null | undefined): string { + if (text == null) return ''; + const index = text.lastIndexOf('_Template'); + return '_' + (index === -1 ? text : text.substr(0, index)); } /** - * This class is a debug version of Object literal so that we can have constructor name show up in + * This class is a debug version of Object literal so that we can have constructor name show up + * in * debug tools in ngDevMode. */ export const TViewConstructor = class TView implements ITView { constructor( + public type: TViewType, // public id: number, // public blueprint: LView, // public template: ComponentTemplate<{}>|null, // @@ -259,8 +298,10 @@ export function toDebug(obj: any): any { * reading. * * @param value possibly wrapped native DOM node. - * @param includeChildren If `true` then the serialized HTML form will include child elements (same - * as `outerHTML`). If `false` then the serialized HTML form will only contain the element itself + * @param includeChildren If `true` then the serialized HTML form will include child elements + * (same + * as `outerHTML`). If `false` then the serialized HTML form will only contain the element + * itself * (will not serialize child elements). */ function toHtml(value: any, includeChildren: boolean = false): string|null { @@ -305,7 +346,8 @@ export class LViewDebug { get html(): string { return (this.nodes || []).map(node => toHtml(node.native, true)).join(''); } get context(): {}|null { return this._raw_lView[CONTEXT]; } /** - * The tree of nodes associated with the current `LView`. The nodes have been normalized into a + * The tree of nodes associated with the current `LView`. The nodes have been normalized into + * a * tree structure with relevant details pulled out for readability. */ get nodes(): DebugNode[]|null { diff --git a/packages/core/src/render3/instructions/shared.ts b/packages/core/src/render3/instructions/shared.ts index 6247a3ccd1..a172c80a3a 100644 --- a/packages/core/src/render3/instructions/shared.ts +++ b/packages/core/src/render3/instructions/shared.ts @@ -27,7 +27,7 @@ import {AttributeMarker, InitialInputData, InitialInputs, LocalRefExtractor, Pro import {RComment, RElement, RNode, RText, Renderer3, RendererFactory3, isProceduralRenderer} from '../interfaces/renderer'; import {SanitizerFn} from '../interfaces/sanitization'; import {isComponentDef, isComponentHost, isContentQueryHost, isLContainer, isRootView} from '../interfaces/type_checks'; -import {CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_VIEW, ExpandoInstructions, FLAGS, HEADER_OFFSET, HOST, INJECTOR, InitPhaseState, LView, LViewFlags, NEXT, PARENT, RENDERER, RENDERER_FACTORY, RootContext, RootContextFlags, SANITIZER, TData, TVIEW, TView, T_HOST} from '../interfaces/view'; +import {CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_VIEW, ExpandoInstructions, FLAGS, HEADER_OFFSET, HOST, INJECTOR, InitPhaseState, LView, LViewFlags, NEXT, PARENT, RENDERER, RENDERER_FACTORY, RootContext, RootContextFlags, SANITIZER, TData, TVIEW, TView, TViewType, T_HOST} from '../interfaces/view'; import {assertNodeOfPossibleTypes} from '../node_assert'; import {isNodeMatchingSelectorList} from '../node_selector_matcher'; import {ActiveElementFlags, enterView, executeElementExitFn, getBindingIndex, getBindingsEnabled, getCheckNoChangesMode, getIsParent, getPreviousOrParentTNode, getSelectedIndex, hasActiveElementFlag, incrementActiveDirectiveId, leaveView, leaveViewProcessExit, setActiveHostElement, setBindingIndex, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setCurrentQueryIndex, setPreviousOrParentTNode, setSelectedIndex} from '../state'; @@ -40,7 +40,7 @@ import {getLViewParent} from '../util/view_traversal_utils'; import {getComponentLViewByIndex, getNativeByIndex, getNativeByTNode, getTNode, isCreationMode, readPatchedLView, resetPreOrderHookFlags, unwrapRNode, viewAttachedToChangeDetector} from '../util/view_utils'; import {selectIndexInternal} from './advance'; -import {LCleanup, LViewBlueprint, MatchesArray, TCleanup, TNodeConstructor, TNodeInitialInputs, TNodeLocalNames, TViewComponents, TViewConstructor, attachLContainerDebug, attachLViewDebug, cloneToLView, cloneToTViewData} from './lview_debug'; +import {LCleanup, LViewBlueprint, MatchesArray, TCleanup, TNodeConstructor, TNodeInitialInputs, TNodeLocalNames, TViewComponents, TViewConstructor, attachLContainerDebug, attachLViewDebug, cloneToLViewFromTViewBlueprint, cloneToTViewData} from './lview_debug'; @@ -158,7 +158,8 @@ export function createLView( host: RElement | null, tHostNode: TViewNode | TElementNode | null, rendererFactory?: RendererFactory3 | null, renderer?: Renderer3 | null, sanitizer?: Sanitizer | null, injector?: Injector | null): LView { - const lView = ngDevMode ? cloneToLView(tView.blueprint) : tView.blueprint.slice() as 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); @@ -568,10 +569,11 @@ export function saveResolvedLocalsInData( * @param def ComponentDef * @returns TView */ -export function getOrCreateTView(def: ComponentDef): TView { - return def.tView || (def.tView = createTView( - -1, def.template, def.decls, def.vars, def.directiveDefs, def.pipeDefs, - def.viewQuery, def.schemas, def.consts)); +export function getOrCreateTComponentView(def: ComponentDef): TView { + return def.tView || + (def.tView = createTView( + TViewType.Component, -1, def.template, def.decls, def.vars, def.directiveDefs, + def.pipeDefs, def.viewQuery, def.schemas, def.consts)); } @@ -588,8 +590,8 @@ export function getOrCreateTView(def: ComponentDef): TView { * @param consts Constants for this view */ export function createTView( - viewIndex: number, templateFn: ComponentTemplate| null, decls: number, vars: number, - directives: DirectiveDefListOrFactory | null, pipes: PipeDefListOrFactory | null, + type: TViewType, viewIndex: number, templateFn: ComponentTemplate| null, decls: number, + vars: number, directives: DirectiveDefListOrFactory | null, pipes: PipeDefListOrFactory | null, viewQuery: ViewQueriesFunction| null, schemas: SchemaMetadata[] | null, consts: TConstants | null): TView { ngDevMode && ngDevMode.tView++; @@ -601,6 +603,7 @@ export function createTView( const blueprint = createViewBlueprint(bindingStartIndex, initialViewLength); return blueprint[TVIEW as any] = ngDevMode ? new TViewConstructor( + type, viewIndex, // id: number, blueprint, // blueprint: LView, templateFn, // template: ComponentTemplate<{}>|null, @@ -1304,7 +1307,7 @@ function baseResolveDirective(tView: TView, viewData: LView, def: DirectiveDe function addComponentLogic(lView: LView, hostTNode: TElementNode, def: ComponentDef): void { const native = getNativeByTNode(hostTNode, lView) as RElement; - const tView = getOrCreateTView(def); + const tView = getOrCreateTComponentView(def); // Only component views should be added to the view tree directly. Embedded views are // accessed through their containers because they may be removed / re-added later. diff --git a/packages/core/src/render3/interfaces/view.ts b/packages/core/src/render3/interfaces/view.ts index 26ac3e4aac..997631eb52 100644 --- a/packages/core/src/render3/interfaces/view.ts +++ b/packages/core/src/render3/interfaces/view.ts @@ -310,6 +310,35 @@ export const enum PreOrderHookFlags { */ export interface ExpandoInstructions extends Array|null> {} +/** + * Explicitly marks `TView` as a specific type in `ngDevMode` + * + * It is useful to know conceptually what time of `TView` we are dealing with when + * debugging an application (even if the runtime does not need it.) For this reason + * we store this information in the `ngDevMode` `TView` and than use it for + * better debugging experience. + */ +export const enum TViewType { + /** + * Root `TView` is the used to bootstrap components into. It is used in conjunction with + * `LView` which takes an existing DOM node not owned by Angular and wraps it in `TView`/`LView` + * so that other components can be loaded into it. + */ + Root = 0, + + /** + * `TView` associated with a Component. This would be the `TView` directly associated with the + * component view (as opposed an `Embedded` `TView` which would be a child of `Component` `TView`) + */ + Component = 1, + + /** + * `TView` associated with a template. Such as `*ngIf`, `` etc... A `Component` + * can have zero or more `Embedede` `TView`s. + */ + Embedded = 2, +} + /** * The static data for an LView (shared between all templates of a * given type). diff --git a/packages/core/src/render3/util/discovery_utils.ts b/packages/core/src/render3/util/discovery_utils.ts index 76740ac515..4553e88923 100644 --- a/packages/core/src/render3/util/discovery_utils.ts +++ b/packages/core/src/render3/util/discovery_utils.ts @@ -370,3 +370,20 @@ export function getDebugNode(element: Node): DebugNode|null { return debugNode; } + +/** + * Retrieve the component `LView` from component/element. + * + * NOTE: `LView` is a private and should not be leaked outside. + * Don't export this method to `ng.*` on window. + * + * @param target Component or Element instance. + */ +export function getComponentLView(target: any): LView { + const lContext = loadLContext(target); + const nodeIndx = lContext.nodeIndex; + const lView = lContext.lView; + const componentLView = lView[nodeIndx]; + ngDevMode && assertLView(componentLView); + return componentLView; +} diff --git a/packages/core/src/util/named_array_type.ts b/packages/core/src/util/named_array_type.ts index 8534839d4f..06de01cf81 100644 --- a/packages/core/src/util/named_array_type.ts +++ b/packages/core/src/util/named_array_type.ts @@ -8,7 +8,6 @@ */ import './ng_dev_mode'; -import {global} from './global'; /** * THIS FILE CONTAINS CODE WHICH SHOULD BE TREE SHAKEN AND NEVER CALLED FROM PRODUCTION CODE!!! diff --git a/packages/core/test/acceptance/discover_utils_spec.ts b/packages/core/test/acceptance/discover_utils_spec.ts index 21d8a5a6fd..9aa1e1d7b5 100644 --- a/packages/core/test/acceptance/discover_utils_spec.ts +++ b/packages/core/test/acceptance/discover_utils_spec.ts @@ -7,11 +7,13 @@ */ import {CommonModule} from '@angular/common'; import {Component, Directive, HostBinding, InjectionToken, ViewChild} from '@angular/core'; +import {isLView} from '@angular/core/src/render3/interfaces/type_checks'; +import {CONTEXT} from '@angular/core/src/render3/interfaces/view'; import {ComponentFixture, TestBed} from '@angular/core/testing'; import {onlyInIvy} from '@angular/private/testing'; import {getHostElement, markDirty} from '../../src/render3/index'; -import {getComponent, getContext, getDebugNode, getDirectives, getInjectionTokens, getInjector, getListeners, getLocalRefs, getRootComponents, getViewComponent, loadLContext} from '../../src/render3/util/discovery_utils'; +import {getComponent, getComponentLView, getContext, getDebugNode, getDirectives, getInjectionTokens, getInjector, getListeners, getLocalRefs, getRootComponents, getViewComponent, loadLContext} from '../../src/render3/util/discovery_utils'; onlyInIvy('Ivy-specific utilities').describe('discovery utils', () => { let fixture: ComponentFixture; @@ -87,6 +89,20 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils', () => { }); }); + describe('getComponentLView', () => { + it('should retrieve component LView from element', () => { + const childLView = getComponentLView(child[0]); + expect(isLView(childLView)).toBe(true); + expect(childLView[CONTEXT] instanceof Child).toBe(true); + }); + + it('should retrieve component LView from component instance', () => { + const childLView = getComponentLView(childComponent[0]); + expect(isLView(childLView)).toBe(true); + expect(childLView[CONTEXT] instanceof Child).toBe(true); + }); + }); + describe('getContext', () => { it('should throw when called on non-element', () => { expect(() => getContext(dirA[0] as any)).toThrowError(/Expecting instance of DOM Node/); diff --git a/packages/core/test/acceptance/ngdevmode_debug_spec.ts b/packages/core/test/acceptance/ngdevmode_debug_spec.ts new file mode 100644 index 0000000000..44027dea6e --- /dev/null +++ b/packages/core/test/acceptance/ngdevmode_debug_spec.ts @@ -0,0 +1,49 @@ +/** + * @license + * Copyright Google Inc. 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 {CommonModule} from '@angular/common'; +import {Component} from '@angular/core'; +import {LView} from '@angular/core/src/render3/interfaces/view'; +import {getComponentLView, loadLContext} from '@angular/core/src/render3/util/discovery_utils'; +import {createNamedArrayType} from '@angular/core/src/util/named_array_type'; +import {TestBed} from '@angular/core/testing'; +import {onlyInIvy} from '@angular/private/testing'; + +const supportsArraySubclassing = + createNamedArrayType('SupportsArraySubclassing').name === 'SupportsArraySubclassing'; + +onlyInIvy('Debug information exist in ivy only').describe('ngDevMode debug', () => { + describe('LViewDebug', () => { + supportsArraySubclassing && it('should name LView based on type', () => { + @Component({ + template: ` +
    +
  • item
  • +
+ ` + }) + class MyApp { + } + + TestBed.configureTestingModule({declarations: [MyApp], imports: [CommonModule]}); + const fixture = TestBed.createComponent(MyApp); + const rootLView = loadLContext(fixture.nativeElement).lView; + expect(rootLView.constructor.name).toEqual('LRootView'); + + const componentLView = getComponentLView(fixture.componentInstance); + expect(componentLView.constructor.name).toEqual('LComponentView_MyApp'); + + const element: HTMLElement = fixture.nativeElement; + fixture.detectChanges(); + const li = element.querySelector('li') !; + const embeddedLView = loadLContext(li).lView; + expect(embeddedLView.constructor.name).toEqual('LEmbeddedView_MyApp_li_1'); + + }); + }); +}); diff --git a/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json b/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json index 910d2e833a..a57b1cb32f 100644 --- a/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json +++ b/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json @@ -354,10 +354,10 @@ "name": "getOrCreateNodeInjectorForNode" }, { - "name": "getOrCreateTNode" + "name": "getOrCreateTComponentView" }, { - "name": "getOrCreateTView" + "name": "getOrCreateTNode" }, { "name": "getParentInjectorIndex" 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 826f028b3d..398fadd1b0 100644 --- a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json @@ -273,10 +273,10 @@ "name": "getOrCreateNodeInjectorForNode" }, { - "name": "getOrCreateTNode" + "name": "getOrCreateTComponentView" }, { - "name": "getOrCreateTView" + "name": "getOrCreateTNode" }, { "name": "getParentInjectorIndex" diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json index 60337420fc..be960678d9 100644 --- a/packages/core/test/bundling/todo/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json @@ -723,10 +723,10 @@ "name": "getOrCreateNodeInjectorForNode" }, { - "name": "getOrCreateTNode" + "name": "getOrCreateTComponentView" }, { - "name": "getOrCreateTView" + "name": "getOrCreateTNode" }, { "name": "getOriginalError" diff --git a/packages/core/test/render3/di_spec.ts b/packages/core/test/render3/di_spec.ts index bba9909f75..e6ac07d32e 100644 --- a/packages/core/test/render3/di_spec.ts +++ b/packages/core/test/render3/di_spec.ts @@ -16,7 +16,7 @@ import {ɵɵdefineDirective, ɵɵdirectiveInject, ɵɵelement, ɵɵelementEnd, import {TNODE} from '../../src/render3/interfaces/injector'; import {TNodeType} from '../../src/render3/interfaces/node'; import {isProceduralRenderer} from '../../src/render3/interfaces/renderer'; -import {LViewFlags, TVIEW} from '../../src/render3/interfaces/view'; +import {LViewFlags, TVIEW, TViewType} from '../../src/render3/interfaces/view'; import {enterView, leaveViewProcessExit} from '../../src/render3/state'; import {getRendererFactory2} from './imported_renderer2'; @@ -223,7 +223,7 @@ describe('di', () => { describe('getOrCreateNodeInjector', () => { it('should handle initial undefined state', () => { const contentView = createLView( - null, createTView(-1, null, 1, 0, null, null, null, null, null), null, + null, createTView(TViewType.Embedded, -1, null, 1, 0, null, null, null, null, null), {}, LViewFlags.CheckAlways, null, null, {} as any, {} as any); enterView(contentView, null); try { diff --git a/packages/core/test/render3/perf/directive_instantiate/index.ts b/packages/core/test/render3/perf/directive_instantiate/index.ts index 572e5c4dde..44b010307b 100644 --- a/packages/core/test/render3/perf/directive_instantiate/index.ts +++ b/packages/core/test/render3/perf/directive_instantiate/index.ts @@ -5,6 +5,7 @@ * 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 {TViewType} from '@angular/core/src/render3/interfaces/view'; import {ɵɵdefineDirective, ɵɵelementEnd, ɵɵelementStart, ɵɵtext} from '../../../../src/render3/index'; import {createTNode, createTView} from '../../../../src/render3/instructions/shared'; import {RenderFlags} from '../../../../src/render3/interfaces/definition'; @@ -74,7 +75,7 @@ function testTemplate(rf: RenderFlags, ctx: any) { const viewTNode = createTNode(null !, null, TNodeType.View, -1, null, null) as TViewNode; const embeddedTView = createTView( - -1, testTemplate, 21, 10, [Tooltip.ɵdir], null, null, null, + TViewType.Embedded, -1, testTemplate, 21, 10, [Tooltip.ɵdir], null, null, null, [['position', 'top', 3, 'tooltip']]); // create view once so we don't profile first template 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 6555d2a332..563fd8e4aa 100644 --- a/packages/core/test/render3/perf/element_text_create/index.ts +++ b/packages/core/test/render3/perf/element_text_create/index.ts @@ -5,6 +5,7 @@ * 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 {TViewType} from '@angular/core/src/render3/interfaces/view'; import {ɵɵelementEnd, ɵɵelementStart} from '../../../../src/render3/instructions/element'; import {createTNode, createTView} from '../../../../src/render3/instructions/shared'; import {ɵɵtext} from '../../../../src/render3/instructions/text'; @@ -64,9 +65,10 @@ function testTemplate(rf: RenderFlags, ctx: any) { } const viewTNode = createTNode(null !, null, TNodeType.View, -1, null, null) as TViewNode; -const embeddedTView = createTView(-1, testTemplate, 21, 0, null, null, null, null, [ - ['name1', 'value1', 'name2', 'value2', 'name3', 'value3', 'name4', 'value4', 'name5', 'value5'] -]); +const embeddedTView = createTView( + TViewType.Embedded, -1, testTemplate, 21, 0, null, null, null, null, [[ + 'name1', 'value1', 'name2', 'value2', 'name3', 'value3', 'name4', 'value4', 'name5', 'value5' + ]]); // create view once so we don't profile first template pass createAndRenderLView(null, embeddedTView, viewTNode); diff --git a/packages/core/test/render3/perf/listeners/index.ts b/packages/core/test/render3/perf/listeners/index.ts index 0edbb09187..89cb11d660 100644 --- a/packages/core/test/render3/perf/listeners/index.ts +++ b/packages/core/test/render3/perf/listeners/index.ts @@ -5,6 +5,7 @@ * 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 {TViewType} from '@angular/core/src/render3/interfaces/view'; import {ɵɵelementEnd, ɵɵelementStart} from '../../../../src/render3/instructions/element'; import {ɵɵlistener} from '../../../../src/render3/instructions/listener'; import {createTNode, createTView} from '../../../../src/render3/instructions/shared'; @@ -75,8 +76,8 @@ function testTemplate(rf: RenderFlags, ctx: any) { } const viewTNode = createTNode(null !, null, TNodeType.View, -1, null, null) as TViewNode; -const embeddedTView = - createTView(-1, testTemplate, 11, 0, null, null, null, null, [[3, 'click', 'input']]); +const embeddedTView = createTView( + TViewType.Embedded, -1, testTemplate, 11, 0, null, null, null, null, [[3, 'click', 'input']]); // create view once so we don't profile first template pass createAndRenderLView(null, 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 63ab566e87..3518a8e4f8 100644 --- a/packages/core/test/render3/perf/ng_template/index.ts +++ b/packages/core/test/render3/perf/ng_template/index.ts @@ -5,8 +5,8 @@ * 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 {TViewType} from '@angular/core/src/render3/interfaces/view'; import {ElementRef, TemplateRef, ViewContainerRef} from '../../../../src/linker'; - import {ɵɵdefineDirective, ɵɵdirectiveInject, ɵɵtemplate} from '../../../../src/render3/index'; import {createTNode, createTView} from '../../../../src/render3/instructions/shared'; import {RenderFlags} from '../../../../src/render3/interfaces/definition'; @@ -60,7 +60,8 @@ function testTemplate(rf: RenderFlags, ctx: any) { const viewTNode = createTNode(null !, null, TNodeType.View, -1, null, null) as TViewNode; const embeddedTView = createTView( - -1, testTemplate, 2, 0, [NgIfLike.ɵdir], null, null, null, [['viewManipulation', '']]); + TViewType.Root, -1, testTemplate, 2, 0, [NgIfLike.ɵdir], null, null, null, + [['viewManipulation', '']]); // create view once so we don't profile first template pass createAndRenderLView(null, embeddedTView, viewTNode); diff --git a/packages/core/test/render3/perf/setup.ts b/packages/core/test/render3/perf/setup.ts index dbd94f60bc..530008987e 100644 --- a/packages/core/test/render3/perf/setup.ts +++ b/packages/core/test/render3/perf/setup.ts @@ -9,7 +9,7 @@ import {addToViewTree, createLContainer, createLView, createTNode, createTView, import {ComponentTemplate} from '../../../src/render3/interfaces/definition'; import {TAttributes, TNodeType, TViewNode} from '../../../src/render3/interfaces/node'; import {RendererFactory3, domRendererFactory3} from '../../../src/render3/interfaces/renderer'; -import {LView, LViewFlags, TView} from '../../../src/render3/interfaces/view'; +import {LView, LViewFlags, TView, TViewType} from '../../../src/render3/interfaces/view'; import {insertView} from '../../../src/render3/node_manipulation'; import {MicroBenchmarkRendererFactory} from './noop_renderer'; @@ -45,7 +45,7 @@ export function setupTestHarness( templateFn: ComponentTemplate| null, decls: number, vars: number, noOfViews: number, embeddedViewContext: any = {}, consts: TAttributes[] | null = null): TestHarness { // Create a root view with a container - const hostTView = createTView(-1, null, 1, 0, null, null, null, null, consts); + const hostTView = createTView(TViewType.Root, -1, null, 1, 0, null, null, null, null, consts); const tContainerNode = getOrCreateTNode(hostTView, null, 0, TNodeType.Container, null, null); const hostNode = renderer.createElement('div'); const hostLView = createLView( @@ -58,7 +58,8 @@ export function setupTestHarness( // create test embedded views - const embeddedTView = createTView(-1, templateFn, decls, vars, null, null, null, null, consts); + const embeddedTView = + createTView(TViewType.Embedded, -1, templateFn, decls, vars, null, null, null, null, consts); const viewTNode = createTNode(hostTView, null, TNodeType.View, -1, null, null) as TViewNode; function createEmbeddedLView(): LView { diff --git a/packages/core/test/render3/render_util.ts b/packages/core/test/render3/render_util.ts index 3cc290343c..479fd071be 100644 --- a/packages/core/test/render3/render_util.ts +++ b/packages/core/test/render3/render_util.ts @@ -12,8 +12,8 @@ import {ElementRef} from '@angular/core/src/linker/element_ref'; import {TemplateRef} from '@angular/core/src/linker/template_ref'; import {ViewContainerRef} from '@angular/core/src/linker/view_container_ref'; import {Renderer2} from '@angular/core/src/render/api'; -import {createLView, createTView, getOrCreateTNode, getOrCreateTView, renderComponentOrTemplate} from '@angular/core/src/render3/instructions/shared'; -import {TAttributes, TConstants, TNodeType} from '@angular/core/src/render3/interfaces/node'; +import {createLView, createTView, getOrCreateTComponentView, getOrCreateTNode, renderComponentOrTemplate} from '@angular/core/src/render3/instructions/shared'; +import {TConstants, TNodeType} from '@angular/core/src/render3/interfaces/node'; import {enterView, getLView} from '@angular/core/src/render3/state'; import {stringifyElement} from '@angular/platform-browser/testing/src/browser_util'; @@ -32,13 +32,15 @@ import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveT import {DirectiveDefList, DirectiveDefListOrFactory, DirectiveTypesOrFactory, HostBindingsFunction, PipeDef, PipeDefList, PipeDefListOrFactory, PipeTypesOrFactory} from '../../src/render3/interfaces/definition'; import {PlayerHandler} from '../../src/render3/interfaces/player'; import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, RendererFactory3, RendererStyleFlags3, domRendererFactory3} from '../../src/render3/interfaces/renderer'; -import {HEADER_OFFSET, LView, LViewFlags, T_HOST} from '../../src/render3/interfaces/view'; +import {HEADER_OFFSET, LView, LViewFlags, TViewType, T_HOST} from '../../src/render3/interfaces/view'; import {destroyLView} from '../../src/render3/node_manipulation'; import {getRootView} from '../../src/render3/util/view_traversal_utils'; import {Sanitizer} from '../../src/sanitization/sanitizer'; import {getRendererFactory2} from './imported_renderer2'; + + export abstract class BaseFixture { /** * Each fixture creates the following initial DOM structure: @@ -254,7 +256,7 @@ 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(-1, null, 1, 0, null, null, null, null, null); + const tView = createTView(TViewType.Root, -1, null, 1, 0, null, null, null, null, null); const hostLView = createLView( null, tView, {}, LViewFlags.CheckAlways | LViewFlags.IsRoot, null, null, providedRendererFactory, renderer); @@ -270,7 +272,7 @@ export function renderTemplate( def.directiveDefs = directives || null; def.pipeDefs = pipes || null; - const componentTView = getOrCreateTView(def); + const componentTView = getOrCreateTComponentView(def); const hostTNode = getOrCreateTNode(tView, hostLView[T_HOST], 0, TNodeType.Element, null, null); hostLView[hostTNode.index] = hostNode; componentView = createLView( diff --git a/packages/core/test/render3/view_utils_spec.ts b/packages/core/test/render3/view_utils_spec.ts index 293838fd44..762c932380 100644 --- a/packages/core/test/render3/view_utils_spec.ts +++ b/packages/core/test/render3/view_utils_spec.ts @@ -8,11 +8,12 @@ import {createLContainer, createLView, createTNode, createTView} from '@angular/core/src/render3/instructions/shared'; import {isLContainer, isLView} from '@angular/core/src/render3/interfaces/type_checks'; +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(0, null, 0, 0, null, null, null, null, null); + const tView = createTView(TViewType.Root, 0, 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, true);