refactor(ivy): move bindingIndex from LView to LFrame (#33235)
				
					
				
			`bindingIndex` stores the current location of the bindings in the template function. Because it used to be stored in `LView` that `LView` was not reentrant. This could happen if a binding was a getter and had a side-effect of calling `detectChanges()`. By moving the `bindingIndex` to `LFrame` where all of the global state is kept in reentrant way we correct the issue. PR Close #33235
This commit is contained in:
		
							parent
							
								
									c61f413477
								
							
						
					
					
						commit
						e16f75db56
					
				| @ -12,7 +12,7 @@ | ||||
|     "master": { | ||||
|       "uncompressed": { | ||||
|         "runtime-es2015": 1485, | ||||
|         "main-es2015": 14678, | ||||
|         "main-es2015": 14861, | ||||
|         "polyfills-es2015": 36808 | ||||
|       } | ||||
|     } | ||||
|  | ||||
| @ -25,9 +25,9 @@ import {TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeType, TProjecti | ||||
| import {RComment, RElement, RText} from './interfaces/renderer'; | ||||
| import {SanitizerFn} from './interfaces/sanitization'; | ||||
| import {isLContainer} from './interfaces/type_checks'; | ||||
| import {BINDING_INDEX, HEADER_OFFSET, LView, RENDERER, TVIEW, TView, T_HOST} from './interfaces/view'; | ||||
| import {HEADER_OFFSET, LView, RENDERER, TVIEW, TView, T_HOST} from './interfaces/view'; | ||||
| import {appendChild, applyProjection, createTextNode, nativeRemoveNode} from './node_manipulation'; | ||||
| import {getIsParent, getLView, getPreviousOrParentTNode, setIsNotParent, setPreviousOrParentTNode} from './state'; | ||||
| import {getBindingIndex, getIsParent, getLView, getPreviousOrParentTNode, nextBindingIndex, setIsNotParent, setPreviousOrParentTNode} from './state'; | ||||
| import {renderStringify} from './util/misc_utils'; | ||||
| import {getNativeByIndex, getNativeByTNode, getTNode, load} from './util/view_utils'; | ||||
| 
 | ||||
| @ -669,7 +669,7 @@ export function ɵɵi18nEnd(): void { | ||||
|  */ | ||||
| function i18nEndFirstPass(lView: LView, tView: TView) { | ||||
|   ngDevMode && assertEqual( | ||||
|                    lView[BINDING_INDEX], tView.bindingStartIndex, | ||||
|                    getBindingIndex(), tView.bindingStartIndex, | ||||
|                    'i18nEnd should be called before any binding'); | ||||
| 
 | ||||
|   const rootIndex = i18nIndexStack[i18nIndexStackPointer--]; | ||||
| @ -1036,7 +1036,7 @@ let shiftsCounter = 0; | ||||
|  */ | ||||
| export function ɵɵi18nExp<T>(value: T): TsickleIssue1009 { | ||||
|   const lView = getLView(); | ||||
|   if (bindingUpdated(lView, lView[BINDING_INDEX]++, value)) { | ||||
|   if (bindingUpdated(lView, nextBindingIndex(), value)) { | ||||
|     changeMask = changeMask | (1 << shiftsCounter); | ||||
|   } | ||||
|   shiftsCounter++; | ||||
| @ -1065,7 +1065,7 @@ export function ɵɵi18nApply(index: number) { | ||||
|       updateOpCodes = (tI18n as TI18n).update; | ||||
|       icus = (tI18n as TI18n).icus; | ||||
|     } | ||||
|     const bindingsStartIndex = lView[BINDING_INDEX] - shiftsCounter - 1; | ||||
|     const bindingsStartIndex = getBindingIndex() - shiftsCounter - 1; | ||||
|     readUpdateOpCodes(updateOpCodes, icus, bindingsStartIndex, changeMask, lView); | ||||
| 
 | ||||
|     // Reset changeMask & maskBit to default for the next update cycle
 | ||||
|  | ||||
| @ -7,8 +7,7 @@ | ||||
|  */ | ||||
| import {bindingUpdated} from '../bindings'; | ||||
| import {SanitizerFn} from '../interfaces/sanitization'; | ||||
| import {BINDING_INDEX} from '../interfaces/view'; | ||||
| import {getLView, getSelectedIndex} from '../state'; | ||||
| import {getLView, getSelectedIndex, nextBindingIndex} from '../state'; | ||||
| 
 | ||||
| import {TsickleIssue1009, elementAttributeInternal} from './shared'; | ||||
| 
 | ||||
| @ -31,7 +30,7 @@ export function ɵɵattribute( | ||||
|     name: string, value: any, sanitizer?: SanitizerFn | null, | ||||
|     namespace?: string): TsickleIssue1009 { | ||||
|   const lView = getLView(); | ||||
|   if (bindingUpdated(lView, lView[BINDING_INDEX]++, value)) { | ||||
|   if (bindingUpdated(lView, nextBindingIndex(), value)) { | ||||
|     elementAttributeInternal(getSelectedIndex(), name, value, lView, sanitizer, namespace); | ||||
|   } | ||||
|   return ɵɵattribute; | ||||
|  | ||||
| @ -11,12 +11,12 @@ import {attachPatchData} from '../context_discovery'; | ||||
| import {executeCheckHooks, executeInitAndCheckHooks, incrementInitPhaseFlags, registerPostOrderHooks} from '../hooks'; | ||||
| import {ACTIVE_INDEX, CONTAINER_HEADER_OFFSET, LContainer} from '../interfaces/container'; | ||||
| import {ComponentTemplate} from '../interfaces/definition'; | ||||
| import {LocalRefExtractor, TAttributes, TContainerNode, TNode, TNodeFlags, TNodeType, TViewNode} from '../interfaces/node'; | ||||
| import {LocalRefExtractor, TAttributes, TContainerNode, TNode, TNodeType, TViewNode} from '../interfaces/node'; | ||||
| import {isDirectiveHost} from '../interfaces/type_checks'; | ||||
| import {BINDING_INDEX, FLAGS, HEADER_OFFSET, InitPhaseState, LView, LViewFlags, RENDERER, TVIEW, T_HOST} from '../interfaces/view'; | ||||
| import {FLAGS, HEADER_OFFSET, InitPhaseState, LView, LViewFlags, RENDERER, TVIEW, T_HOST} from '../interfaces/view'; | ||||
| import {assertNodeType} from '../node_assert'; | ||||
| import {appendChild, removeView} from '../node_manipulation'; | ||||
| import {getCheckNoChangesMode, getIsParent, getLView, getPreviousOrParentTNode, setIsNotParent, setPreviousOrParentTNode} from '../state'; | ||||
| import {getBindingIndex, getCheckNoChangesMode, getIsParent, getLView, getPreviousOrParentTNode, setIsNotParent, setPreviousOrParentTNode} from '../state'; | ||||
| import {getNativeByTNode, load} from '../util/view_utils'; | ||||
| 
 | ||||
| import {addToViewTree, createDirectivesInstances, createLContainer, createTNode, createTView, getOrCreateTNode, resolveDirectives, saveResolvedLocalsInData} from './shared'; | ||||
| @ -172,7 +172,7 @@ function containerInternal( | ||||
|     lView: LView, nodeIndex: number, tagName: string | null, | ||||
|     attrs: TAttributes | null): TContainerNode { | ||||
|   ngDevMode && assertEqual( | ||||
|                    lView[BINDING_INDEX], lView[TVIEW].bindingStartIndex, | ||||
|                    getBindingIndex(), lView[TVIEW].bindingStartIndex, | ||||
|                    'container nodes should be created before any bindings'); | ||||
| 
 | ||||
|   const adjustedIndex = nodeIndex + HEADER_OFFSET; | ||||
|  | ||||
| @ -14,10 +14,10 @@ import {TAttributes, TNodeFlags, TNodeType} from '../interfaces/node'; | ||||
| import {RElement} from '../interfaces/renderer'; | ||||
| import {StylingMapArray, TStylingContext} from '../interfaces/styling'; | ||||
| import {isContentQueryHost, isDirectiveHost} from '../interfaces/type_checks'; | ||||
| import {BINDING_INDEX, HEADER_OFFSET, LView, RENDERER, TVIEW, T_HOST} from '../interfaces/view'; | ||||
| import {HEADER_OFFSET, LView, RENDERER, TVIEW, T_HOST} from '../interfaces/view'; | ||||
| import {assertNodeType} from '../node_assert'; | ||||
| import {appendChild} from '../node_manipulation'; | ||||
| import {decreaseElementDepthCount, getElementDepthCount, getIsParent, getLView, getNamespace, getPreviousOrParentTNode, getSelectedIndex, increaseElementDepthCount, setIsNotParent, setPreviousOrParentTNode} from '../state'; | ||||
| import {decreaseElementDepthCount, getBindingIndex, getElementDepthCount, getIsParent, getLView, getNamespace, getPreviousOrParentTNode, getSelectedIndex, increaseElementDepthCount, setIsNotParent, setPreviousOrParentTNode} from '../state'; | ||||
| import {setUpAttributes} from '../util/attrs_utils'; | ||||
| import {getInitialStylingValue, hasClassInput, hasStyleInput, selectClassBasedInputName} from '../util/styling_utils'; | ||||
| import {getNativeByTNode, getTNode} from '../util/view_utils'; | ||||
| @ -48,7 +48,7 @@ export function ɵɵelementStart( | ||||
|   const tViewConsts = tView.consts; | ||||
|   const consts = tViewConsts === null || constsIndex == null ? null : tViewConsts[constsIndex]; | ||||
|   ngDevMode && assertEqual( | ||||
|                    lView[BINDING_INDEX], tView.bindingStartIndex, | ||||
|                    getBindingIndex(), tView.bindingStartIndex, | ||||
|                    'elements should be created before any bindings'); | ||||
| 
 | ||||
|   ngDevMode && ngDevMode.rendererCreateElement++; | ||||
|  | ||||
| @ -11,10 +11,10 @@ import {attachPatchData} from '../context_discovery'; | ||||
| import {registerPostOrderHooks} from '../hooks'; | ||||
| import {TAttributes, TNodeType} from '../interfaces/node'; | ||||
| import {isContentQueryHost, isDirectiveHost} from '../interfaces/type_checks'; | ||||
| import {BINDING_INDEX, HEADER_OFFSET, RENDERER, TVIEW, T_HOST} from '../interfaces/view'; | ||||
| import {HEADER_OFFSET, RENDERER, TVIEW, T_HOST} from '../interfaces/view'; | ||||
| import {assertNodeType} from '../node_assert'; | ||||
| import {appendChild} from '../node_manipulation'; | ||||
| import {getIsParent, getLView, getPreviousOrParentTNode, setIsNotParent, setPreviousOrParentTNode} from '../state'; | ||||
| import {getBindingIndex, getIsParent, getLView, getPreviousOrParentTNode, setIsNotParent, setPreviousOrParentTNode} from '../state'; | ||||
| 
 | ||||
| import {createDirectivesInstances, executeContentQueries, getOrCreateTNode, resolveDirectives, saveResolvedLocalsInData} from './shared'; | ||||
| import {registerInitialStylingOnTNode} from './styling'; | ||||
| @ -44,7 +44,7 @@ export function ɵɵelementContainerStart( | ||||
|   const tViewConsts = tView.consts; | ||||
|   const consts = tViewConsts === null || constsIndex == null ? null : tViewConsts[constsIndex]; | ||||
|   ngDevMode && assertEqual( | ||||
|                    lView[BINDING_INDEX], tView.bindingStartIndex, | ||||
|                    getBindingIndex(), tView.bindingStartIndex, | ||||
|                    'element containers should be created before any bindings'); | ||||
| 
 | ||||
|   ngDevMode && ngDevMode.rendererCreateComment++; | ||||
|  | ||||
| @ -7,8 +7,8 @@ | ||||
|  */ | ||||
| import {bindingUpdated} from '../bindings'; | ||||
| import {SanitizerFn} from '../interfaces/sanitization'; | ||||
| import {BINDING_INDEX, TVIEW} from '../interfaces/view'; | ||||
| import {getLView, getSelectedIndex} from '../state'; | ||||
| import {TVIEW} from '../interfaces/view'; | ||||
| import {getLView, getSelectedIndex, nextBindingIndex} from '../state'; | ||||
| import {NO_CHANGE} from '../tokens'; | ||||
| 
 | ||||
| import {TsickleIssue1009, elementPropertyInternal, loadComponentRenderer, storePropertyBindingMetadata} from './shared'; | ||||
| @ -30,7 +30,7 @@ import {TsickleIssue1009, elementPropertyInternal, loadComponentRenderer, storeP | ||||
| export function ɵɵhostProperty<T>( | ||||
|     propName: string, value: T, sanitizer?: SanitizerFn | null): TsickleIssue1009 { | ||||
|   const lView = getLView(); | ||||
|   const bindingIndex = lView[BINDING_INDEX]++; | ||||
|   const bindingIndex = nextBindingIndex(); | ||||
|   if (bindingUpdated(lView, bindingIndex, value)) { | ||||
|     const nodeIndex = getSelectedIndex(); | ||||
|     elementPropertyInternal(lView, nodeIndex, propName, value, sanitizer, true); | ||||
| @ -64,7 +64,7 @@ export function ɵɵhostProperty<T>( | ||||
| export function ɵɵupdateSyntheticHostBinding<T>( | ||||
|     propName: string, value: T | NO_CHANGE, sanitizer?: SanitizerFn | null): TsickleIssue1009 { | ||||
|   const lView = getLView(); | ||||
|   const bindingIndex = lView[BINDING_INDEX]++; | ||||
|   const bindingIndex = nextBindingIndex(); | ||||
|   if (bindingUpdated(lView, bindingIndex, value)) { | ||||
|     const nodeIndex = getSelectedIndex(); | ||||
|     elementPropertyInternal( | ||||
|  | ||||
| @ -8,7 +8,8 @@ | ||||
| 
 | ||||
| import {assertEqual, assertLessThan} from '../../util/assert'; | ||||
| import {bindingUpdated, bindingUpdated2, bindingUpdated3, bindingUpdated4} from '../bindings'; | ||||
| import {BINDING_INDEX, LView} from '../interfaces/view'; | ||||
| import {LView} from '../interfaces/view'; | ||||
| import {getBindingIndex, incrementBindingIndex, nextBindingIndex, setBindingIndex} from '../state'; | ||||
| import {NO_CHANGE} from '../tokens'; | ||||
| import {renderStringify} from '../util/misc_utils'; | ||||
| 
 | ||||
| @ -30,13 +31,13 @@ export function interpolationV(lView: LView, values: any[]): string|NO_CHANGE { | ||||
|   ngDevMode && assertLessThan(2, values.length, 'should have at least 3 values'); | ||||
|   ngDevMode && assertEqual(values.length % 2, 1, 'should have an odd number of values'); | ||||
|   let isBindingUpdated = false; | ||||
|   let bindingIndex = lView[BINDING_INDEX]; | ||||
|   let bindingIndex = getBindingIndex(); | ||||
| 
 | ||||
|   for (let i = 1; i < values.length; i += 2) { | ||||
|     // Check if bindings (odd indexes) have changed
 | ||||
|     isBindingUpdated = bindingUpdated(lView, bindingIndex++, values[i]) || isBindingUpdated; | ||||
|   } | ||||
|   lView[BINDING_INDEX] = bindingIndex; | ||||
|   setBindingIndex(bindingIndex); | ||||
| 
 | ||||
|   if (!isBindingUpdated) { | ||||
|     return NO_CHANGE; | ||||
| @ -60,7 +61,7 @@ export function interpolationV(lView: LView, values: any[]): string|NO_CHANGE { | ||||
|  */ | ||||
| export function interpolation1(lView: LView, prefix: string, v0: any, suffix: string): string| | ||||
|     NO_CHANGE { | ||||
|   const different = bindingUpdated(lView, lView[BINDING_INDEX]++, v0); | ||||
|   const different = bindingUpdated(lView, nextBindingIndex(), v0); | ||||
|   return different ? prefix + renderStringify(v0) + suffix : NO_CHANGE; | ||||
| } | ||||
| 
 | ||||
| @ -69,9 +70,9 @@ export function interpolation1(lView: LView, prefix: string, v0: any, suffix: st | ||||
|  */ | ||||
| export function interpolation2( | ||||
|     lView: LView, prefix: string, v0: any, i0: string, v1: any, suffix: string): string|NO_CHANGE { | ||||
|   const bindingIndex = lView[BINDING_INDEX]; | ||||
|   const bindingIndex = getBindingIndex(); | ||||
|   const different = bindingUpdated2(lView, bindingIndex, v0, v1); | ||||
|   lView[BINDING_INDEX] += 2; | ||||
|   incrementBindingIndex(2); | ||||
| 
 | ||||
|   return different ? prefix + renderStringify(v0) + i0 + renderStringify(v1) + suffix : NO_CHANGE; | ||||
| } | ||||
| @ -82,9 +83,9 @@ export function interpolation2( | ||||
| export function interpolation3( | ||||
|     lView: LView, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, | ||||
|     suffix: string): string|NO_CHANGE { | ||||
|   const bindingIndex = lView[BINDING_INDEX]; | ||||
|   const bindingIndex = getBindingIndex(); | ||||
|   const different = bindingUpdated3(lView, bindingIndex, v0, v1, v2); | ||||
|   lView[BINDING_INDEX] += 3; | ||||
|   incrementBindingIndex(3); | ||||
| 
 | ||||
|   return different ? | ||||
|       prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + renderStringify(v2) + suffix : | ||||
| @ -97,9 +98,9 @@ export function interpolation3( | ||||
| export function interpolation4( | ||||
|     lView: LView, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, | ||||
|     v3: any, suffix: string): string|NO_CHANGE { | ||||
|   const bindingIndex = lView[BINDING_INDEX]; | ||||
|   const bindingIndex = getBindingIndex(); | ||||
|   const different = bindingUpdated4(lView, bindingIndex, v0, v1, v2, v3); | ||||
|   lView[BINDING_INDEX] += 4; | ||||
|   incrementBindingIndex(4); | ||||
| 
 | ||||
|   return different ? | ||||
|       prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + renderStringify(v2) + i2 + | ||||
| @ -113,10 +114,10 @@ export function interpolation4( | ||||
| export function interpolation5( | ||||
|     lView: LView, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, | ||||
|     v3: any, i3: string, v4: any, suffix: string): string|NO_CHANGE { | ||||
|   const bindingIndex = lView[BINDING_INDEX]; | ||||
|   const bindingIndex = getBindingIndex(); | ||||
|   let different = bindingUpdated4(lView, bindingIndex, v0, v1, v2, v3); | ||||
|   different = bindingUpdated(lView, bindingIndex + 4, v4) || different; | ||||
|   lView[BINDING_INDEX] += 5; | ||||
|   incrementBindingIndex(5); | ||||
| 
 | ||||
|   return different ? | ||||
|       prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + renderStringify(v2) + i2 + | ||||
| @ -130,10 +131,10 @@ export function interpolation5( | ||||
| export function interpolation6( | ||||
|     lView: LView, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, | ||||
|     v3: any, i3: string, v4: any, i4: string, v5: any, suffix: string): string|NO_CHANGE { | ||||
|   const bindingIndex = lView[BINDING_INDEX]; | ||||
|   const bindingIndex = getBindingIndex(); | ||||
|   let different = bindingUpdated4(lView, bindingIndex, v0, v1, v2, v3); | ||||
|   different = bindingUpdated2(lView, bindingIndex + 4, v4, v5) || different; | ||||
|   lView[BINDING_INDEX] += 6; | ||||
|   incrementBindingIndex(6); | ||||
| 
 | ||||
|   return different ? | ||||
|       prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + renderStringify(v2) + i2 + | ||||
| @ -148,10 +149,10 @@ export function interpolation7( | ||||
|     lView: LView, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, | ||||
|     v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, suffix: string): string| | ||||
|     NO_CHANGE { | ||||
|   const bindingIndex = lView[BINDING_INDEX]; | ||||
|   const bindingIndex = getBindingIndex(); | ||||
|   let different = bindingUpdated4(lView, bindingIndex, v0, v1, v2, v3); | ||||
|   different = bindingUpdated3(lView, bindingIndex + 4, v4, v5, v6) || different; | ||||
|   lView[BINDING_INDEX] += 7; | ||||
|   incrementBindingIndex(7); | ||||
| 
 | ||||
|   return different ? | ||||
|       prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + renderStringify(v2) + i2 + | ||||
| @ -167,10 +168,10 @@ export function interpolation8( | ||||
|     lView: LView, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, | ||||
|     v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, i6: string, v7: any, | ||||
|     suffix: string): string|NO_CHANGE { | ||||
|   const bindingIndex = lView[BINDING_INDEX]; | ||||
|   const bindingIndex = getBindingIndex(); | ||||
|   let different = bindingUpdated4(lView, bindingIndex, v0, v1, v2, v3); | ||||
|   different = bindingUpdated4(lView, bindingIndex + 4, v4, v5, v6, v7) || different; | ||||
|   lView[BINDING_INDEX] += 8; | ||||
|   incrementBindingIndex(8); | ||||
| 
 | ||||
|   return different ? | ||||
|       prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + renderStringify(v2) + i2 + | ||||
|  | ||||
| @ -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 {BINDING_INDEX, 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, T_HOST} from '../interfaces/view'; | ||||
| import {DebugNodeStyling, NodeStylingDebug} from '../styling/styling_debug'; | ||||
| import {attachDebugObject} from '../util/debug_utils'; | ||||
| import {isStylingContext} from '../util/styling_utils'; | ||||
| @ -325,7 +325,6 @@ export class LViewDebug { | ||||
|   get declarationView() { return toDebug(this._raw_lView[DECLARATION_VIEW]); } | ||||
|   get queries() { return this._raw_lView[QUERIES]; } | ||||
|   get tHost() { return this._raw_lView[T_HOST]; } | ||||
|   get bindingIndex() { return this._raw_lView[BINDING_INDEX]; } | ||||
| 
 | ||||
|   /** | ||||
|    * Normalized view of child views (and containers) attached at this location. | ||||
|  | ||||
| @ -7,8 +7,8 @@ | ||||
|  */ | ||||
| import {bindingUpdated} from '../bindings'; | ||||
| import {SanitizerFn} from '../interfaces/sanitization'; | ||||
| import {BINDING_INDEX, TVIEW} from '../interfaces/view'; | ||||
| import {getLView, getSelectedIndex} from '../state'; | ||||
| import {TVIEW} from '../interfaces/view'; | ||||
| import {getLView, getSelectedIndex, nextBindingIndex} from '../state'; | ||||
| 
 | ||||
| import {TsickleIssue1009, elementPropertyInternal, storePropertyBindingMetadata} from './shared'; | ||||
| 
 | ||||
| @ -34,7 +34,7 @@ import {TsickleIssue1009, elementPropertyInternal, storePropertyBindingMetadata} | ||||
| export function ɵɵproperty<T>( | ||||
|     propName: string, value: T, sanitizer?: SanitizerFn | null): TsickleIssue1009 { | ||||
|   const lView = getLView(); | ||||
|   const bindingIndex = lView[BINDING_INDEX]++; | ||||
|   const bindingIndex = nextBindingIndex(); | ||||
|   if (bindingUpdated(lView, bindingIndex, value)) { | ||||
|     const nodeIndex = getSelectedIndex(); | ||||
|     elementPropertyInternal(lView, nodeIndex, propName, value, sanitizer); | ||||
|  | ||||
| @ -6,8 +6,8 @@ | ||||
|  * found in the LICENSE file at https://angular.io/license
 | ||||
|  */ | ||||
| import {SanitizerFn} from '../interfaces/sanitization'; | ||||
| import {BINDING_INDEX, TVIEW} from '../interfaces/view'; | ||||
| import {getLView, getSelectedIndex} from '../state'; | ||||
| import {TVIEW} from '../interfaces/view'; | ||||
| import {getBindingIndex, getLView, getSelectedIndex} from '../state'; | ||||
| import {NO_CHANGE} from '../tokens'; | ||||
| 
 | ||||
| import {interpolation1, interpolation2, interpolation3, interpolation4, interpolation5, interpolation6, interpolation7, interpolation8, interpolationV} from './interpolation'; | ||||
| @ -86,9 +86,9 @@ export function ɵɵpropertyInterpolate1( | ||||
|   const interpolatedValue = interpolation1(lView, prefix, v0, suffix); | ||||
|   if (interpolatedValue !== NO_CHANGE) { | ||||
|     elementPropertyInternal(lView, getSelectedIndex(), propName, interpolatedValue, sanitizer); | ||||
|     ngDevMode && storePropertyBindingMetadata( | ||||
|                      lView[TVIEW].data, getSelectedIndex(), propName, lView[BINDING_INDEX] - 1, | ||||
|                      prefix, suffix); | ||||
|     ngDevMode && | ||||
|         storePropertyBindingMetadata( | ||||
|             lView[TVIEW].data, getSelectedIndex(), propName, getBindingIndex() - 1, prefix, suffix); | ||||
|   } | ||||
|   return ɵɵpropertyInterpolate1; | ||||
| } | ||||
| @ -133,7 +133,7 @@ export function ɵɵpropertyInterpolate2( | ||||
|     elementPropertyInternal(lView, nodeIndex, propName, interpolatedValue, sanitizer); | ||||
|     ngDevMode && | ||||
|         storePropertyBindingMetadata( | ||||
|             lView[TVIEW].data, nodeIndex, propName, lView[BINDING_INDEX] - 2, prefix, i0, suffix); | ||||
|             lView[TVIEW].data, nodeIndex, propName, getBindingIndex() - 2, prefix, i0, suffix); | ||||
|   } | ||||
|   return ɵɵpropertyInterpolate2; | ||||
| } | ||||
| @ -179,9 +179,9 @@ export function ɵɵpropertyInterpolate3( | ||||
|   if (interpolatedValue !== NO_CHANGE) { | ||||
|     const nodeIndex = getSelectedIndex(); | ||||
|     elementPropertyInternal(lView, nodeIndex, propName, interpolatedValue, sanitizer); | ||||
|     ngDevMode && storePropertyBindingMetadata( | ||||
|                      lView[TVIEW].data, nodeIndex, propName, lView[BINDING_INDEX] - 3, prefix, i0, | ||||
|                      i1, suffix); | ||||
|     ngDevMode && | ||||
|         storePropertyBindingMetadata( | ||||
|             lView[TVIEW].data, nodeIndex, propName, getBindingIndex() - 3, prefix, i0, i1, suffix); | ||||
|   } | ||||
|   return ɵɵpropertyInterpolate3; | ||||
| } | ||||
| @ -230,8 +230,8 @@ export function ɵɵpropertyInterpolate4( | ||||
|     const nodeIndex = getSelectedIndex(); | ||||
|     elementPropertyInternal(lView, nodeIndex, propName, interpolatedValue, sanitizer); | ||||
|     ngDevMode && storePropertyBindingMetadata( | ||||
|                      lView[TVIEW].data, nodeIndex, propName, lView[BINDING_INDEX] - 4, prefix, i0, | ||||
|                      i1, i2, suffix); | ||||
|                      lView[TVIEW].data, nodeIndex, propName, getBindingIndex() - 4, prefix, i0, i1, | ||||
|                      i2, suffix); | ||||
|   } | ||||
|   return ɵɵpropertyInterpolate4; | ||||
| } | ||||
| @ -283,8 +283,8 @@ export function ɵɵpropertyInterpolate5( | ||||
|     const nodeIndex = getSelectedIndex(); | ||||
|     elementPropertyInternal(lView, nodeIndex, propName, interpolatedValue, sanitizer); | ||||
|     ngDevMode && storePropertyBindingMetadata( | ||||
|                      lView[TVIEW].data, nodeIndex, propName, lView[BINDING_INDEX] - 5, prefix, i0, | ||||
|                      i1, i2, i3, suffix); | ||||
|                      lView[TVIEW].data, nodeIndex, propName, getBindingIndex() - 5, prefix, i0, i1, | ||||
|                      i2, i3, suffix); | ||||
|   } | ||||
|   return ɵɵpropertyInterpolate5; | ||||
| } | ||||
| @ -339,8 +339,8 @@ export function ɵɵpropertyInterpolate6( | ||||
|     const nodeIndex = getSelectedIndex(); | ||||
|     elementPropertyInternal(lView, nodeIndex, propName, interpolatedValue, sanitizer); | ||||
|     ngDevMode && storePropertyBindingMetadata( | ||||
|                      lView[TVIEW].data, nodeIndex, propName, lView[BINDING_INDEX] - 6, prefix, i0, | ||||
|                      i1, i2, i3, i4, suffix); | ||||
|                      lView[TVIEW].data, nodeIndex, propName, getBindingIndex() - 6, prefix, i0, i1, | ||||
|                      i2, i3, i4, suffix); | ||||
|   } | ||||
|   return ɵɵpropertyInterpolate6; | ||||
| } | ||||
| @ -397,8 +397,8 @@ export function ɵɵpropertyInterpolate7( | ||||
|     const nodeIndex = getSelectedIndex(); | ||||
|     elementPropertyInternal(lView, nodeIndex, propName, interpolatedValue, sanitizer); | ||||
|     ngDevMode && storePropertyBindingMetadata( | ||||
|                      lView[TVIEW].data, nodeIndex, propName, lView[BINDING_INDEX] - 7, prefix, i0, | ||||
|                      i1, i2, i3, i4, i5, suffix); | ||||
|                      lView[TVIEW].data, nodeIndex, propName, getBindingIndex() - 7, prefix, i0, i1, | ||||
|                      i2, i3, i4, i5, suffix); | ||||
|   } | ||||
|   return ɵɵpropertyInterpolate7; | ||||
| } | ||||
| @ -457,8 +457,8 @@ export function ɵɵpropertyInterpolate8( | ||||
|     const nodeIndex = getSelectedIndex(); | ||||
|     elementPropertyInternal(lView, nodeIndex, propName, interpolatedValue, sanitizer); | ||||
|     ngDevMode && storePropertyBindingMetadata( | ||||
|                      lView[TVIEW].data, nodeIndex, propName, lView[BINDING_INDEX] - 8, prefix, i0, | ||||
|                      i1, i2, i3, i4, i5, i6, suffix); | ||||
|                      lView[TVIEW].data, nodeIndex, propName, getBindingIndex() - 8, prefix, i0, i1, | ||||
|                      i2, i3, i4, i5, i6, suffix); | ||||
|   } | ||||
|   return ɵɵpropertyInterpolate8; | ||||
| } | ||||
| @ -507,7 +507,7 @@ export function ɵɵpropertyInterpolateV( | ||||
|       } | ||||
|       storePropertyBindingMetadata( | ||||
|           lView[TVIEW].data, nodeIndex, propName, | ||||
|           lView[BINDING_INDEX] - interpolationInBetween.length + 1, ...interpolationInBetween); | ||||
|           getBindingIndex() - interpolationInBetween.length + 1, ...interpolationInBetween); | ||||
|     } | ||||
|   } | ||||
|   return ɵɵpropertyInterpolateV; | ||||
|  | ||||
| @ -10,7 +10,7 @@ import {ErrorHandler} from '../../error_handler'; | ||||
| import {CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA, SchemaMetadata} from '../../metadata/schema'; | ||||
| import {validateAgainstEventAttributes, validateAgainstEventProperties} from '../../sanitization/sanitization'; | ||||
| import {Sanitizer} from '../../sanitization/sanitizer'; | ||||
| import {assertDataInRange, assertDefined, assertDomNode, assertEqual, assertGreaterThan, assertNotEqual, assertNotSame} from '../../util/assert'; | ||||
| import {assertDataInRange, assertDefined, assertDomNode, assertEqual, assertGreaterThan, assertLessThanOrEqual, assertNotEqual, assertNotSame} from '../../util/assert'; | ||||
| import {createNamedArrayType} from '../../util/named_array_type'; | ||||
| import {initNgDevMode} from '../../util/ng_dev_mode'; | ||||
| import {normalizeDebugBindingName, normalizeDebugBindingValue} from '../../util/ng_reflect'; | ||||
| @ -27,10 +27,10 @@ import {AttributeMarker, InitialInputData, InitialInputs, LocalRefExtractor, Pro | ||||
| import {RComment, RElement, RText, Renderer3, RendererFactory3, isProceduralRenderer} from '../interfaces/renderer'; | ||||
| import {SanitizerFn} from '../interfaces/sanitization'; | ||||
| import {isComponentDef, isComponentHost, isContentQueryHost, isLContainer, isRootView} from '../interfaces/type_checks'; | ||||
| 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 {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 {isNodeMatchingSelectorList} from '../node_selector_matcher'; | ||||
| import {ActiveElementFlags, enterView, executeElementExitFn, getBindingsEnabled, getCheckNoChangesMode, getIsParent, getPreviousOrParentTNode, getSelectedIndex, hasActiveElementFlag, incrementActiveDirectiveId, leaveView, leaveViewProcessExit, setActiveHostElement, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setCurrentQueryIndex, setPreviousOrParentTNode, setSelectedIndex} from '../state'; | ||||
| import {ActiveElementFlags, enterView, executeElementExitFn, getBindingIndex, getBindingsEnabled, getCheckNoChangesMode, getIsParent, getPreviousOrParentTNode, getSelectedIndex, hasActiveElementFlag, incrementActiveDirectiveId, leaveView, leaveViewProcessExit, setActiveHostElement, setBindingIndex, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setCurrentQueryIndex, setPreviousOrParentTNode, setSelectedIndex} from '../state'; | ||||
| import {renderStylingMap, writeStylingValueDirectly} from '../styling/bindings'; | ||||
| import {NO_CHANGE} from '../tokens'; | ||||
| import {isAnimationProp} from '../util/attrs_utils'; | ||||
| @ -51,11 +51,11 @@ import {LCleanup, LViewBlueprint, MatchesArray, TCleanup, TNodeConstructor, TNod | ||||
| const _CLEAN_PROMISE = (() => Promise.resolve(null))(); | ||||
| 
 | ||||
| /** Sets the host bindings for the current view. */ | ||||
| export function setHostBindings(tView: TView, viewData: LView): void { | ||||
| export function setHostBindings(tView: TView, lView: LView): void { | ||||
|   const selectedIndex = getSelectedIndex(); | ||||
|   try { | ||||
|     if (tView.expandoInstructions !== null) { | ||||
|       let bindingRootIndex = viewData[BINDING_INDEX] = tView.expandoStartIndex; | ||||
|       let bindingRootIndex = setBindingIndex(tView.expandoStartIndex); | ||||
|       setBindingRoot(bindingRootIndex); | ||||
|       let currentDirectiveIndex = -1; | ||||
|       let currentElementIndex = -1; | ||||
| @ -92,8 +92,8 @@ export function setHostBindings(tView: TView, viewData: LView): void { | ||||
|             // are run because this way the first directive ID value is not zero.
 | ||||
|             incrementActiveDirectiveId(); | ||||
| 
 | ||||
|             viewData[BINDING_INDEX] = bindingRootIndex; | ||||
|             const hostCtx = unwrapRNode(viewData[currentDirectiveIndex]); | ||||
|             setBindingIndex(bindingRootIndex); | ||||
|             const hostCtx = unwrapRNode(lView[currentDirectiveIndex]); | ||||
|             instruction(RenderFlags.Update, hostCtx, currentElementIndex); | ||||
|           } | ||||
|           currentDirectiveIndex++; | ||||
| @ -378,7 +378,7 @@ export function refreshView<T>( | ||||
|   try { | ||||
|     resetPreOrderHookFlags(lView); | ||||
| 
 | ||||
|     setBindingRoot(lView[BINDING_INDEX] = tView.bindingStartIndex); | ||||
|     setBindingIndex(tView.bindingStartIndex); | ||||
|     if (templateFn !== null) { | ||||
|       executeTemplate(lView, templateFn, RenderFlags.Update, context); | ||||
|     } | ||||
| @ -666,7 +666,6 @@ function createViewBlueprint(bindingStartIndex: number, initialViewLength: numbe | ||||
|   for (let i = 0; i < initialViewLength; i++) { | ||||
|     blueprint.push(i < bindingStartIndex ? null : NO_CHANGE); | ||||
|   } | ||||
|   blueprint[BINDING_INDEX] = bindingStartIndex; | ||||
| 
 | ||||
|   return blueprint as LView; | ||||
| } | ||||
| @ -1191,8 +1190,8 @@ function postProcessDirective<T>( | ||||
|  * A lighter version of postProcessDirective() that is used for the root component. | ||||
|  */ | ||||
| function postProcessBaseDirective<T>(lView: LView, hostTNode: TNode, directive: T): void { | ||||
|   ngDevMode && assertEqual( | ||||
|                    lView[BINDING_INDEX], lView[TVIEW].bindingStartIndex, | ||||
|   ngDevMode && assertLessThanOrEqual( | ||||
|                    getBindingIndex(), lView[TVIEW].bindingStartIndex, | ||||
|                    'directives should be created before any bindings'); | ||||
|   attachPatchData(directive, lView); | ||||
|   const native = getNativeByTNode(hostTNode, lView); | ||||
|  | ||||
| @ -13,8 +13,8 @@ import {AttributeMarker, TAttributes, TNode, TNodeFlags, TNodeType} from '../int | ||||
| import {RElement} from '../interfaces/renderer'; | ||||
| import {StylingMapArray, StylingMapArrayIndex, TStylingConfig, TStylingContext} from '../interfaces/styling'; | ||||
| import {isDirectiveHost} from '../interfaces/type_checks'; | ||||
| import {BINDING_INDEX, LView, RENDERER} from '../interfaces/view'; | ||||
| import {getActiveDirectiveId, getCheckNoChangesMode, getCurrentStyleSanitizer, getLView, getSelectedIndex, resetCurrentStyleSanitizer, setCurrentStyleSanitizer, setElementExitFn} from '../state'; | ||||
| import {LView, RENDERER} from '../interfaces/view'; | ||||
| import {getActiveDirectiveId, getCheckNoChangesMode, getCurrentStyleSanitizer, getLView, getSelectedIndex, incrementBindingIndex, nextBindingIndex, resetCurrentStyleSanitizer, setCurrentStyleSanitizer, setElementExitFn} from '../state'; | ||||
| import {applyStylingMapDirectly, applyStylingValueDirectly, flushStyling, setClass, setStyle, updateClassViaContext, updateStyleViaContext} from '../styling/bindings'; | ||||
| import {activateStylingMapFeature} from '../styling/map_based_bindings'; | ||||
| import {attachStylingDebugObject} from '../styling/styling_debug'; | ||||
| @ -90,13 +90,11 @@ export function ɵɵstyleProp( | ||||
| export function stylePropInternal( | ||||
|     elementIndex: number, prop: string, value: string | number | SafeValue | null, | ||||
|     suffix?: string | null | undefined): void { | ||||
|   const lView = getLView(); | ||||
| 
 | ||||
|   // if a value is interpolated then it may render a `NO_CHANGE` value.
 | ||||
|   // in this case we do not need to do anything, but the binding index
 | ||||
|   // still needs to be incremented because all styling binding values
 | ||||
|   // are stored inside of the lView.
 | ||||
|   const bindingIndex = getAndIncrementBindingIndex(lView, false); | ||||
|   const bindingIndex = nextBindingIndex(); | ||||
| 
 | ||||
|   const updated = | ||||
|       stylingProp(elementIndex, bindingIndex, prop, resolveStylePropValue(value, suffix), false); | ||||
| @ -124,13 +122,11 @@ export function stylePropInternal( | ||||
|  * @codeGenApi | ||||
|  */ | ||||
| export function ɵɵclassProp(className: string, value: boolean | null): void { | ||||
|   const lView = getLView(); | ||||
| 
 | ||||
|   // if a value is interpolated then it may render a `NO_CHANGE` value.
 | ||||
|   // in this case we do not need to do anything, but the binding index
 | ||||
|   // still needs to be incremented because all styling binding values
 | ||||
|   // are stored inside of the lView.
 | ||||
|   const bindingIndex = getAndIncrementBindingIndex(lView, false); | ||||
|   const bindingIndex = nextBindingIndex(); | ||||
| 
 | ||||
|   const updated = stylingProp(getSelectedIndex(), bindingIndex, className, value, true); | ||||
|   if (ngDevMode) { | ||||
| @ -248,7 +244,7 @@ export function ɵɵstyleMap(styles: {[styleName: string]: any} | NO_CHANGE | nu | ||||
|   // in this case we do not need to do anything, but the binding index
 | ||||
|   // still needs to be incremented because all styling binding values
 | ||||
|   // are stored inside of the lView.
 | ||||
|   const bindingIndex = getAndIncrementBindingIndex(lView, true); | ||||
|   const bindingIndex = incrementBindingIndex(2); | ||||
| 
 | ||||
|   // inputs are only evaluated from a template binding into a directive, therefore,
 | ||||
|   // there should not be a situation where a directive host bindings function
 | ||||
| @ -300,7 +296,7 @@ export function classMapInternal( | ||||
|   // in this case we do not need to do anything, but the binding index
 | ||||
|   // still needs to be incremented because all styling binding values
 | ||||
|   // are stored inside of the lView.
 | ||||
|   const bindingIndex = getAndIncrementBindingIndex(lView, true); | ||||
|   const bindingIndex = incrementBindingIndex(2); | ||||
| 
 | ||||
|   // inputs are only evaluated from a template binding into a directive, therefore,
 | ||||
|   // there should not be a situation where a directive host bindings function
 | ||||
| @ -582,11 +578,3 @@ function resolveStylePropValue( | ||||
| function isHostStyling(): boolean { | ||||
|   return isHostStylingActive(getActiveDirectiveId()); | ||||
| } | ||||
| 
 | ||||
| function getAndIncrementBindingIndex(lView: LView, isMapBased: boolean): number { | ||||
|   // map-based bindings use two slots because the previously constructed
 | ||||
|   // className / style value must be compared against.
 | ||||
|   const index = lView[BINDING_INDEX]; | ||||
|   lView[BINDING_INDEX] += isMapBased ? 2 : 1; | ||||
|   return index; | ||||
| } | ||||
|  | ||||
| @ -7,9 +7,10 @@ | ||||
|  */ | ||||
| import {assertDataInRange, assertEqual} from '../../util/assert'; | ||||
| import {TNodeType} from '../interfaces/node'; | ||||
| import {BINDING_INDEX, HEADER_OFFSET, RENDERER, TVIEW, T_HOST} from '../interfaces/view'; | ||||
| import {HEADER_OFFSET, RENDERER, TVIEW, T_HOST} from '../interfaces/view'; | ||||
| import {appendChild, createTextNode} from '../node_manipulation'; | ||||
| import {getLView, setIsNotParent} from '../state'; | ||||
| import {getBindingIndex, getLView, setIsNotParent} from '../state'; | ||||
| 
 | ||||
| import {getOrCreateTNode} from './shared'; | ||||
| 
 | ||||
| 
 | ||||
| @ -25,7 +26,7 @@ import {getOrCreateTNode} from './shared'; | ||||
| export function ɵɵtext(index: number, value: string = ''): void { | ||||
|   const lView = getLView(); | ||||
|   ngDevMode && assertEqual( | ||||
|                    lView[BINDING_INDEX], lView[TVIEW].bindingStartIndex, | ||||
|                    getBindingIndex(), lView[TVIEW].bindingStartIndex, | ||||
|                    'text nodes should be created before any bindings'); | ||||
|   ngDevMode && assertDataInRange(lView, index + HEADER_OFFSET); | ||||
|   const textNative = lView[index + HEADER_OFFSET] = createTextNode(value, lView[RENDERER]); | ||||
|  | ||||
| @ -32,20 +32,19 @@ export const PARENT = 3; | ||||
| export const NEXT = 4; | ||||
| export const QUERIES = 5; | ||||
| export const T_HOST = 6; | ||||
| export const BINDING_INDEX = 7; | ||||
| export const CLEANUP = 8; | ||||
| export const CONTEXT = 9; | ||||
| export const INJECTOR = 10; | ||||
| export const RENDERER_FACTORY = 11; | ||||
| export const RENDERER = 12; | ||||
| export const SANITIZER = 13; | ||||
| export const CHILD_HEAD = 14; | ||||
| export const CHILD_TAIL = 15; | ||||
| export const DECLARATION_VIEW = 16; | ||||
| export const DECLARATION_LCONTAINER = 17; | ||||
| export const PREORDER_HOOK_FLAGS = 18; | ||||
| export const CLEANUP = 7; | ||||
| export const CONTEXT = 8; | ||||
| export const INJECTOR = 9; | ||||
| export const RENDERER_FACTORY = 10; | ||||
| export const RENDERER = 11; | ||||
| export const SANITIZER = 12; | ||||
| export const CHILD_HEAD = 13; | ||||
| export const CHILD_TAIL = 14; | ||||
| export const DECLARATION_VIEW = 15; | ||||
| export const DECLARATION_LCONTAINER = 16; | ||||
| export const PREORDER_HOOK_FLAGS = 17; | ||||
| /** Size of LView's header. Necessary to adjust for it when setting slots.  */ | ||||
| export const HEADER_OFFSET = 19; | ||||
| export const HEADER_OFFSET = 18; | ||||
| 
 | ||||
| 
 | ||||
| // This interface replaces the real LView interface if it is an arg or a
 | ||||
| @ -120,15 +119,6 @@ export interface LView extends Array<any> { | ||||
|    */ | ||||
|   [T_HOST]: TViewNode|TElementNode|null; | ||||
| 
 | ||||
|   /** | ||||
|    * The binding index we should access next. | ||||
|    * | ||||
|    * This is stored so that bindings can continue where they left off | ||||
|    * if a view is left midway through processing bindings (e.g. if there is | ||||
|    * a setter that creates an embedded view, like in ngIf). | ||||
|    */ | ||||
|   [BINDING_INDEX]: number; | ||||
| 
 | ||||
|   /** | ||||
|    * When a view is destroyed, listeners need to be released and outputs need to be | ||||
|    * unsubscribed. This context array stores both listener functions wrapped with | ||||
| @ -381,6 +371,8 @@ export interface TView { | ||||
|    * starts to store bindings only. Saving this value ensures that we | ||||
|    * will begin reading bindings at the correct point in the array when | ||||
|    * we are in update mode. | ||||
|    * | ||||
|    * -1 means that it has not been initialized. | ||||
|    */ | ||||
|   bindingStartIndex: number; | ||||
| 
 | ||||
|  | ||||
| @ -12,9 +12,9 @@ import {PipeTransform} from '../change_detection/pipe_transform'; | ||||
| import {getFactoryDef} from './definition'; | ||||
| import {store} from './instructions/all'; | ||||
| import {PipeDef, PipeDefList} from './interfaces/definition'; | ||||
| import {BINDING_INDEX, HEADER_OFFSET, LView, TVIEW} from './interfaces/view'; | ||||
| import {HEADER_OFFSET, LView, TVIEW} from './interfaces/view'; | ||||
| import {ɵɵpureFunction1, ɵɵpureFunction2, ɵɵpureFunction3, ɵɵpureFunction4, ɵɵpureFunctionV} from './pure_function'; | ||||
| import {getLView} from './state'; | ||||
| import {getBindingIndex, getLView} from './state'; | ||||
| import {NO_CHANGE} from './tokens'; | ||||
| import {load} from './util/view_utils'; | ||||
| 
 | ||||
| @ -200,7 +200,7 @@ function unwrapValue(lView: LView, newValue: any): any { | ||||
|     newValue = WrappedValue.unwrap(newValue); | ||||
|     // The NO_CHANGE value needs to be written at the index where the impacted binding value is
 | ||||
|     // stored
 | ||||
|     const bindingToInvalidateIdx = lView[BINDING_INDEX]; | ||||
|     const bindingToInvalidateIdx = getBindingIndex(); | ||||
|     lView[bindingToInvalidateIdx] = NO_CHANGE; | ||||
|   } | ||||
|   return newValue; | ||||
|  | ||||
| @ -12,7 +12,7 @@ import {assertDefined, assertEqual} from '../util/assert'; | ||||
| import {assertLViewOrUndefined} from './assert'; | ||||
| import {ComponentDef, DirectiveDef} from './interfaces/definition'; | ||||
| import {TNode} from './interfaces/node'; | ||||
| import {BINDING_INDEX, CONTEXT, DECLARATION_VIEW, LView, OpaqueViewState, TVIEW} from './interfaces/view'; | ||||
| import {CONTEXT, DECLARATION_VIEW, LView, OpaqueViewState, TVIEW} from './interfaces/view'; | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
| @ -62,6 +62,11 @@ interface LFrame { | ||||
|    */ | ||||
|   selectedIndex: number; | ||||
| 
 | ||||
|   /** | ||||
|    * Current pointer to the binding index. | ||||
|    */ | ||||
|   bindingIndex: number; | ||||
| 
 | ||||
|   /** | ||||
|    * The last viewData retrieved by nextContext(). | ||||
|    * Allows building nextContext() and reference() calls. | ||||
| @ -433,11 +438,38 @@ export function getBindingRoot() { | ||||
|   let index = lFrame.bindingRootIndex; | ||||
|   if (index === -1) { | ||||
|     const lView = lFrame.lView; | ||||
|     index = lFrame.bindingRootIndex = lView[BINDING_INDEX] = lView[TVIEW].bindingStartIndex; | ||||
|     index = lFrame.bindingRootIndex = lView[TVIEW].bindingStartIndex; | ||||
|   } | ||||
|   return index; | ||||
| } | ||||
| 
 | ||||
| export function getBindingIndex(): number { | ||||
|   return instructionState.lFrame.bindingIndex; | ||||
| } | ||||
| 
 | ||||
| export function setBindingIndex(value: number): number { | ||||
|   return instructionState.lFrame.bindingIndex = value; | ||||
| } | ||||
| 
 | ||||
| export function nextBindingIndex(): number { | ||||
|   return instructionState.lFrame.bindingIndex++; | ||||
| } | ||||
| 
 | ||||
| export function incrementBindingIndex(count: number): number { | ||||
|   const lFrame = instructionState.lFrame; | ||||
|   const index = lFrame.bindingIndex; | ||||
|   lFrame.bindingIndex = lFrame.bindingIndex + count; | ||||
|   return index; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Set a new binding root index so that host template functions can execute. | ||||
|  * | ||||
|  * Bindings inside the host template are 0 index. But because we don't know ahead of time | ||||
|  * how many host bindings we have we can't pre-compute them. For this reason they are all | ||||
|  * 0 index and we just shift the root so that they match next available location in the LView. | ||||
|  * @param value | ||||
|  */ | ||||
| export function setBindingRoot(value: number) { | ||||
|   instructionState.lFrame.bindingRootIndex = value; | ||||
| } | ||||
| @ -513,6 +545,7 @@ export function enterView(newView: LView, tNode: TNode | null): void { | ||||
|   newLFrame.currentDirectiveDef = null; | ||||
|   newLFrame.activeDirectiveId = 0; | ||||
|   newLFrame.bindingRootIndex = -1; | ||||
|   newLFrame.bindingIndex = newView === null ? -1 : newView[TVIEW].bindingStartIndex; | ||||
|   newLFrame.currentQueryIndex = 0; | ||||
| } | ||||
| 
 | ||||
| @ -539,6 +572,7 @@ function createLFrame(parent: LFrame | null): LFrame { | ||||
|     currentDirectiveDef: null,      //
 | ||||
|     activeDirectiveId: 0,           //
 | ||||
|     bindingRootIndex: -1,           //
 | ||||
|     bindingIndex: -1,               //
 | ||||
|     currentQueryIndex: 0,           //
 | ||||
|     parent: parent !,               //
 | ||||
|     child: null,                    //
 | ||||
|  | ||||
| @ -48,6 +48,12 @@ export function assertLessThan<T>(actual: T, expected: T, msg: string) { | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| export function assertLessThanOrEqual<T>(actual: T, expected: T, msg: string) { | ||||
|   if (actual > expected) { | ||||
|     throwError(msg); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| export function assertGreaterThan<T>(actual: T, expected: T, msg: string) { | ||||
|   if (actual <= expected) { | ||||
|     throwError(msg); | ||||
|  | ||||
| @ -127,6 +127,53 @@ describe('change detection', () => { | ||||
|       fixture.detectChanges(false); | ||||
|       expect(fixture.nativeElement).toHaveText('1|dynamic'); | ||||
|     }); | ||||
| 
 | ||||
|     it('should support re-enterant change detection', () => { | ||||
|       @Component({ | ||||
|         selector: 'has-host-binding', | ||||
|         template: '..', | ||||
|         host: { | ||||
|           '[class.x]': 'x', | ||||
|         } | ||||
|       }) | ||||
|       class HasHostBinding { | ||||
|         x = true; | ||||
|       } | ||||
| 
 | ||||
|       @Component({ | ||||
|         selector: 'child', | ||||
|         template: '<has-host-binding></has-host-binding>', | ||||
|         inputs: ['input'], | ||||
|       }) | ||||
|       class Child { | ||||
|         /** | ||||
|          * @internal | ||||
|          */ | ||||
|         private _input !: number; | ||||
| 
 | ||||
|         constructor(private cdr: ChangeDetectorRef) {} | ||||
| 
 | ||||
|         get input() { return this._input; } | ||||
| 
 | ||||
|         set input(value: number) { | ||||
|           this._input = value; | ||||
|           this.cdr.detectChanges(); | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|       @Component({ | ||||
|         selector: 'root', | ||||
|         template: '<child [input]="3"></child>', | ||||
|       }) | ||||
|       class Root { | ||||
|       } | ||||
| 
 | ||||
|       TestBed.configureTestingModule({ | ||||
|         declarations: [Root, Child, HasHostBinding], | ||||
|       }); | ||||
| 
 | ||||
|       TestBed.createComponent(Root).detectChanges(); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   describe('OnPush', () => { | ||||
|  | ||||
| @ -2,9 +2,6 @@ | ||||
|   { | ||||
|     "name": "ACTIVE_INDEX" | ||||
|   }, | ||||
|   { | ||||
|     "name": "BINDING_INDEX" | ||||
|   }, | ||||
|   { | ||||
|     "name": "BLOOM_MASK" | ||||
|   }, | ||||
| @ -608,6 +605,9 @@ | ||||
|   { | ||||
|     "name": "setActiveHostElement" | ||||
|   }, | ||||
|   { | ||||
|     "name": "setBindingIndex" | ||||
|   }, | ||||
|   { | ||||
|     "name": "setBindingRoot" | ||||
|   }, | ||||
|  | ||||
| @ -2,9 +2,6 @@ | ||||
|   { | ||||
|     "name": "ACTIVE_INDEX" | ||||
|   }, | ||||
|   { | ||||
|     "name": "BINDING_INDEX" | ||||
|   }, | ||||
|   { | ||||
|     "name": "BLOOM_MASK" | ||||
|   }, | ||||
| @ -437,6 +434,9 @@ | ||||
|   { | ||||
|     "name": "setActiveHostElement" | ||||
|   }, | ||||
|   { | ||||
|     "name": "setBindingIndex" | ||||
|   }, | ||||
|   { | ||||
|     "name": "setBindingRoot" | ||||
|   }, | ||||
|  | ||||
| @ -2,9 +2,6 @@ | ||||
|   { | ||||
|     "name": "ACTIVE_INDEX" | ||||
|   }, | ||||
|   { | ||||
|     "name": "BINDING_INDEX" | ||||
|   }, | ||||
|   { | ||||
|     "name": "BIT_MASK_START_VALUE" | ||||
|   }, | ||||
| @ -611,9 +608,6 @@ | ||||
|   { | ||||
|     "name": "getActiveDirectiveId" | ||||
|   }, | ||||
|   { | ||||
|     "name": "getAndIncrementBindingIndex" | ||||
|   }, | ||||
|   { | ||||
|     "name": "getBeforeNodeForView" | ||||
|   }, | ||||
| @ -1073,6 +1067,9 @@ | ||||
|   { | ||||
|     "name": "nativeRemoveNode" | ||||
|   }, | ||||
|   { | ||||
|     "name": "nextBindingIndex" | ||||
|   }, | ||||
|   { | ||||
|     "name": "nextContextImpl" | ||||
|   }, | ||||
| @ -1208,6 +1205,9 @@ | ||||
|   { | ||||
|     "name": "setActiveHostElement" | ||||
|   }, | ||||
|   { | ||||
|     "name": "setBindingIndex" | ||||
|   }, | ||||
|   { | ||||
|     "name": "setBindingRoot" | ||||
|   }, | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user