refactor(ivy): Intruduce LFrame to store global instruction information (#33178)

`LFrame` stores information specifice to the current `LView` As the code
enters and leaves `LView`s we use `enterView()` and `leaveView()`
respectively to build a a stack of `LFrame`s. This allows us to easily
restore the previous `LView` instruction state.

PR Close #33178
This commit is contained in:
Miško Hevery 2019-10-14 13:59:17 -07:00 committed by Andrew Kushnir
parent 9ea3430a5b
commit 09a2bb839f
19 changed files with 327 additions and 259 deletions

View File

@ -12,7 +12,7 @@
"master": { "master": {
"uncompressed": { "uncompressed": {
"runtime-es2015": 1485, "runtime-es2015": 1485,
"main-es2015": 14440, "main-es2015": 14678,
"polyfills-es2015": 36808 "polyfills-es2015": 36808
} }
} }
@ -64,4 +64,4 @@
} }
} }
} }
} }

View File

@ -23,7 +23,7 @@ import {TElementNode, TNode, TNodeType} from './interfaces/node';
import {PlayerHandler} from './interfaces/player'; import {PlayerHandler} from './interfaces/player';
import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from './interfaces/renderer'; 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} from './interfaces/view';
import {getPreviousOrParentTNode, incrementActiveDirectiveId, resetComponentState, selectView, setActiveHostElement} from './state'; import {enterView, getPreviousOrParentTNode, incrementActiveDirectiveId, leaveView, setActiveHostElement} from './state';
import {publishDefaultGlobalUtils} from './util/global_utils'; import {publishDefaultGlobalUtils} from './util/global_utils';
import {defaultScheduler, stringifyForError} from './util/misc_utils'; import {defaultScheduler, stringifyForError} from './util/misc_utils';
import {getRootContext} from './util/view_traversal_utils'; import {getRootContext} from './util/view_traversal_utils';
@ -111,10 +111,6 @@ export function renderComponent<T>(
ngDevMode && publishDefaultGlobalUtils(); ngDevMode && publishDefaultGlobalUtils();
ngDevMode && assertComponentType(componentType); ngDevMode && assertComponentType(componentType);
// this is preemptively set to avoid having test and debug code accidentally
// read data from a previous application state...
setActiveHostElement(null);
const rendererFactory = opts.rendererFactory || domRendererFactory3; const rendererFactory = opts.rendererFactory || domRendererFactory3;
const sanitizer = opts.sanitizer || null; const sanitizer = opts.sanitizer || null;
const componentDef = getComponentDef<T>(componentType) !; const componentDef = getComponentDef<T>(componentType) !;
@ -133,7 +129,7 @@ export function renderComponent<T>(
null, rootTView, rootContext, rootFlags, null, null, rendererFactory, renderer, undefined, null, rootTView, rootContext, rootFlags, null, null, rendererFactory, renderer, undefined,
opts.injector || null); opts.injector || null);
const oldView = selectView(rootView, null); enterView(rootView, null);
let component: T; let component: T;
try { try {
@ -149,7 +145,7 @@ export function renderComponent<T>(
refreshView(rootView, rootTView, null, null); refreshView(rootView, rootTView, null, null);
} finally { } finally {
selectView(oldView, null); leaveView();
if (rendererFactory.end) rendererFactory.end(); if (rendererFactory.end) rendererFactory.end();
} }
@ -170,7 +166,6 @@ export function renderComponent<T>(
export function createRootComponentView( export function createRootComponentView(
rNode: RElement | null, def: ComponentDef<any>, rootView: LView, rNode: RElement | null, def: ComponentDef<any>, rootView: LView,
rendererFactory: RendererFactory3, renderer: Renderer3, sanitizer?: Sanitizer | null): LView { rendererFactory: RendererFactory3, renderer: Renderer3, sanitizer?: Sanitizer | null): LView {
resetComponentState();
const tView = rootView[TVIEW]; const tView = rootView[TVIEW];
ngDevMode && assertDataInRange(rootView, 0 + HEADER_OFFSET); ngDevMode && assertDataInRange(rootView, 0 + HEADER_OFFSET);
rootView[0 + HEADER_OFFSET] = rNode; rootView[0 + HEADER_OFFSET] = rNode;

View File

@ -29,7 +29,7 @@ import {ComponentDef} from './interfaces/definition';
import {TContainerNode, TElementContainerNode, TElementNode} from './interfaces/node'; import {TContainerNode, TElementContainerNode, TElementNode} from './interfaces/node';
import {RNode, RendererFactory3, domRendererFactory3, isProceduralRenderer} from './interfaces/renderer'; import {RNode, RendererFactory3, domRendererFactory3, isProceduralRenderer} from './interfaces/renderer';
import {LView, LViewFlags, TVIEW} from './interfaces/view'; import {LView, LViewFlags, TVIEW} from './interfaces/view';
import {namespaceHTMLInternal, selectView} from './state'; import {enterView, leaveView} from './state';
import {defaultScheduler} from './util/misc_utils'; import {defaultScheduler} from './util/misc_utils';
import {getTNode} from './util/view_utils'; import {getTNode} from './util/view_utils';
import {createElementRef} from './view_engine_compatibility'; import {createElementRef} from './view_engine_compatibility';
@ -133,9 +133,6 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
rootViewInjector.get(RendererFactory2, domRendererFactory3) as RendererFactory3; rootViewInjector.get(RendererFactory2, domRendererFactory3) as RendererFactory3;
const sanitizer = rootViewInjector.get(Sanitizer, null); const sanitizer = rootViewInjector.get(Sanitizer, null);
// Ensure that the namespace for the root node is correct,
// otherwise the browser might not render out the element properly.
namespaceHTMLInternal();
const hostRNode = rootSelectorOrNode ? const hostRNode = rootSelectorOrNode ?
locateHostElement(rendererFactory, rootSelectorOrNode) : locateHostElement(rendererFactory, rootSelectorOrNode) :
elementCreate(this.selector, rendererFactory.createRenderer(null, this.componentDef), null); elementCreate(this.selector, rendererFactory.createRenderer(null, this.componentDef), null);
@ -167,7 +164,11 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
rootViewInjector); rootViewInjector);
// rootView is the parent when bootstrapping // rootView is the parent when bootstrapping
const oldLView = selectView(rootLView, null); // TODO(misko): it looks like we are entering view here but we don't really need to as
// `renderView` does that. However as the code is written it is needed because
// `createRootComponentView` and `createRootComponent` both read global state. Fixing those
// issues would allow us to drop this.
enterView(rootLView, null);
let component: T; let component: T;
let tElementNode: TElementNode; let tElementNode: TElementNode;
@ -194,7 +195,7 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
renderView(rootLView, rootTView, null); renderView(rootLView, rootTView, null);
} finally { } finally {
selectView(oldLView, null); leaveView();
} }
const componentRef = new ComponentRef( const componentRef = new ComponentRef(

View File

@ -10,7 +10,7 @@ import {isForwardRef, resolveForwardRef} from '../di/forward_ref';
import {InjectionToken} from '../di/injection_token'; import {InjectionToken} from '../di/injection_token';
import {Injector} from '../di/injector'; import {Injector} from '../di/injector';
import {injectRootLimpMode, setInjectImplementation} from '../di/injector_compatibility'; import {injectRootLimpMode, setInjectImplementation} from '../di/injector_compatibility';
import {getInjectableDef, getInjectorDef} from '../di/interface/defs'; import {getInjectorDef} from '../di/interface/defs';
import {InjectFlags} from '../di/interface/injector'; import {InjectFlags} from '../di/interface/injector';
import {Type} from '../interface/type'; import {Type} from '../interface/type';
import {assertDefined, assertEqual} from '../util/assert'; import {assertDefined, assertEqual} from '../util/assert';
@ -19,11 +19,11 @@ import {getFactoryDef} from './definition';
import {NG_ELEMENT_ID, NG_FACTORY_DEF} from './fields'; import {NG_ELEMENT_ID, NG_FACTORY_DEF} from './fields';
import {DirectiveDef, FactoryFn} from './interfaces/definition'; import {DirectiveDef, FactoryFn} from './interfaces/definition';
import {NO_PARENT_INJECTOR, NodeInjectorFactory, PARENT_INJECTOR, RelativeInjectorLocation, RelativeInjectorLocationFlags, TNODE, isFactory} from './interfaces/injector'; import {NO_PARENT_INJECTOR, NodeInjectorFactory, PARENT_INJECTOR, RelativeInjectorLocation, RelativeInjectorLocationFlags, TNODE, isFactory} from './interfaces/injector';
import {AttributeMarker, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType} from './interfaces/node'; import {AttributeMarker, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeProviderIndexes, TNodeType} from './interfaces/node';
import {isComponentDef, isComponentHost} from './interfaces/type_checks'; import {isComponentDef, isComponentHost} from './interfaces/type_checks';
import {DECLARATION_VIEW, INJECTOR, LView, TData, TVIEW, TView, T_HOST} from './interfaces/view'; import {DECLARATION_VIEW, INJECTOR, LView, TData, TVIEW, TView, T_HOST} from './interfaces/view';
import {assertNodeOfPossibleTypes} from './node_assert'; import {assertNodeOfPossibleTypes} from './node_assert';
import {getLView, getPreviousOrParentTNode, setTNodeAndViewData} from './state'; import {enterDI, leaveDI} from './state';
import {isNameOnlyAttributeMarker} from './util/attrs_utils'; import {isNameOnlyAttributeMarker} from './util/attrs_utils';
import {getParentInjectorIndex, getParentInjectorView, hasParentInjector} from './util/injector_utils'; import {getParentInjectorIndex, getParentInjectorView, hasParentInjector} from './util/injector_utils';
import {stringifyForError} from './util/misc_utils'; import {stringifyForError} from './util/misc_utils';
@ -334,9 +334,7 @@ export function getOrCreateInjectable<T>(
// If the ID stored here is a function, this is a special object like ElementRef or TemplateRef // If the ID stored here is a function, this is a special object like ElementRef or TemplateRef
// so just call the factory function to create it. // so just call the factory function to create it.
if (typeof bloomHash === 'function') { if (typeof bloomHash === 'function') {
const savePreviousOrParentTNode = getPreviousOrParentTNode(); enterDI(lView, tNode);
const saveLView = getLView();
setTNodeAndViewData(tNode, lView);
try { try {
const value = bloomHash(); const value = bloomHash();
if (value == null && !(flags & InjectFlags.Optional)) { if (value == null && !(flags & InjectFlags.Optional)) {
@ -345,7 +343,7 @@ export function getOrCreateInjectable<T>(
return value; return value;
} }
} finally { } finally {
setTNodeAndViewData(savePreviousOrParentTNode, saveLView); leaveDI();
} }
} else if (typeof bloomHash == 'number') { } else if (typeof bloomHash == 'number') {
if (bloomHash === -1) { if (bloomHash === -1) {
@ -530,8 +528,8 @@ export function locateDirectiveOrProvider<T>(
* instantiates the `injectable` and caches the value. * instantiates the `injectable` and caches the value.
*/ */
export function getNodeInjectable( export function getNodeInjectable(
tData: TData, lData: LView, index: number, tNode: TElementNode): any { tData: TData, lView: LView, index: number, tNode: TElementNode): any {
let value = lData[index]; let value = lView[index];
if (isFactory(value)) { if (isFactory(value)) {
const factory: NodeInjectorFactory = value; const factory: NodeInjectorFactory = value;
if (factory.resolving) { if (factory.resolving) {
@ -543,16 +541,14 @@ export function getNodeInjectable(
if (factory.injectImpl) { if (factory.injectImpl) {
previousInjectImplementation = setInjectImplementation(factory.injectImpl); previousInjectImplementation = setInjectImplementation(factory.injectImpl);
} }
const savePreviousOrParentTNode = getPreviousOrParentTNode(); enterDI(lView, tNode);
const saveLView = getLView();
setTNodeAndViewData(tNode, lData);
try { try {
value = lData[index] = factory.factory(undefined, tData, lData, tNode); value = lView[index] = factory.factory(undefined, tData, lView, tNode);
} finally { } finally {
if (factory.injectImpl) setInjectImplementation(previousInjectImplementation); if (factory.injectImpl) setInjectImplementation(previousInjectImplementation);
setIncludeViewProviders(previousIncludeViewProviders); setIncludeViewProviders(previousIncludeViewProviders);
factory.resolving = false; factory.resolving = false;
setTNodeAndViewData(savePreviousOrParentTNode, saveLView); leaveDI();
} }
} }
return value; return value;

View File

@ -14,12 +14,12 @@ 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, T_HOST} from '../interfaces/view';
import {assertNodeType} from '../node_assert'; import {assertNodeType} from '../node_assert';
import {insertView, removeView} from '../node_manipulation'; import {insertView, removeView} from '../node_manipulation';
import {getIsParent, getLView, getPreviousOrParentTNode, selectView, setIsParent, setPreviousOrParentTNode} from '../state'; import {enterView, getIsParent, getLView, getPreviousOrParentTNode, leaveViewProcessExit, setIsParent, setPreviousOrParentTNode} from '../state';
import {isCreationMode} from '../util/view_utils'; import {isCreationMode} from '../util/view_utils';
import {assignTViewNodeToLView, createLView, createTView, refreshView, renderView} from './shared'; import {assignTViewNodeToLView, createLView, createTView, refreshView, renderView} from './shared';
/** /**
* Marks the start of an embedded view. * Marks the start of an embedded view.
* *
@ -42,7 +42,7 @@ export function ɵɵembeddedViewStart(viewBlockId: number, decls: number, vars:
if (viewToRender) { if (viewToRender) {
setIsParent(); setIsParent();
selectView(viewToRender, viewToRender[TVIEW].node); enterView(viewToRender, viewToRender[TVIEW].node);
} else { } else {
// When we create a new LView, we always reset the state of the instructions. // When we create a new LView, we always reset the state of the instructions.
viewToRender = createLView( viewToRender = createLView(
@ -52,7 +52,7 @@ export function ɵɵembeddedViewStart(viewBlockId: number, decls: number, vars:
const tParentNode = getIsParent() ? previousOrParentTNode : const tParentNode = getIsParent() ? previousOrParentTNode :
previousOrParentTNode && previousOrParentTNode.parent; previousOrParentTNode && previousOrParentTNode.parent;
assignTViewNodeToLView(viewToRender[TVIEW], tParentNode, viewBlockId, viewToRender); assignTViewNodeToLView(viewToRender[TVIEW], tParentNode, viewBlockId, viewToRender);
selectView(viewToRender, viewToRender[TVIEW].node); enterView(viewToRender, viewToRender[TVIEW].node);
} }
if (lContainer) { if (lContainer) {
if (isCreationMode(viewToRender)) { if (isCreationMode(viewToRender)) {
@ -138,6 +138,6 @@ export function ɵɵembeddedViewEnd(): void {
const lContainer = lView[PARENT] as LContainer; const lContainer = lView[PARENT] as LContainer;
ngDevMode && assertLContainerOrUndefined(lContainer); ngDevMode && assertLContainerOrUndefined(lContainer);
selectView(lContainer[PARENT] !, null); leaveViewProcessExit();
setPreviousOrParentTNode(viewHost !, false); setPreviousOrParentTNode(viewHost !, false);
} }

View File

@ -17,8 +17,11 @@ import {CLEANUP, FLAGS, LView, LViewFlags, RENDERER, TVIEW} from '../interfaces/
import {assertNodeOfPossibleTypes} from '../node_assert'; import {assertNodeOfPossibleTypes} from '../node_assert';
import {getLView, getPreviousOrParentTNode} from '../state'; import {getLView, getPreviousOrParentTNode} from '../state';
import {getComponentLViewByIndex, getNativeByTNode, unwrapRNode} from '../util/view_utils'; import {getComponentLViewByIndex, getNativeByTNode, unwrapRNode} from '../util/view_utils';
import {getCleanup, handleError, loadComponentRenderer, markViewDirty} from './shared'; import {getCleanup, handleError, loadComponentRenderer, markViewDirty} from './shared';
/** /**
* Adds an event listener to the current node. * Adds an event listener to the current node.
* *
@ -213,7 +216,7 @@ function listenerInternal(
} }
function executeListenerWithErrorHandling( function executeListenerWithErrorHandling(
lView: LView, listenerFn: (e?: any) => any, e: any): boolean { lView: LView, tNode: TNode, listenerFn: (e?: any) => any, e: any): boolean {
try { try {
// Only explicitly returning false from a listener should preventDefault // Only explicitly returning false from a listener should preventDefault
return listenerFn(e) !== false; return listenerFn(e) !== false;
@ -256,13 +259,13 @@ function wrapListener(
markViewDirty(startView); markViewDirty(startView);
} }
let result = executeListenerWithErrorHandling(lView, listenerFn, e); let result = executeListenerWithErrorHandling(lView, tNode, listenerFn, e);
// A just-invoked listener function might have coalesced listeners so we need to check for // A just-invoked listener function might have coalesced listeners so we need to check for
// their presence and invoke as needed. // their presence and invoke as needed.
let nextListenerFn = (<any>wrapListenerIn_markDirtyAndPreventDefault).__ngNextListenerFn__; let nextListenerFn = (<any>wrapListenerIn_markDirtyAndPreventDefault).__ngNextListenerFn__;
while (nextListenerFn) { while (nextListenerFn) {
// We should prevent default if any of the listeners explicitly return false // We should prevent default if any of the listeners explicitly return false
result = executeListenerWithErrorHandling(lView, nextListenerFn, e) && result; result = executeListenerWithErrorHandling(lView, tNode, nextListenerFn, e) && result;
nextListenerFn = (<any>nextListenerFn).__ngNextListenerFn__; nextListenerFn = (<any>nextListenerFn).__ngNextListenerFn__;
} }

View File

@ -15,7 +15,7 @@ import {createNamedArrayType} from '../../util/named_array_type';
import {initNgDevMode} from '../../util/ng_dev_mode'; import {initNgDevMode} from '../../util/ng_dev_mode';
import {normalizeDebugBindingName, normalizeDebugBindingValue} from '../../util/ng_reflect'; import {normalizeDebugBindingName, normalizeDebugBindingValue} from '../../util/ng_reflect';
import {assertFirstTemplatePass, assertLView} from '../assert'; import {assertFirstTemplatePass, assertLView} from '../assert';
import {attachPatchData, getComponentViewByInstance} from '../context_discovery'; import {attachPatchData} from '../context_discovery';
import {getFactoryDef} from '../definition'; import {getFactoryDef} from '../definition';
import {diPublicInInjector, getNodeInjectable, getOrCreateNodeInjectorForNode} from '../di'; import {diPublicInInjector, getNodeInjectable, getOrCreateNodeInjectorForNode} from '../di';
import {throwMultipleComponentError} from '../errors'; import {throwMultipleComponentError} from '../errors';
@ -30,7 +30,7 @@ import {isComponentDef, isComponentHost, isContentQueryHost, isLContainer, isRoo
import {BINDING_INDEX, 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 {BINDING_INDEX, 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 {assertNodeOfPossibleTypes} from '../node_assert'; import {assertNodeOfPossibleTypes} from '../node_assert';
import {isNodeMatchingSelectorList} from '../node_selector_matcher'; import {isNodeMatchingSelectorList} from '../node_selector_matcher';
import {ActiveElementFlags, executeElementExitFn, getBindingsEnabled, getCheckNoChangesMode, getIsParent, getPreviousOrParentTNode, getSelectedIndex, hasActiveElementFlag, incrementActiveDirectiveId, namespaceHTMLInternal, selectView, setActiveHostElement, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setCurrentQueryIndex, setPreviousOrParentTNode, setSelectedIndex} from '../state'; import {ActiveElementFlags, enterView, executeElementExitFn, getBindingsEnabled, getCheckNoChangesMode, getIsParent, getPreviousOrParentTNode, getSelectedIndex, hasActiveElementFlag, incrementActiveDirectiveId, leaveView, leaveViewProcessExit, setActiveHostElement, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setCurrentQueryIndex, setPreviousOrParentTNode, setSelectedIndex} from '../state';
import {renderStylingMap} from '../styling/bindings'; import {renderStylingMap} from '../styling/bindings';
import {NO_CHANGE} from '../tokens'; import {NO_CHANGE} from '../tokens';
import {isAnimationProp} from '../util/attrs_utils'; import {isAnimationProp} from '../util/attrs_utils';
@ -312,7 +312,7 @@ export function allocExpando(view: LView, numSlotsToAlloc: number) {
*/ */
export function renderView<T>(lView: LView, tView: TView, context: T): void { export function renderView<T>(lView: LView, tView: TView, context: T): void {
ngDevMode && assertEqual(isCreationMode(lView), true, 'Should be run in creation mode'); ngDevMode && assertEqual(isCreationMode(lView), true, 'Should be run in creation mode');
const oldView = selectView(lView, lView[T_HOST]); enterView(lView, lView[T_HOST]);
try { try {
const viewQuery = tView.viewQuery; const viewQuery = tView.viewQuery;
if (viewQuery !== null) { if (viewQuery !== null) {
@ -357,7 +357,7 @@ export function renderView<T>(lView: LView, tView: TView, context: T): void {
} finally { } finally {
lView[FLAGS] &= ~LViewFlags.CreationMode; lView[FLAGS] &= ~LViewFlags.CreationMode;
selectView(oldView, null); leaveView();
} }
} }
@ -372,7 +372,7 @@ export function renderView<T>(lView: LView, tView: TView, context: T): void {
export function refreshView<T>( export function refreshView<T>(
lView: LView, tView: TView, templateFn: ComponentTemplate<{}>| null, context: T) { lView: LView, tView: TView, templateFn: ComponentTemplate<{}>| null, context: T) {
ngDevMode && assertEqual(isCreationMode(lView), false, 'Should be run in update mode'); ngDevMode && assertEqual(isCreationMode(lView), false, 'Should be run in update mode');
const oldView = selectView(lView, lView[T_HOST]); enterView(lView, lView[T_HOST]);
const flags = lView[FLAGS]; const flags = lView[FLAGS];
try { try {
resetPreOrderHookFlags(lView); resetPreOrderHookFlags(lView);
@ -463,7 +463,7 @@ export function refreshView<T>(
} finally { } finally {
lView[FLAGS] &= ~(LViewFlags.Dirty | LViewFlags.FirstLViewPass); lView[FLAGS] &= ~(LViewFlags.Dirty | LViewFlags.FirstLViewPass);
selectView(oldView, null); leaveViewProcessExit();
} }
} }
@ -472,8 +472,6 @@ export function renderComponentOrTemplate<T>(
const rendererFactory = hostView[RENDERER_FACTORY]; const rendererFactory = hostView[RENDERER_FACTORY];
const normalExecutionPath = !getCheckNoChangesMode(); const normalExecutionPath = !getCheckNoChangesMode();
const creationModeIsActive = isCreationMode(hostView); const creationModeIsActive = isCreationMode(hostView);
const previousOrParentTNode = getPreviousOrParentTNode();
const isParent = getIsParent();
try { try {
if (normalExecutionPath && !creationModeIsActive && rendererFactory.begin) { if (normalExecutionPath && !creationModeIsActive && rendererFactory.begin) {
rendererFactory.begin(); rendererFactory.begin();
@ -487,13 +485,11 @@ export function renderComponentOrTemplate<T>(
if (normalExecutionPath && !creationModeIsActive && rendererFactory.end) { if (normalExecutionPath && !creationModeIsActive && rendererFactory.end) {
rendererFactory.end(); rendererFactory.end();
} }
setPreviousOrParentTNode(previousOrParentTNode, isParent);
} }
} }
function executeTemplate<T>( function executeTemplate<T>(
lView: LView, templateFn: ComponentTemplate<T>, rf: RenderFlags, context: T) { lView: LView, templateFn: ComponentTemplate<T>, rf: RenderFlags, context: T) {
namespaceHTMLInternal();
const prevSelectedIndex = getSelectedIndex(); const prevSelectedIndex = getSelectedIndex();
try { try {
setActiveHostElement(null); setActiveHostElement(null);
@ -1659,9 +1655,6 @@ export function tickRootContext(rootContext: RootContext) {
export function detectChangesInternal<T>(view: LView, context: T) { export function detectChangesInternal<T>(view: LView, context: T) {
const rendererFactory = view[RENDERER_FACTORY]; const rendererFactory = view[RENDERER_FACTORY];
const previousOrParentTNode = getPreviousOrParentTNode();
const isParent = getIsParent();
if (rendererFactory.begin) rendererFactory.begin(); if (rendererFactory.begin) rendererFactory.begin();
try { try {
const tView = view[TVIEW]; const tView = view[TVIEW];
@ -1671,7 +1664,6 @@ export function detectChangesInternal<T>(view: LView, context: T) {
throw error; throw error;
} finally { } finally {
if (rendererFactory.end) rendererFactory.end(); if (rendererFactory.end) rendererFactory.end();
setPreviousOrParentTNode(previousOrParentTNode, isParent);
} }
} }
@ -1684,18 +1676,6 @@ export function detectChangesInRootView(lView: LView): void {
tickRootContext(lView[CONTEXT] as RootContext); tickRootContext(lView[CONTEXT] as RootContext);
} }
/**
* Checks the change detector and its children, and throws if any changes are detected.
*
* This is used in development mode to verify that running change detection doesn't
* introduce other changes.
*/
export function checkNoChanges<T>(component: T): void {
const view = getComponentViewByInstance(component);
checkNoChangesInternal<T>(view, component);
}
export function checkNoChangesInternal<T>(view: LView, context: T) { export function checkNoChangesInternal<T>(view: LView, context: T) {
setCheckNoChangesMode(true); setCheckNoChangesMode(true);
try { try {

View File

@ -11,19 +11,28 @@ import {assertDefined, assertEqual} from '../util/assert';
import {assertLViewOrUndefined} from './assert'; import {assertLViewOrUndefined} from './assert';
import {ComponentDef, DirectiveDef} from './interfaces/definition'; import {ComponentDef, DirectiveDef} from './interfaces/definition';
import {TElementNode, TNode, TViewNode} from './interfaces/node'; import {TNode} from './interfaces/node';
import {CONTEXT, DECLARATION_VIEW, LView, OpaqueViewState} from './interfaces/view'; import {BINDING_INDEX, CONTEXT, DECLARATION_VIEW, LView, OpaqueViewState, TVIEW} from './interfaces/view';
/** /**
* All implicit instruction state is stored here.
* *
* It is useful to have a single object where all of the state is stored as a mental model
* (rather it being spread across many different variables.)
*
* PERF NOTE: Turns out that writing to a true global variable is slower than
* having an intermediate object with properties.
*/ */
interface InstructionState { interface LFrame {
/**
* Parent LFrame.
*
* This is needed when `leaveView` is called to restore the previous state.
*/
parent: LFrame;
/**
* Child LFrame.
*
* This is used to cache existing LFrames to relieve the memory pressure.
*/
child: LFrame|null;
/** /**
* State of the current view being processed. * State of the current view being processed.
* *
@ -61,39 +70,14 @@ interface InstructionState {
*/ */
contextLView: LView; contextLView: LView;
/**
* In this mode, any changes in bindings will throw an ExpressionChangedAfterChecked error.
*
* Necessary to support ChangeDetectorRef.checkNoChanges().
*/
checkNoChangesMode: boolean;
/** /**
* Store the element depth count. This is used to identify the root elements of the template * Store the element depth count. This is used to identify the root elements of the template
* so that we can then attach `LView` to only those elements. * so that we can then attach patch data `LView` to only those elements. We know that those
* are the only places where the patch data could change, this way we will save on number
* of places where tha patching occurs.
*/ */
elementDepthCount: number; elementDepthCount: number;
/**
* Stores whether directives should be matched to elements.
*
* When template contains `ngNonBindable` then we need to prevent the runtime form matching
* directives on children of that element.
*
* Example:
* ```
* <my-comp my-directive>
* Should match component / directive.
* </my-comp>
* <div ngNonBindable>
* <my-comp my-directive>
* Should not match component / directive because we are in ngNonBindable.
* </my-comp>
* </div>
* ```
*/
bindingsEnabled: boolean;
/** /**
* Current namespace to be used when creating elements * Current namespace to be used when creating elements
*/ */
@ -131,7 +115,51 @@ interface InstructionState {
* We iterate over the list of Queries and increment current query index at every step. * We iterate over the list of Queries and increment current query index at every step.
*/ */
currentQueryIndex: number; currentQueryIndex: number;
}
/**
* All implicit instruction state is stored here.
*
* It is useful to have a single object where all of the state is stored as a mental model
* (rather it being spread across many different variables.)
*
* PERF NOTE: Turns out that writing to a true global variable is slower than
* having an intermediate object with properties.
*/
interface InstructionState {
/**
* Current `LFrame`
*
* `null` if we have not called `enterView`
*/
lFrame: LFrame;
/**
* Stores whether directives should be matched to elements.
*
* When template contains `ngNonBindable` then we need to prevent the runtime from matching
* directives on children of that element.
*
* Example:
* ```
* <my-comp my-directive>
* Should match component / directive.
* </my-comp>
* <div ngNonBindable>
* <my-comp my-directive>
* Should not match component / directive because we are in ngNonBindable.
* </my-comp>
* </div>
* ```
*/
bindingsEnabled: boolean;
/**
* In this mode, any changes in bindings will throw an ExpressionChangedAfterChecked error.
*
* Necessary to support ChangeDetectorRef.checkNoChanges().
*/
checkNoChangesMode: boolean;
/** /**
* Function to be called when the element is exited. * Function to be called when the element is exited.
@ -142,49 +170,34 @@ interface InstructionState {
} }
export const instructionState: InstructionState = { export const instructionState: InstructionState = {
previousOrParentTNode: null !, lFrame: createLFrame(null),
isParent: null !,
lView: null !,
// tslint:disable-next-line: no-toplevel-property-access
selectedIndex: -1 << ActiveElementFlags.Size,
contextLView: null !,
checkNoChangesMode: false,
elementDepthCount: 0,
bindingsEnabled: true, bindingsEnabled: true,
currentNamespace: null,
currentSanitizer: null,
currentDirectiveDef: null,
activeDirectiveId: 0,
bindingRootIndex: -1,
currentQueryIndex: 0,
elementExitFn: null, elementExitFn: null,
checkNoChangesMode: false,
}; };
export function getElementDepthCount() { export function getElementDepthCount() {
// top level variables should not be exported for performance reasons (PERF_NOTES.md) return instructionState.lFrame.elementDepthCount;
return instructionState.elementDepthCount;
} }
export function increaseElementDepthCount() { export function increaseElementDepthCount() {
instructionState.elementDepthCount++; instructionState.lFrame.elementDepthCount++;
} }
export function decreaseElementDepthCount() { export function decreaseElementDepthCount() {
instructionState.elementDepthCount--; instructionState.lFrame.elementDepthCount--;
} }
export function getCurrentDirectiveDef(): DirectiveDef<any>|ComponentDef<any>|null { export function getCurrentDirectiveDef(): DirectiveDef<any>|ComponentDef<any>|null {
// top level variables should not be exported for performance reasons (PERF_NOTES.md) return instructionState.lFrame.currentDirectiveDef;
return instructionState.currentDirectiveDef;
} }
export function setCurrentDirectiveDef(def: DirectiveDef<any>| ComponentDef<any>| null): void { export function setCurrentDirectiveDef(def: DirectiveDef<any>| ComponentDef<any>| null): void {
instructionState.currentDirectiveDef = def; instructionState.lFrame.currentDirectiveDef = def;
} }
export function getBindingsEnabled(): boolean { export function getBindingsEnabled(): boolean {
// top level variables should not be exported for performance reasons (PERF_NOTES.md)
return instructionState.bindingsEnabled; return instructionState.bindingsEnabled;
} }
@ -235,8 +248,16 @@ export function ɵɵdisableBindings(): void {
instructionState.bindingsEnabled = false; instructionState.bindingsEnabled = false;
} }
/**
* Return the current LView.
*
* The return value can be `null` if the method is called outside of template. This can happen if
* directive is instantiated by module injector (rather than by node injector.)
*/
export function getLView(): LView { export function getLView(): LView {
return instructionState.lView; // TODO(misko): the return value should be `LView|null` but doing so breaks a lot of code.
const lFrame = instructionState.lFrame;
return lFrame === null ? null ! : lFrame.lView;
} }
/** /**
@ -259,14 +280,14 @@ export const enum ActiveElementFlags {
* Determines whether or not a flag is currently set for the active element. * Determines whether or not a flag is currently set for the active element.
*/ */
export function hasActiveElementFlag(flag: ActiveElementFlags) { export function hasActiveElementFlag(flag: ActiveElementFlags) {
return (instructionState.selectedIndex & flag) === flag; return (instructionState.lFrame.selectedIndex & flag) === flag;
} }
/** /**
* Sets a flag is for the active element. * Sets a flag is for the active element.
*/ */
export function setActiveElementFlag(flag: ActiveElementFlags) { function setActiveElementFlag(flag: ActiveElementFlags) {
instructionState.selectedIndex |= flag; instructionState.lFrame.selectedIndex |= flag;
} }
/** /**
@ -277,20 +298,16 @@ export function setActiveElementFlag(flag: ActiveElementFlags) {
* the directive/component instance lives * the directive/component instance lives
*/ */
export function setActiveHostElement(elementIndex: number | null = null) { export function setActiveHostElement(elementIndex: number | null = null) {
if (getSelectedIndex() !== elementIndex) { if (hasActiveElementFlag(ActiveElementFlags.RunExitFn)) {
if (hasActiveElementFlag(ActiveElementFlags.RunExitFn)) { executeElementExitFn();
executeElementExitFn();
}
setSelectedIndex(elementIndex === null ? -1 : elementIndex);
instructionState.activeDirectiveId = 0;
} }
setSelectedIndex(elementIndex === null ? -1 : elementIndex);
instructionState.lFrame.activeDirectiveId = 0;
} }
export function executeElementExitFn() { export function executeElementExitFn() {
instructionState.elementExitFn !(); instructionState.elementExitFn !();
// TODO (matsko|misko): remove this unassignment once the state management of instructionState.lFrame.selectedIndex &= ~ActiveElementFlags.RunExitFn;
// global variables are better managed.
instructionState.selectedIndex &= ~ActiveElementFlags.RunExitFn;
} }
/** /**
@ -331,7 +348,7 @@ export function setElementExitFn(fn: () => void): void {
* different set of directives). * different set of directives).
*/ */
export function getActiveDirectiveId() { export function getActiveDirectiveId() {
return instructionState.activeDirectiveId; return instructionState.lFrame.activeDirectiveId;
} }
/** /**
@ -360,7 +377,7 @@ export function incrementActiveDirectiveId() {
// directive uniqueId is not set anywhere--it is just incremented between // directive uniqueId is not set anywhere--it is just incremented between
// each hostBindings call and is useful for helping instruction code // each hostBindings call and is useful for helping instruction code
// uniquely determine which directive is currently active when executed. // uniquely determine which directive is currently active when executed.
instructionState.activeDirectiveId += 1; instructionState.lFrame.activeDirectiveId += 1;
} }
/** /**
@ -375,44 +392,34 @@ export function incrementActiveDirectiveId() {
* @codeGenApi * @codeGenApi
*/ */
export function ɵɵrestoreView(viewToRestore: OpaqueViewState) { export function ɵɵrestoreView(viewToRestore: OpaqueViewState) {
instructionState.contextLView = viewToRestore as any as LView; instructionState.lFrame.contextLView = viewToRestore as any as LView;
} }
export function getPreviousOrParentTNode(): TNode { export function getPreviousOrParentTNode(): TNode {
// top level variables should not be exported for performance reasons (PERF_NOTES.md) return instructionState.lFrame.previousOrParentTNode;
return instructionState.previousOrParentTNode;
} }
export function setPreviousOrParentTNode(tNode: TNode, _isParent: boolean) { export function setPreviousOrParentTNode(tNode: TNode, _isParent: boolean) {
instructionState.previousOrParentTNode = tNode; instructionState.lFrame.previousOrParentTNode = tNode;
instructionState.isParent = _isParent; instructionState.lFrame.isParent = _isParent;
}
export function setTNodeAndViewData(tNode: TNode, view: LView) {
ngDevMode && assertLViewOrUndefined(view);
instructionState.previousOrParentTNode = tNode;
instructionState.lView = view;
} }
export function getIsParent(): boolean { export function getIsParent(): boolean {
// top level variables should not be exported for performance reasons (PERF_NOTES.md) return instructionState.lFrame.isParent;
return instructionState.isParent;
} }
export function setIsNotParent(): void { export function setIsNotParent(): void {
instructionState.isParent = false; instructionState.lFrame.isParent = false;
} }
export function setIsParent(): void { export function setIsParent(): void {
instructionState.isParent = true; instructionState.lFrame.isParent = true;
} }
export function getContextLView(): LView { export function getContextLView(): LView {
// top level variables should not be exported for performance reasons (PERF_NOTES.md) return instructionState.lFrame.contextLView;
return instructionState.contextLView;
} }
export function getCheckNoChangesMode(): boolean { export function getCheckNoChangesMode(): boolean {
// top level variables should not be exported for performance reasons (PERF_NOTES.md)
return instructionState.checkNoChangesMode; return instructionState.checkNoChangesMode;
} }
@ -422,22 +429,63 @@ export function setCheckNoChangesMode(mode: boolean): void {
// top level variables should not be exported for performance reasons (PERF_NOTES.md) // top level variables should not be exported for performance reasons (PERF_NOTES.md)
export function getBindingRoot() { export function getBindingRoot() {
return instructionState.bindingRootIndex; const lFrame = instructionState.lFrame;
let index = lFrame.bindingRootIndex;
if (index === -1) {
const lView = lFrame.lView;
index = lFrame.bindingRootIndex = lView[BINDING_INDEX] = lView[TVIEW].bindingStartIndex;
}
return index;
} }
export function setBindingRoot(value: number) { export function setBindingRoot(value: number) {
instructionState.bindingRootIndex = value; instructionState.lFrame.bindingRootIndex = value;
} }
export function getCurrentQueryIndex(): number { export function getCurrentQueryIndex(): number {
// top level variables should not be exported for performance reasons (PERF_NOTES.md) return instructionState.lFrame.currentQueryIndex;
return instructionState.currentQueryIndex;
} }
export function setCurrentQueryIndex(value: number): void { export function setCurrentQueryIndex(value: number): void {
instructionState.currentQueryIndex = value; instructionState.lFrame.currentQueryIndex = value;
} }
/**
* This is a light weight version of the `enterView` which is needed by the DI system.
* @param newView
* @param tNode
*/
export function enterDI(newView: LView, tNode: TNode) {
ngDevMode && assertLViewOrUndefined(newView);
const newLFrame = allocLFrame();
instructionState.lFrame = newLFrame;
newLFrame.previousOrParentTNode = tNode !;
newLFrame.lView = newView;
if (ngDevMode) {
// resetting for safety in dev mode only.
newLFrame.isParent = DEV_MODE_VALUE;
newLFrame.selectedIndex = DEV_MODE_VALUE;
newLFrame.contextLView = DEV_MODE_VALUE;
newLFrame.elementDepthCount = DEV_MODE_VALUE;
newLFrame.currentNamespace = DEV_MODE_VALUE;
newLFrame.currentSanitizer = DEV_MODE_VALUE;
newLFrame.currentDirectiveDef = DEV_MODE_VALUE;
newLFrame.activeDirectiveId = DEV_MODE_VALUE;
newLFrame.bindingRootIndex = DEV_MODE_VALUE;
newLFrame.currentQueryIndex = DEV_MODE_VALUE;
}
}
const DEV_MODE_VALUE: any =
'Value indicating that DI is trying to read value which it should not need to know about.';
/**
* This is a light weight version of the `leaveView` which is needed by the DI system.
*
* Because the implementation is same it is only an alias
*/
export const leaveDI = leaveView;
/** /**
* Swap the current lView with a new lView. * Swap the current lView with a new lView.
* *
@ -447,27 +495,72 @@ export function setCurrentQueryIndex(value: number): void {
* exited the state has to be restored * exited the state has to be restored
* *
* @param newView New lView to become active * @param newView New lView to become active
* @param host Element to which the View is a child of * @param tNode Element to which the View is a child of
* @returns the previously active lView; * @returns the previously active lView;
*/ */
export function selectView(newView: LView, hostTNode: TElementNode | TViewNode | null): LView { export function enterView(newView: LView, tNode: TNode | null): void {
ngDevMode && assertLViewOrUndefined(newView);
const newLFrame = allocLFrame();
instructionState.lFrame = newLFrame;
newLFrame.previousOrParentTNode = tNode !;
newLFrame.isParent = true;
newLFrame.lView = newView;
newLFrame.selectedIndex = 0;
newLFrame.contextLView = newView !;
newLFrame.elementDepthCount = 0;
newLFrame.currentNamespace = null;
newLFrame.currentSanitizer = null;
newLFrame.currentDirectiveDef = null;
newLFrame.activeDirectiveId = 0;
newLFrame.bindingRootIndex = -1;
newLFrame.currentQueryIndex = 0;
}
/**
* Allocates next free LFrame. This function tries to reuse the `LFrame`s to lower memory pressure.
*/
function allocLFrame() {
const currentLFrame = instructionState.lFrame;
const childLFrame = currentLFrame === null ? null : currentLFrame.child;
const newLFrame = childLFrame === null ? createLFrame(currentLFrame) : childLFrame;
return newLFrame;
}
function createLFrame(parent: LFrame | null): LFrame {
const lFrame: LFrame = {
previousOrParentTNode: null !, //
isParent: true, //
lView: null !, //
selectedIndex: 0, //
contextLView: null !, //
elementDepthCount: 0, //
currentNamespace: null, //
currentSanitizer: null, //
currentDirectiveDef: null, //
activeDirectiveId: 0, //
bindingRootIndex: -1, //
currentQueryIndex: 0, //
parent: parent !, //
child: null, //
};
parent !== null && (parent.child = lFrame); // link the new LFrame for reuse.
return lFrame;
}
export function leaveViewProcessExit() {
if (hasActiveElementFlag(ActiveElementFlags.RunExitFn)) { if (hasActiveElementFlag(ActiveElementFlags.RunExitFn)) {
executeElementExitFn(); executeElementExitFn();
} }
leaveView();
}
ngDevMode && assertLViewOrUndefined(newView); export function leaveView() {
const oldView = instructionState.lView; instructionState.lFrame = instructionState.lFrame.parent;
instructionState.previousOrParentTNode = hostTNode !;
instructionState.isParent = true;
instructionState.lView = instructionState.contextLView = newView;
return oldView;
} }
export function nextContextImpl<T = any>(level: number = 1): T { export function nextContextImpl<T = any>(level: number = 1): T {
instructionState.contextLView = walkUpViews(level, instructionState.contextLView !); instructionState.lFrame.contextLView = walkUpViews(level, instructionState.lFrame.contextLView !);
return instructionState.contextLView[CONTEXT] as T; return instructionState.lFrame.contextLView[CONTEXT] as T;
} }
function walkUpViews(nestingLevel: number, currentView: LView): LView { function walkUpViews(nestingLevel: number, currentView: LView): LView {
@ -481,17 +574,6 @@ function walkUpViews(nestingLevel: number, currentView: LView): LView {
return currentView; return currentView;
} }
/**
* Resets the application state.
*/
export function resetComponentState() {
instructionState.isParent = false;
instructionState.previousOrParentTNode = null !;
instructionState.elementDepthCount = 0;
instructionState.bindingsEnabled = true;
setCurrentStyleSanitizer(null);
}
/** /**
* Gets the most recent index passed to {@link select} * Gets the most recent index passed to {@link select}
* *
@ -499,7 +581,7 @@ export function resetComponentState() {
* current `LView` to act on. * current `LView` to act on.
*/ */
export function getSelectedIndex() { export function getSelectedIndex() {
return instructionState.selectedIndex >> ActiveElementFlags.Size; return instructionState.lFrame.selectedIndex >> ActiveElementFlags.Size;
} }
/** /**
@ -512,7 +594,7 @@ export function getSelectedIndex() {
* run if and when the provided `index` value is different from the current selected index value.) * run if and when the provided `index` value is different from the current selected index value.)
*/ */
export function setSelectedIndex(index: number) { export function setSelectedIndex(index: number) {
instructionState.selectedIndex = index << ActiveElementFlags.Size; instructionState.lFrame.selectedIndex = index << ActiveElementFlags.Size;
} }
@ -522,7 +604,7 @@ export function setSelectedIndex(index: number) {
* @codeGenApi * @codeGenApi
*/ */
export function ɵɵnamespaceSVG() { export function ɵɵnamespaceSVG() {
instructionState.currentNamespace = 'http://www.w3.org/2000/svg'; instructionState.lFrame.currentNamespace = 'http://www.w3.org/2000/svg';
} }
/** /**
@ -531,7 +613,7 @@ export function ɵɵnamespaceSVG() {
* @codeGenApi * @codeGenApi
*/ */
export function ɵɵnamespaceMathML() { export function ɵɵnamespaceMathML() {
instructionState.currentNamespace = 'http://www.w3.org/1998/MathML/'; instructionState.lFrame.currentNamespace = 'http://www.w3.org/1998/MathML/';
} }
/** /**
@ -549,15 +631,15 @@ export function ɵɵnamespaceHTML() {
* `createElement` rather than `createElementNS`. * `createElement` rather than `createElementNS`.
*/ */
export function namespaceHTMLInternal() { export function namespaceHTMLInternal() {
instructionState.currentNamespace = null; instructionState.lFrame.currentNamespace = null;
} }
export function getNamespace(): string|null { export function getNamespace(): string|null {
return instructionState.currentNamespace; return instructionState.lFrame.currentNamespace;
} }
export function setCurrentStyleSanitizer(sanitizer: StyleSanitizeFn | null) { export function setCurrentStyleSanitizer(sanitizer: StyleSanitizeFn | null) {
instructionState.currentSanitizer = sanitizer; instructionState.lFrame.currentSanitizer = sanitizer;
} }
export function resetCurrentStyleSanitizer() { export function resetCurrentStyleSanitizer() {
@ -565,5 +647,8 @@ export function resetCurrentStyleSanitizer() {
} }
export function getCurrentStyleSanitizer() { export function getCurrentStyleSanitizer() {
return instructionState.currentSanitizer; // TODO(misko): This should throw when there is no LView, but it turns out we can get here from
// `NodeStyleDebug` hence we return `null`. This should be fixed
const lFrame = instructionState.lFrame;
return lFrame === null ? null : lFrame.currentSanitizer;
} }

View File

@ -2105,7 +2105,7 @@ describe('styling', () => {
expect(readyChild).toBeTruthy(); expect(readyChild).toBeTruthy();
}); });
onlyInIvy('only ivy allows for multiple styles/classes to be balanaced across directives') onlyInIvy('only ivy allows for multiple styles/classes to be balanced across directives')
.it('should allow various duplicate properties to be defined in various styling maps within the template and directive styling bindings', .it('should allow various duplicate properties to be defined in various styling maps within the template and directive styling bindings',
() => { () => {
@Component({ @Component({

View File

@ -155,6 +155,9 @@
{ {
"name": "addToViewTree" "name": "addToViewTree"
}, },
{
"name": "allocLFrame"
},
{ {
"name": "allocStylingMapArray" "name": "allocStylingMapArray"
}, },
@ -185,6 +188,9 @@
{ {
"name": "createDirectivesInstances" "name": "createDirectivesInstances"
}, },
{
"name": "createLFrame"
},
{ {
"name": "createLView" "name": "createLView"
}, },
@ -227,6 +233,12 @@
{ {
"name": "elementCreate" "name": "elementCreate"
}, },
{
"name": "enterDI"
},
{
"name": "enterView"
},
{ {
"name": "executeCheckHooks" "name": "executeCheckHooks"
}, },
@ -479,6 +491,15 @@
{ {
"name": "isStylingValueDefined" "name": "isStylingValueDefined"
}, },
{
"name": "leaveDI"
},
{
"name": "leaveView"
},
{
"name": "leaveViewProcessExit"
},
{ {
"name": "locateHostElement" "name": "locateHostElement"
}, },
@ -488,9 +509,6 @@
{ {
"name": "matchTemplateAttribute" "name": "matchTemplateAttribute"
}, },
{
"name": "namespaceHTMLInternal"
},
{ {
"name": "nativeAppendChild" "name": "nativeAppendChild"
}, },
@ -560,9 +578,6 @@
{ {
"name": "renderView" "name": "renderView"
}, },
{
"name": "resetComponentState"
},
{ {
"name": "resetPreOrderHookFlags" "name": "resetPreOrderHookFlags"
}, },
@ -581,9 +596,6 @@
{ {
"name": "selectIndexInternal" "name": "selectIndexInternal"
}, },
{
"name": "selectView"
},
{ {
"name": "setActiveHostElement" "name": "setActiveHostElement"
}, },
@ -599,9 +611,6 @@
{ {
"name": "setCurrentQueryIndex" "name": "setCurrentQueryIndex"
}, },
{
"name": "setCurrentStyleSanitizer"
},
{ {
"name": "setDirectiveStylingInput" "name": "setDirectiveStylingInput"
}, },
@ -635,9 +644,6 @@
{ {
"name": "setStyle" "name": "setStyle"
}, },
{
"name": "setTNodeAndViewData"
},
{ {
"name": "setUpAttributes" "name": "setUpAttributes"
}, },

View File

@ -134,6 +134,9 @@
{ {
"name": "addToViewTree" "name": "addToViewTree"
}, },
{
"name": "allocLFrame"
},
{ {
"name": "appendChild" "name": "appendChild"
}, },
@ -152,6 +155,9 @@
{ {
"name": "callHooks" "name": "callHooks"
}, },
{
"name": "createLFrame"
},
{ {
"name": "createLView" "name": "createLView"
}, },
@ -188,6 +194,12 @@
{ {
"name": "domRendererFactory3" "name": "domRendererFactory3"
}, },
{
"name": "enterDI"
},
{
"name": "enterView"
},
{ {
"name": "executeCheckHooks" "name": "executeCheckHooks"
}, },
@ -350,15 +362,21 @@
{ {
"name": "isRootView" "name": "isRootView"
}, },
{
"name": "leaveDI"
},
{
"name": "leaveView"
},
{
"name": "leaveViewProcessExit"
},
{ {
"name": "locateHostElement" "name": "locateHostElement"
}, },
{ {
"name": "markAsComponentHost" "name": "markAsComponentHost"
}, },
{
"name": "namespaceHTMLInternal"
},
{ {
"name": "nativeAppendChild" "name": "nativeAppendChild"
}, },
@ -410,18 +428,12 @@
{ {
"name": "renderView" "name": "renderView"
}, },
{
"name": "resetComponentState"
},
{ {
"name": "resetPreOrderHookFlags" "name": "resetPreOrderHookFlags"
}, },
{ {
"name": "selectIndexInternal" "name": "selectIndexInternal"
}, },
{
"name": "selectView"
},
{ {
"name": "setActiveHostElement" "name": "setActiveHostElement"
}, },
@ -434,9 +446,6 @@
{ {
"name": "setCurrentQueryIndex" "name": "setCurrentQueryIndex"
}, },
{
"name": "setCurrentStyleSanitizer"
},
{ {
"name": "setHostBindings" "name": "setHostBindings"
}, },
@ -455,9 +464,6 @@
{ {
"name": "setSelectedIndex" "name": "setSelectedIndex"
}, },
{
"name": "setTNodeAndViewData"
},
{ {
"name": "stringifyForError" "name": "stringifyForError"
}, },

View File

@ -362,6 +362,9 @@
{ {
"name": "addToViewTree" "name": "addToViewTree"
}, },
{
"name": "allocLFrame"
},
{ {
"name": "allocStylingMapArray" "name": "allocStylingMapArray"
}, },
@ -461,6 +464,9 @@
{ {
"name": "createLContext" "name": "createLContext"
}, },
{
"name": "createLFrame"
},
{ {
"name": "createLView" "name": "createLView"
}, },
@ -527,6 +533,12 @@
{ {
"name": "elementPropertyInternal" "name": "elementPropertyInternal"
}, },
{
"name": "enterDI"
},
{
"name": "enterView"
},
{ {
"name": "executeCheckHooks" "name": "executeCheckHooks"
}, },
@ -977,6 +989,15 @@
{ {
"name": "iterateListLike" "name": "iterateListLike"
}, },
{
"name": "leaveDI"
},
{
"name": "leaveView"
},
{
"name": "leaveViewProcessExit"
},
{ {
"name": "listenerInternal" "name": "listenerInternal"
}, },
@ -1022,9 +1043,6 @@
{ {
"name": "matchTemplateAttribute" "name": "matchTemplateAttribute"
}, },
{
"name": "namespaceHTMLInternal"
},
{ {
"name": "nativeAppendChild" "name": "nativeAppendChild"
}, },
@ -1139,9 +1157,6 @@
{ {
"name": "renderView" "name": "renderView"
}, },
{
"name": "resetComponentState"
},
{ {
"name": "resetCurrentStyleSanitizer" "name": "resetCurrentStyleSanitizer"
}, },
@ -1175,9 +1190,6 @@
{ {
"name": "selectIndexInternal" "name": "selectIndexInternal"
}, },
{
"name": "selectView"
},
{ {
"name": "setActiveElementFlag" "name": "setActiveElementFlag"
}, },
@ -1247,9 +1259,6 @@
{ {
"name": "setStyle" "name": "setStyle"
}, },
{
"name": "setTNodeAndViewData"
},
{ {
"name": "setUpAttributes" "name": "setUpAttributes"
}, },

View File

@ -17,7 +17,7 @@ import {TNODE} from '../../src/render3/interfaces/injector';
import {TNodeType} from '../../src/render3/interfaces/node'; import {TNodeType} from '../../src/render3/interfaces/node';
import {isProceduralRenderer} from '../../src/render3/interfaces/renderer'; import {isProceduralRenderer} from '../../src/render3/interfaces/renderer';
import {LViewFlags, TVIEW} from '../../src/render3/interfaces/view'; import {LViewFlags, TVIEW} from '../../src/render3/interfaces/view';
import {selectView} from '../../src/render3/state'; import {enterView, leaveViewProcessExit} from '../../src/render3/state';
import {getRendererFactory2} from './imported_renderer2'; import {getRendererFactory2} from './imported_renderer2';
import {ComponentFixture, createComponent, createDirective} from './render_util'; import {ComponentFixture, createComponent, createDirective} from './render_util';
@ -225,7 +225,7 @@ describe('di', () => {
const contentView = createLView( const contentView = createLView(
null, createTView(-1, null, 1, 0, null, null, null, null, null), null, null, createTView(-1, null, 1, 0, null, null, null, null, null), null,
LViewFlags.CheckAlways, null, null, {} as any, {} as any); LViewFlags.CheckAlways, null, null, {} as any, {} as any);
const oldView = selectView(contentView, null); enterView(contentView, null);
try { try {
const parentTNode = const parentTNode =
getOrCreateTNode(contentView[TVIEW], null, 0, TNodeType.Element, null, null); getOrCreateTNode(contentView[TVIEW], null, 0, TNodeType.Element, null, null);
@ -237,7 +237,7 @@ describe('di', () => {
const injector = getOrCreateNodeInjectorForNode(parentTNode, contentView); const injector = getOrCreateNodeInjectorForNode(parentTNode, contentView);
expect(injector).not.toEqual(-1); expect(injector).not.toEqual(-1);
} finally { } finally {
selectView(oldView, null); leaveViewProcessExit();
} }
}); });
}); });

View File

@ -9,7 +9,6 @@ import {ɵɵdefineDirective, ɵɵelementEnd, ɵɵelementStart, ɵɵtext} from '.
import {createTNode, createTView} from '../../../../src/render3/instructions/shared'; import {createTNode, createTView} from '../../../../src/render3/instructions/shared';
import {RenderFlags} from '../../../../src/render3/interfaces/definition'; import {RenderFlags} from '../../../../src/render3/interfaces/definition';
import {TNodeType, TViewNode} from '../../../../src/render3/interfaces/node'; import {TNodeType, TViewNode} from '../../../../src/render3/interfaces/node';
import {resetComponentState} from '../../../../src/render3/state';
import {createBenchmark} from '../micro_bench'; import {createBenchmark} from '../micro_bench';
import {createAndRenderLView} from '../setup'; import {createAndRenderLView} from '../setup';
@ -78,9 +77,6 @@ const embeddedTView = createTView(
-1, testTemplate, 21, 10, [Tooltip.ɵdir], null, null, null, -1, testTemplate, 21, 10, [Tooltip.ɵdir], null, null, null,
[['position', 'top', 3, 'tooltip']]); [['position', 'top', 3, 'tooltip']]);
// initialize global state
resetComponentState();
// create view once so we don't profile first template pass // create view once so we don't profile first template pass
createAndRenderLView(null, embeddedTView, viewTNode); createAndRenderLView(null, embeddedTView, viewTNode);

View File

@ -10,7 +10,6 @@ import {createTNode, createTView} from '../../../../src/render3/instructions/sha
import {ɵɵtext} from '../../../../src/render3/instructions/text'; import {ɵɵtext} from '../../../../src/render3/instructions/text';
import {RenderFlags} from '../../../../src/render3/interfaces/definition'; import {RenderFlags} from '../../../../src/render3/interfaces/definition';
import {TNodeType, TViewNode} from '../../../../src/render3/interfaces/node'; import {TNodeType, TViewNode} from '../../../../src/render3/interfaces/node';
import {resetComponentState} from '../../../../src/render3/state';
import {createBenchmark} from '../micro_bench'; import {createBenchmark} from '../micro_bench';
import {createAndRenderLView} from '../setup'; import {createAndRenderLView} from '../setup';
@ -69,9 +68,6 @@ const embeddedTView = createTView(-1, testTemplate, 21, 0, null, null, null, nul
['name1', 'value1', 'name2', 'value2', 'name3', 'value3', 'name4', 'value4', 'name5', 'value5'] ['name1', 'value1', 'name2', 'value2', 'name3', 'value3', 'name4', 'value4', 'name5', 'value5']
]); ]);
// initialize global state
resetComponentState();
// create view once so we don't profile first template pass // create view once so we don't profile first template pass
createAndRenderLView(null, embeddedTView, viewTNode); createAndRenderLView(null, embeddedTView, viewTNode);

View File

@ -10,7 +10,6 @@ import {ɵɵlistener} from '../../../../src/render3/instructions/listener';
import {createTNode, createTView} from '../../../../src/render3/instructions/shared'; import {createTNode, createTView} from '../../../../src/render3/instructions/shared';
import {RenderFlags} from '../../../../src/render3/interfaces/definition'; import {RenderFlags} from '../../../../src/render3/interfaces/definition';
import {TNodeType, TViewNode} from '../../../../src/render3/interfaces/node'; import {TNodeType, TViewNode} from '../../../../src/render3/interfaces/node';
import {resetComponentState} from '../../../../src/render3/state';
import {createBenchmark} from '../micro_bench'; import {createBenchmark} from '../micro_bench';
import {createAndRenderLView} from '../setup'; import {createAndRenderLView} from '../setup';
@ -79,9 +78,6 @@ const viewTNode = createTNode(null !, null, TNodeType.View, -1, null, null) as T
const embeddedTView = const embeddedTView =
createTView(-1, testTemplate, 11, 0, null, null, null, null, [[3, 'click', 'input']]); createTView(-1, testTemplate, 11, 0, null, null, null, null, [[3, 'click', 'input']]);
// initialize global state
resetComponentState();
// create view once so we don't profile first template pass // create view once so we don't profile first template pass
createAndRenderLView(null, embeddedTView, viewTNode); createAndRenderLView(null, embeddedTView, viewTNode);

View File

@ -18,7 +18,7 @@ const noopChangeDetection = createBenchmark('noop change detection');
const refreshTime = noopChangeDetection('refresh'); const refreshTime = noopChangeDetection('refresh');
// run change detection in the update mode // run change detection in the update mode
console.profile('noop_refresh'); console.profile('noop_change_detection');
while (refreshTime()) { while (refreshTime()) {
refreshView(rootLView, rootTView, null, null); refreshView(rootLView, rootTView, null, null);
} }

View File

@ -14,7 +14,7 @@ import {ViewContainerRef} from '@angular/core/src/linker/view_container_ref';
import {Renderer2} from '@angular/core/src/render/api'; import {Renderer2} from '@angular/core/src/render/api';
import {createLView, createTView, getOrCreateTNode, getOrCreateTView, renderComponentOrTemplate} from '@angular/core/src/render3/instructions/shared'; import {createLView, createTView, getOrCreateTNode, getOrCreateTView, renderComponentOrTemplate} from '@angular/core/src/render3/instructions/shared';
import {TAttributes, TNodeType} from '@angular/core/src/render3/interfaces/node'; import {TAttributes, TNodeType} from '@angular/core/src/render3/interfaces/node';
import {getLView, resetComponentState, selectView} from '@angular/core/src/render3/state'; import {enterView, getLView} from '@angular/core/src/render3/state';
import {stringifyElement} from '@angular/platform-browser/testing/src/browser_util'; import {stringifyElement} from '@angular/platform-browser/testing/src/browser_util';
import {SWITCH_CHANGE_DETECTOR_REF_FACTORY__POST_R3__ as R3_CHANGE_DETECTOR_REF_FACTORY} from '../../src/change_detection/change_detector_ref'; import {SWITCH_CHANGE_DETECTOR_REF_FACTORY__POST_R3__ as R3_CHANGE_DETECTOR_REF_FACTORY} from '../../src/change_detection/change_detector_ref';
@ -32,7 +32,7 @@ import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveT
import {DirectiveDefList, DirectiveDefListOrFactory, DirectiveTypesOrFactory, HostBindingsFunction, PipeDef, PipeDefList, PipeDefListOrFactory, PipeTypesOrFactory} from '../../src/render3/interfaces/definition'; import {DirectiveDefList, DirectiveDefListOrFactory, DirectiveTypesOrFactory, HostBindingsFunction, PipeDef, PipeDefList, PipeDefListOrFactory, PipeTypesOrFactory} from '../../src/render3/interfaces/definition';
import {PlayerHandler} from '../../src/render3/interfaces/player'; import {PlayerHandler} from '../../src/render3/interfaces/player';
import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, RendererFactory3, RendererStyleFlags3, domRendererFactory3} from '../../src/render3/interfaces/renderer'; import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, RendererFactory3, RendererStyleFlags3, domRendererFactory3} from '../../src/render3/interfaces/renderer';
import {HEADER_OFFSET, LView, LViewFlags, TVIEW, T_HOST} from '../../src/render3/interfaces/view'; import {HEADER_OFFSET, LView, LViewFlags, T_HOST} from '../../src/render3/interfaces/view';
import {destroyLView} from '../../src/render3/node_manipulation'; import {destroyLView} from '../../src/render3/node_manipulation';
import {getRootView} from '../../src/render3/util/view_traversal_utils'; import {getRootView} from '../../src/render3/util/view_traversal_utils';
import {Sanitizer} from '../../src/sanitization/sanitizer'; import {Sanitizer} from '../../src/sanitization/sanitizer';
@ -251,7 +251,6 @@ export function renderTemplate<T>(
directives?: DirectiveDefListOrFactory | null, pipes?: PipeDefListOrFactory | null, directives?: DirectiveDefListOrFactory | null, pipes?: PipeDefListOrFactory | null,
sanitizer?: Sanitizer | null, consts?: TAttributes[]): LView { sanitizer?: Sanitizer | null, consts?: TAttributes[]): LView {
if (componentView === null) { if (componentView === null) {
resetComponentState();
const renderer = providedRendererFactory.createRenderer(null, null); 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 // We need to create a root view so it's possible to look up the host element through its index
@ -259,7 +258,7 @@ export function renderTemplate<T>(
const hostLView = createLView( const hostLView = createLView(
null, tView, {}, LViewFlags.CheckAlways | LViewFlags.IsRoot, null, null, null, tView, {}, LViewFlags.CheckAlways | LViewFlags.IsRoot, null, null,
providedRendererFactory, renderer); providedRendererFactory, renderer);
selectView(hostLView, null); // SUSPECT! why do we need to enter the View? enterView(hostLView, null);
const def: ComponentDef<any> = ɵɵdefineComponent({ const def: ComponentDef<any> = ɵɵdefineComponent({
selectors: [], selectors: [],

View File

@ -8,8 +8,8 @@
*/ */
import {SECURITY_SCHEMA} from '@angular/compiler/src/schema/dom_security_schema'; import {SECURITY_SCHEMA} from '@angular/compiler/src/schema/dom_security_schema';
import {HEADER_OFFSET, LView} from '@angular/core/src/render3/interfaces/view'; import {LView} from '@angular/core/src/render3/interfaces/view';
import {setTNodeAndViewData} from '@angular/core/src/render3/state'; import {enterView, leaveView} from '@angular/core/src/render3/state';
import {bypassSanitizationTrustHtml, bypassSanitizationTrustResourceUrl, bypassSanitizationTrustScript, bypassSanitizationTrustStyle, bypassSanitizationTrustUrl} from '../../src/sanitization/bypass'; import {bypassSanitizationTrustHtml, bypassSanitizationTrustResourceUrl, bypassSanitizationTrustScript, bypassSanitizationTrustStyle, bypassSanitizationTrustUrl} from '../../src/sanitization/bypass';
import {getUrlSanitizer, ɵɵsanitizeHtml, ɵɵsanitizeResourceUrl, ɵɵsanitizeScript, ɵɵsanitizeStyle, ɵɵsanitizeUrl, ɵɵsanitizeUrlOrResourceUrl} from '../../src/sanitization/sanitization'; import {getUrlSanitizer, ɵɵsanitizeHtml, ɵɵsanitizeResourceUrl, ɵɵsanitizeScript, ɵɵsanitizeStyle, ɵɵsanitizeUrl, ɵɵsanitizeUrlOrResourceUrl} from '../../src/sanitization/sanitization';
@ -20,8 +20,8 @@ function fakeLView(): LView {
} }
describe('sanitization', () => { describe('sanitization', () => {
beforeEach(() => setTNodeAndViewData(null !, fakeLView())); beforeEach(() => enterView(fakeLView(), null));
afterEach(() => setTNodeAndViewData(null !, null !)); afterEach(() => leaveView());
class Wrap { class Wrap {
constructor(private value: string) {} constructor(private value: string) {}
toString() { return this.value; } toString() { return this.value; }