diff --git a/packages/core/src/render3/i18n.ts b/packages/core/src/render3/i18n.ts index 6687bea279..fbf3866358 100644 --- a/packages/core/src/render3/i18n.ts +++ b/packages/core/src/render3/i18n.ts @@ -7,7 +7,7 @@ */ import {assertEqual, assertLessThan} from './assert'; -import {NO_CHANGE, _getViewData, bindingUpdated, bindingUpdated2, bindingUpdated4, createLNode, getPreviousOrParentNode, getRenderer, load, resetApplicationState} from './instructions'; +import {NO_CHANGE, _getViewData, bindingUpdated, bindingUpdated2, bindingUpdated3, bindingUpdated4, createLNode, getPreviousOrParentNode, getRenderer, initBindings, load, resetApplicationState} from './instructions'; import {RENDER_PARENT} from './interfaces/container'; import {LContainerNode, LNode, TContainerNode, TElementNode, TNodeType} from './interfaces/node'; import {BINDING_INDEX, HEADER_OFFSET, TVIEW} from './interfaces/view'; @@ -383,7 +383,8 @@ export function i18nExpMapping( * @returns The concatenated string when any of the arguments changes, `NO_CHANGE` otherwise. */ export function i18nInterpolation1(instructions: I18nExpInstruction[], v0: any): string|NO_CHANGE { - const different = bindingUpdated(v0); + initBindings(); + const different = bindingUpdated(_getViewData()[BINDING_INDEX]++, v0); if (!different) { return NO_CHANGE; @@ -414,7 +415,10 @@ export function i18nInterpolation1(instructions: I18nExpInstruction[], v0: any): */ export function i18nInterpolation2(instructions: I18nExpInstruction[], v0: any, v1: any): string| NO_CHANGE { - const different = bindingUpdated2(v0, v1); + initBindings(); + const viewData = _getViewData(); + const different = bindingUpdated2(viewData[BINDING_INDEX], v0, v1); + viewData[BINDING_INDEX] += 2; if (!different) { return NO_CHANGE; @@ -452,8 +456,10 @@ export function i18nInterpolation2(instructions: I18nExpInstruction[], v0: any, */ export function i18nInterpolation3( instructions: I18nExpInstruction[], v0: any, v1: any, v2: any): string|NO_CHANGE { - let different = bindingUpdated2(v0, v1); - different = bindingUpdated(v2) || different; + initBindings(); + const viewData = _getViewData(); + const different = bindingUpdated3(viewData[BINDING_INDEX], v0, v1, v2); + viewData[BINDING_INDEX] += 3; if (!different) { return NO_CHANGE; @@ -493,7 +499,10 @@ export function i18nInterpolation3( */ export function i18nInterpolation4( instructions: I18nExpInstruction[], v0: any, v1: any, v2: any, v3: any): string|NO_CHANGE { - const different = bindingUpdated4(v0, v1, v2, v3); + initBindings(); + const viewData = _getViewData(); + const different = bindingUpdated4(viewData[BINDING_INDEX], v0, v1, v2, v3); + viewData[BINDING_INDEX] += 4; if (!different) { return NO_CHANGE; @@ -535,8 +544,11 @@ export function i18nInterpolation4( export function i18nInterpolation5( instructions: I18nExpInstruction[], v0: any, v1: any, v2: any, v3: any, v4: any): string| NO_CHANGE { - let different = bindingUpdated4(v0, v1, v2, v3); - different = bindingUpdated(v4) || different; + initBindings(); + const viewData = _getViewData(); + let different = bindingUpdated4(viewData[BINDING_INDEX], v0, v1, v2, v3); + different = bindingUpdated(viewData[BINDING_INDEX] + 4, v4) || different; + viewData[BINDING_INDEX] += 5; if (!different) { return NO_CHANGE; @@ -580,8 +592,11 @@ export function i18nInterpolation5( i18nInterpolation6( instructions: I18nExpInstruction[], v0: any, v1: any, v2: any, v3: any, v4: any, v5: any): string|NO_CHANGE { - let different = bindingUpdated4(v0, v1, v2, v3); - different = bindingUpdated2(v4, v5) || different; + initBindings(); + const viewData = _getViewData(); + let different = bindingUpdated4(viewData[BINDING_INDEX], v0, v1, v2, v3); + different = bindingUpdated2(viewData[BINDING_INDEX] + 4, v4, v5) || different; + viewData[BINDING_INDEX] += 6; if (!different) { return NO_CHANGE; @@ -626,9 +641,11 @@ i18nInterpolation6( export function i18nInterpolation7( instructions: I18nExpInstruction[], v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, v6: any): string|NO_CHANGE { - let different = bindingUpdated4(v0, v1, v2, v3); - different = bindingUpdated2(v4, v5) || different; - different = bindingUpdated(v6) || different; + initBindings(); + const viewData = _getViewData(); + let different = bindingUpdated4(viewData[BINDING_INDEX], v0, v1, v2, v3); + different = bindingUpdated3(viewData[BINDING_INDEX] + 4, v4, v5, v6) || different; + viewData[BINDING_INDEX] += 7; if (!different) { return NO_CHANGE; @@ -674,8 +691,11 @@ export function i18nInterpolation7( export function i18nInterpolation8( instructions: I18nExpInstruction[], v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, v6: any, v7: any): string|NO_CHANGE { - let different = bindingUpdated4(v0, v1, v2, v3); - different = bindingUpdated4(v4, v5, v6, v7) || different; + initBindings(); + const viewData = _getViewData(); + let different = bindingUpdated4(viewData[BINDING_INDEX], v0, v1, v2, v3); + different = bindingUpdated4(viewData[BINDING_INDEX] + 4, v4, v5, v6, v7) || different; + viewData[BINDING_INDEX] += 8; if (!different) { return NO_CHANGE; @@ -713,10 +733,12 @@ export function i18nInterpolation8( */ export function i18nInterpolationV(instructions: I18nExpInstruction[], values: any[]): string| NO_CHANGE { + initBindings(); + const viewData = _getViewData(); let different = false; for (let i = 0; i < values.length; i++) { // Check if bindings have changed - bindingUpdated(values[i]) && (different = true); + bindingUpdated(viewData[BINDING_INDEX]++, values[i]) && (different = true); } if (!different) { diff --git a/packages/core/src/render3/instructions.ts b/packages/core/src/render3/instructions.ts index 5c49f08dd8..3b9c127458 100644 --- a/packages/core/src/render3/instructions.ts +++ b/packages/core/src/render3/instructions.ts @@ -369,6 +369,7 @@ export function executeInitAndContentHooks(): void { export function createLViewData( renderer: Renderer3, tView: TView, context: T | null, flags: LViewFlags, sanitizer?: Sanitizer | null): LViewData { + // TODO(kara): create from blueprint return [ tView, // tView viewData, // parent @@ -2506,14 +2507,14 @@ export const NO_CHANGE = {} as NO_CHANGE; * This function must be called before any binding related function is called * (ie `bind()`, `interpolationX()`, `pureFunctionX()`) */ -function initBindings() { - ngDevMode && assertEqual( - viewData[BINDING_INDEX], -1, - 'Binding index should not yet be set ' + viewData[BINDING_INDEX]); +export function initBindings() { + // TODO(kara): remove this check when we have pre-filled array if (tView.bindingStartIndex === -1) { tView.bindingStartIndex = viewData.length; } - viewData[BINDING_INDEX] = tView.bindingStartIndex; + if (viewData[BINDING_INDEX] === -1) { + viewData[BINDING_INDEX] = tView.bindingStartIndex; + } } /** @@ -2522,26 +2523,11 @@ function initBindings() { * @param value Value to diff */ export function bind(value: T): T|NO_CHANGE { - return bindingUpdated(value) ? value : NO_CHANGE; + initBindings(); + return bindingUpdated(viewData[BINDING_INDEX]++, value) ? value : NO_CHANGE; } -/** - * Reserves slots for pure functions (`pureFunctionX` instructions) - * - * Bindings for pure functions are stored after the LNodes in the data array but before the binding. - * - * ---------------------------------------------------------------------------- - * | LNodes ... | pure function bindings | regular bindings / interpolations | - * ---------------------------------------------------------------------------- - * ^ - * TView.bindingStartIndex - * - * Pure function instructions are given an offset from TView.bindingStartIndex. - * Subtracting the offset from TView.bindingStartIndex gives the first index where the bindings - * are stored. - * - * NOTE: reserveSlots instructions are only ever allowed at the very end of the creation block - */ +// TODO(kara): Remove this when updating the compiler (cannot remove without breaking JIT test) export function reserveSlots(numSlots: number) { // Init the slots with a unique `NO_CHANGE` value so that the first change is always detected // whether it happens or not during the first change detection pass - pure functions checks @@ -2553,29 +2539,6 @@ export function reserveSlots(numSlots: number) { initBindings(); } -/** - * Sets up the binding index before executing any `pureFunctionX` instructions. - * - * The index must be restored after the pure function is executed - * - * {@link reserveSlots} - */ -export function moveBindingIndexToReservedSlot(offset: number): number { - const currentSlot = viewData[BINDING_INDEX]; - viewData[BINDING_INDEX] = tView.bindingStartIndex - offset; - return currentSlot; -} - -/** - * Restores the binding index to the given value. - * - * This function is typically used to restore the index after a `pureFunctionX` has - * been executed. - */ -export function restoreBindingIndex(index: number): void { - viewData[BINDING_INDEX] = index; -} - /** * Create interpolation bindings with a variable number of expressions. * @@ -2591,12 +2554,12 @@ export function restoreBindingIndex(index: number): void { export function interpolationV(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'); - + initBindings(); let different = false; for (let i = 1; i < values.length; i += 2) { // Check if bindings (odd indexes) have changed - bindingUpdated(values[i]) && (different = true); + bindingUpdated(viewData[BINDING_INDEX]++, values[i]) && (different = true); } if (!different) { @@ -2620,15 +2583,17 @@ export function interpolationV(values: any[]): string|NO_CHANGE { * @param suffix static value used for concatenation only. */ export function interpolation1(prefix: string, v0: any, suffix: string): string|NO_CHANGE { - const different = bindingUpdated(v0); - + initBindings(); + const different = bindingUpdated(viewData[BINDING_INDEX]++, v0); return different ? prefix + stringify(v0) + suffix : NO_CHANGE; } /** Creates an interpolation binding with 2 expressions. */ export function interpolation2( prefix: string, v0: any, i0: string, v1: any, suffix: string): string|NO_CHANGE { - const different = bindingUpdated2(v0, v1); + initBindings(); + const different = bindingUpdated2(viewData[BINDING_INDEX], v0, v1); + viewData[BINDING_INDEX] += 2; return different ? prefix + stringify(v0) + i0 + stringify(v1) + suffix : NO_CHANGE; } @@ -2637,8 +2602,9 @@ export function interpolation2( export function interpolation3( prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, suffix: string): string| NO_CHANGE { - let different = bindingUpdated2(v0, v1); - different = bindingUpdated(v2) || different; + initBindings(); + const different = bindingUpdated3(viewData[BINDING_INDEX], v0, v1, v2); + viewData[BINDING_INDEX] += 3; return different ? prefix + stringify(v0) + i0 + stringify(v1) + i1 + stringify(v2) + suffix : NO_CHANGE; @@ -2648,7 +2614,9 @@ export function interpolation3( export function interpolation4( prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, suffix: string): string|NO_CHANGE { - const different = bindingUpdated4(v0, v1, v2, v3); + initBindings(); + const different = bindingUpdated4(viewData[BINDING_INDEX], v0, v1, v2, v3); + viewData[BINDING_INDEX] += 4; return different ? prefix + stringify(v0) + i0 + stringify(v1) + i1 + stringify(v2) + i2 + stringify(v3) + @@ -2660,8 +2628,10 @@ export function interpolation4( export function interpolation5( 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 { - let different = bindingUpdated4(v0, v1, v2, v3); - different = bindingUpdated(v4) || different; + initBindings(); + let different = bindingUpdated4(viewData[BINDING_INDEX], v0, v1, v2, v3); + different = bindingUpdated(viewData[BINDING_INDEX] + 4, v4) || different; + viewData[BINDING_INDEX] += 5; return different ? prefix + stringify(v0) + i0 + stringify(v1) + i1 + stringify(v2) + i2 + stringify(v3) + i3 + @@ -2673,8 +2643,10 @@ export function interpolation5( export function interpolation6( 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 { - let different = bindingUpdated4(v0, v1, v2, v3); - different = bindingUpdated2(v4, v5) || different; + initBindings(); + let different = bindingUpdated4(viewData[BINDING_INDEX], v0, v1, v2, v3); + different = bindingUpdated2(viewData[BINDING_INDEX] + 4, v4, v5) || different; + viewData[BINDING_INDEX] += 6; return different ? prefix + stringify(v0) + i0 + stringify(v1) + i1 + stringify(v2) + i2 + stringify(v3) + i3 + @@ -2687,9 +2659,10 @@ export function interpolation7( 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 { - let different = bindingUpdated4(v0, v1, v2, v3); - different = bindingUpdated2(v4, v5) || different; - different = bindingUpdated(v6) || different; + initBindings(); + let different = bindingUpdated4(viewData[BINDING_INDEX], v0, v1, v2, v3); + different = bindingUpdated3(viewData[BINDING_INDEX] + 4, v4, v5, v6) || different; + viewData[BINDING_INDEX] += 7; return different ? prefix + stringify(v0) + i0 + stringify(v1) + i1 + stringify(v2) + i2 + stringify(v3) + i3 + @@ -2702,8 +2675,10 @@ export function interpolation8( 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 { - let different = bindingUpdated4(v0, v1, v2, v3); - different = bindingUpdated4(v4, v5, v6, v7) || different; + initBindings(); + let different = bindingUpdated4(viewData[BINDING_INDEX], v0, v1, v2, v3); + different = bindingUpdated4(viewData[BINDING_INDEX] + 4, v4, v5, v6, v7) || different; + viewData[BINDING_INDEX] += 8; return different ? prefix + stringify(v0) + i0 + stringify(v1) + i1 + stringify(v2) + i2 + stringify(v3) + i3 + @@ -2770,49 +2745,51 @@ export function loadElement(index: number): LElementNode { return loadElementInternal(index, viewData); } -/** Gets the current binding value and increments the binding index. */ -export function consumeBinding(): any { - ngDevMode && assertDataInRange(viewData[BINDING_INDEX]); +/** Gets the current binding value. */ +export function getBinding(bindingIndex: number): any { + ngDevMode && assertDataInRange(viewData[bindingIndex]); ngDevMode && - assertNotEqual( - viewData[viewData[BINDING_INDEX]], NO_CHANGE, 'Stored value should never be NO_CHANGE.'); - return viewData[viewData[BINDING_INDEX]++]; + assertNotEqual(viewData[bindingIndex], NO_CHANGE, 'Stored value should never be NO_CHANGE.'); + return viewData[bindingIndex]; } /** Updates binding if changed, then returns whether it was updated. */ -export function bindingUpdated(value: any): boolean { +export function bindingUpdated(bindingIndex: number, value: any): boolean { ngDevMode && assertNotEqual(value, NO_CHANGE, 'Incoming value should never be NO_CHANGE.'); - if (viewData[BINDING_INDEX] === -1) initBindings(); - const bindingIndex = viewData[BINDING_INDEX]; if (bindingIndex >= viewData.length) { - viewData[viewData[BINDING_INDEX]++] = value; + viewData[bindingIndex] = value; } else if (isDifferent(viewData[bindingIndex], value, checkNoChangesMode)) { throwErrorIfNoChangesMode(creationMode, checkNoChangesMode, viewData[bindingIndex], value); - viewData[viewData[BINDING_INDEX]++] = value; + viewData[bindingIndex] = value; } else { - viewData[BINDING_INDEX]++; return false; } return true; } -/** Updates binding if changed, then returns the latest value. */ -export function checkAndUpdateBinding(value: any): any { - bindingUpdated(value); - return value; +/** Updates binding and returns the value. */ +export function updateBinding(bindingIndex: number, value: any): any { + return viewData[bindingIndex] = value; } /** Updates 2 bindings if changed, then returns whether either was updated. */ -export function bindingUpdated2(exp1: any, exp2: any): boolean { - const different = bindingUpdated(exp1); - return bindingUpdated(exp2) || different; +export function bindingUpdated2(bindingIndex: number, exp1: any, exp2: any): boolean { + const different = bindingUpdated(bindingIndex, exp1); + return bindingUpdated(bindingIndex + 1, exp2) || different; +} + +/** Updates 3 bindings if changed, then returns whether any was updated. */ +export function bindingUpdated3(bindingIndex: number, exp1: any, exp2: any, exp3: any): boolean { + const different = bindingUpdated2(bindingIndex, exp1, exp2); + return bindingUpdated(bindingIndex + 2, exp3) || different; } /** Updates 4 bindings if changed, then returns whether any was updated. */ -export function bindingUpdated4(exp1: any, exp2: any, exp3: any, exp4: any): boolean { - const different = bindingUpdated2(exp1, exp2); - return bindingUpdated2(exp3, exp4) || different; +export function bindingUpdated4( + bindingIndex: number, exp1: any, exp2: any, exp3: any, exp4: any): boolean { + const different = bindingUpdated2(bindingIndex, exp1, exp2); + return bindingUpdated2(bindingIndex + 2, exp3, exp4) || different; } export function getTView(): TView { @@ -2856,22 +2833,6 @@ function assertDataNext(index: number, arr?: any[]) { arr.length, index, `index ${index} expected to be at the end of arr (length ${arr.length})`); } -/** - * On the first template pass, the reserved slots should be set `NO_CHANGE`. - * - * If not, they might not have been actually reserved. - */ -export function assertReservedSlotInitialized(slotOffset: number, numSlots: number) { - if (firstTemplatePass) { - const startIndex = tView.bindingStartIndex - slotOffset; - for (let i = 0; i < numSlots; i++) { - assertEqual( - viewData[startIndex + i], NO_CHANGE, - 'The reserved slots should be set to `NO_CHANGE` on first template pass'); - } - } -} - export function _getComponentHostLElementNode(component: T): LElementNode { ngDevMode && assertDefined(component, 'expecting component got null'); const lElementNode = (component as any)[NG_HOST_SYMBOL] as LElementNode; diff --git a/packages/core/src/render3/pure_function.ts b/packages/core/src/render3/pure_function.ts index ecf5be9666..5c793f4475 100644 --- a/packages/core/src/render3/pure_function.ts +++ b/packages/core/src/render3/pure_function.ts @@ -6,34 +6,44 @@ * found in the LICENSE file at https://angular.io/license */ -import {assertReservedSlotInitialized, bindingUpdated, bindingUpdated2, bindingUpdated4, checkAndUpdateBinding, consumeBinding, getCreationMode, moveBindingIndexToReservedSlot, restoreBindingIndex} from './instructions'; - +import {bindingUpdated, bindingUpdated2, bindingUpdated4, updateBinding, getBinding, getCreationMode, getTView, initBindings, bindingUpdated3,} from './instructions'; +/** + * Bindings for pure functions are stored after regular bindings. + * + * ---------------------------------------------------------------------------- + * | LNodes ... | regular bindings / interpolations | pure function bindings + * ---------------------------------------------------------------------------- + * ^ + * TView.bindingStartIndex + * + * Pure function instructions are given an offset from TView.bindingStartIndex. + * Adding the offset to TView.bindingStartIndex gives the first index where the bindings + * are stored. + */ /** * If the value hasn't been saved, calls the pure function to store and return the * value. If it has been saved, returns the saved value. * + * @param slotOffset the offset from binding root to the reserved slot * @param pureFn Function that returns a value - * @param slotOffset the offset in the reserved slot space {@link reserveSlots} * @param thisArg Optional calling context of pureFn * @returns value */ export function pureFunction0(slotOffset: number, pureFn: () => T, thisArg?: any): T { - ngDevMode && assertReservedSlotInitialized(slotOffset, 1); - const index = moveBindingIndexToReservedSlot(slotOffset); - const value = getCreationMode() ? - checkAndUpdateBinding(thisArg ? pureFn.call(thisArg) : pureFn()) : - consumeBinding(); - restoreBindingIndex(index); - return value; + initBindings(); // TODO(kara): remove this check when we have pre-filled array + const bindingIndex = getTView().bindingStartIndex + slotOffset; + return getCreationMode() ? + updateBinding(bindingIndex, thisArg ? pureFn.call(thisArg) : pureFn()) : + getBinding(bindingIndex); } /** * If the value of the provided exp has changed, calls the pure function to return * an updated value. Or if the value has not changed, returns cached value. * - * @param slotOffset the offset in the reserved slot space {@link reserveSlots} + * @param slotOffset the offset from binding root to the reserved slot * @param pureFn Function that returns an updated value * @param exp Updated expression value * @param thisArg Optional calling context of pureFn @@ -41,20 +51,18 @@ export function pureFunction0(slotOffset: number, pureFn: () => T, thisArg?: */ export function pureFunction1( slotOffset: number, pureFn: (v: any) => any, exp: any, thisArg?: any): any { - ngDevMode && assertReservedSlotInitialized(slotOffset, 2); - const index = moveBindingIndexToReservedSlot(slotOffset); - const value = bindingUpdated(exp) ? - checkAndUpdateBinding(thisArg ? pureFn.call(thisArg, exp) : pureFn(exp)) : - consumeBinding(); - restoreBindingIndex(index); - return value; + initBindings(); // TODO(kara): remove this check when we have pre-filled array + const bindingIndex = getTView().bindingStartIndex + slotOffset; + return bindingUpdated(bindingIndex, exp) ? + updateBinding(bindingIndex + 1, thisArg ? pureFn.call(thisArg, exp) : pureFn(exp)) : + getBinding(bindingIndex + 1); } /** * If the value of any provided exp has changed, calls the pure function to return * an updated value. Or if no values have changed, returns cached value. * - * @param slotOffset the offset in the reserved slot space {@link reserveSlots} + * @param slotOffset the offset from binding root to the reserved slot * @param pureFn * @param exp1 * @param exp2 @@ -64,20 +72,19 @@ export function pureFunction1( export function pureFunction2( slotOffset: number, pureFn: (v1: any, v2: any) => any, exp1: any, exp2: any, thisArg?: any): any { - ngDevMode && assertReservedSlotInitialized(slotOffset, 3); - const index = moveBindingIndexToReservedSlot(slotOffset); - const value = bindingUpdated2(exp1, exp2) ? - checkAndUpdateBinding(thisArg ? pureFn.call(thisArg, exp1, exp2) : pureFn(exp1, exp2)) : - consumeBinding(); - restoreBindingIndex(index); - return value; + initBindings(); // TODO(kara): remove this check when we have pre-filled array + const bindingIndex = getTView().bindingStartIndex + slotOffset; + return bindingUpdated2(bindingIndex, exp1, exp2) ? + updateBinding( + bindingIndex + 2, thisArg ? pureFn.call(thisArg, exp1, exp2) : pureFn(exp1, exp2)) : + getBinding(bindingIndex + 2); } /** * If the value of any provided exp has changed, calls the pure function to return * an updated value. Or if no values have changed, returns cached value. * - * @param slotOffset the offset in the reserved slot space {@link reserveSlots} + * @param slotOffset the offset from binding root to the reserved slot * @param pureFn * @param exp1 * @param exp2 @@ -88,22 +95,20 @@ export function pureFunction2( export function pureFunction3( slotOffset: number, pureFn: (v1: any, v2: any, v3: any) => any, exp1: any, exp2: any, exp3: any, thisArg?: any): any { - ngDevMode && assertReservedSlotInitialized(slotOffset, 4); - const index = moveBindingIndexToReservedSlot(slotOffset); - const different = bindingUpdated2(exp1, exp2); - const value = bindingUpdated(exp3) || different ? - checkAndUpdateBinding( + initBindings(); // TODO(kara): remove this check when we have pre-filled array + const bindingIndex = getTView().bindingStartIndex + slotOffset; + return bindingUpdated3(bindingIndex, exp1, exp2, exp3) ? + updateBinding( + bindingIndex + 3, thisArg ? pureFn.call(thisArg, exp1, exp2, exp3) : pureFn(exp1, exp2, exp3)) : - consumeBinding(); - restoreBindingIndex(index); - return value; + getBinding(bindingIndex + 3); } /** * If the value of any provided exp has changed, calls the pure function to return * an updated value. Or if no values have changed, returns cached value. * - * @param slotOffset the offset in the reserved slot space {@link reserveSlots} + * @param slotOffset the offset from binding root to the reserved slot * @param pureFn * @param exp1 * @param exp2 @@ -115,21 +120,20 @@ export function pureFunction3( export function pureFunction4( slotOffset: number, pureFn: (v1: any, v2: any, v3: any, v4: any) => any, exp1: any, exp2: any, exp3: any, exp4: any, thisArg?: any): any { - ngDevMode && assertReservedSlotInitialized(slotOffset, 5); - const index = moveBindingIndexToReservedSlot(slotOffset); - const value = bindingUpdated4(exp1, exp2, exp3, exp4) ? - checkAndUpdateBinding( + initBindings(); // TODO(kara): remove this check when we have pre-filled array + const bindingIndex = getTView().bindingStartIndex + slotOffset; + return bindingUpdated4(bindingIndex, exp1, exp2, exp3, exp4) ? + updateBinding( + bindingIndex + 4, thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4) : pureFn(exp1, exp2, exp3, exp4)) : - consumeBinding(); - restoreBindingIndex(index); - return value; + getBinding(bindingIndex + 4); } /** * If the value of any provided exp has changed, calls the pure function to return * an updated value. Or if no values have changed, returns cached value. * - * @param slotOffset the offset in the reserved slot space {@link reserveSlots} + * @param slotOffset the offset from binding root to the reserved slot * @param pureFn * @param exp1 * @param exp2 @@ -142,23 +146,21 @@ export function pureFunction4( export function pureFunction5( slotOffset: number, pureFn: (v1: any, v2: any, v3: any, v4: any, v5: any) => any, exp1: any, exp2: any, exp3: any, exp4: any, exp5: any, thisArg?: any): any { - ngDevMode && assertReservedSlotInitialized(slotOffset, 6); - const index = moveBindingIndexToReservedSlot(slotOffset); - const different = bindingUpdated4(exp1, exp2, exp3, exp4); - const value = bindingUpdated(exp5) || different ? - checkAndUpdateBinding( - thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5) : - pureFn(exp1, exp2, exp3, exp4, exp5)) : - consumeBinding(); - restoreBindingIndex(index); - return value; + initBindings(); // TODO(kara): remove this check when we have pre-filled array + const bindingIndex = getTView().bindingStartIndex + slotOffset; + const different = bindingUpdated4(bindingIndex, exp1, exp2, exp3, exp4); + return bindingUpdated(bindingIndex + 4, exp5) || different ? + updateBinding( + bindingIndex + 5, thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5) : + pureFn(exp1, exp2, exp3, exp4, exp5)) : + getBinding(bindingIndex + 5); } /** * If the value of any provided exp has changed, calls the pure function to return * an updated value. Or if no values have changed, returns cached value. * - * @param slotOffset the offset in the reserved slot space {@link reserveSlots} + * @param slotOffset the offset from binding root to the reserved slot * @param pureFn * @param exp1 * @param exp2 @@ -172,23 +174,21 @@ export function pureFunction5( export function pureFunction6( slotOffset: number, pureFn: (v1: any, v2: any, v3: any, v4: any, v5: any, v6: any) => any, exp1: any, exp2: any, exp3: any, exp4: any, exp5: any, exp6: any, thisArg?: any): any { - ngDevMode && assertReservedSlotInitialized(slotOffset, 7); - const index = moveBindingIndexToReservedSlot(slotOffset); - const different = bindingUpdated4(exp1, exp2, exp3, exp4); - const value = bindingUpdated2(exp5, exp6) || different ? - checkAndUpdateBinding( - thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5, exp6) : - pureFn(exp1, exp2, exp3, exp4, exp5, exp6)) : - consumeBinding(); - restoreBindingIndex(index); - return value; + initBindings(); // TODO(kara): remove this check when we have pre-filled array + const bindingIndex = getTView().bindingStartIndex + slotOffset; + const different = bindingUpdated4(bindingIndex, exp1, exp2, exp3, exp4); + return bindingUpdated2(bindingIndex + 4, exp5, exp6) || different ? + updateBinding( + bindingIndex + 6, thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5, exp6) : + pureFn(exp1, exp2, exp3, exp4, exp5, exp6)) : + getBinding(bindingIndex + 6); } /** * If the value of any provided exp has changed, calls the pure function to return * an updated value. Or if no values have changed, returns cached value. * - * @param slotOffset the offset in the reserved slot space {@link reserveSlots} + * @param slotOffset the offset from binding root to the reserved slot * @param pureFn * @param exp1 * @param exp2 @@ -204,24 +204,22 @@ export function pureFunction7( slotOffset: number, pureFn: (v1: any, v2: any, v3: any, v4: any, v5: any, v6: any, v7: any) => any, exp1: any, exp2: any, exp3: any, exp4: any, exp5: any, exp6: any, exp7: any, thisArg?: any): any { - ngDevMode && assertReservedSlotInitialized(slotOffset, 8); - const index = moveBindingIndexToReservedSlot(slotOffset); - let different = bindingUpdated4(exp1, exp2, exp3, exp4); - different = bindingUpdated2(exp5, exp6) || different; - const value = bindingUpdated(exp7) || different ? - checkAndUpdateBinding( - thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5, exp6, exp7) : - pureFn(exp1, exp2, exp3, exp4, exp5, exp6, exp7)) : - consumeBinding(); - restoreBindingIndex(index); - return value; + initBindings(); // TODO(kara): remove this check when we have pre-filled array + const bindingIndex = getTView().bindingStartIndex + slotOffset; + let different = bindingUpdated4(bindingIndex, exp1, exp2, exp3, exp4); + return bindingUpdated3(bindingIndex + 4, exp5, exp6, exp7) || different ? + updateBinding( + bindingIndex + 7, thisArg ? + pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5, exp6, exp7) : + pureFn(exp1, exp2, exp3, exp4, exp5, exp6, exp7)) : + getBinding(bindingIndex + 7); } /** * If the value of any provided exp has changed, calls the pure function to return * an updated value. Or if no values have changed, returns cached value. * - * @param slotOffset the offset in the reserved slot space {@link reserveSlots} + * @param slotOffset the offset from binding root to the reserved slot * @param pureFn * @param exp1 * @param exp2 @@ -239,16 +237,15 @@ export function pureFunction8( pureFn: (v1: any, v2: any, v3: any, v4: any, v5: any, v6: any, v7: any, v8: any) => any, exp1: any, exp2: any, exp3: any, exp4: any, exp5: any, exp6: any, exp7: any, exp8: any, thisArg?: any): any { - ngDevMode && assertReservedSlotInitialized(slotOffset, 9); - const index = moveBindingIndexToReservedSlot(slotOffset); - const different = bindingUpdated4(exp1, exp2, exp3, exp4); - const value = bindingUpdated4(exp5, exp6, exp7, exp8) || different ? - checkAndUpdateBinding( - thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5, exp6, exp7, exp8) : - pureFn(exp1, exp2, exp3, exp4, exp5, exp6, exp7, exp8)) : - consumeBinding(); - restoreBindingIndex(index); - return value; + initBindings(); // TODO(kara): remove this check when we have pre-filled array + const bindingIndex = getTView().bindingStartIndex + slotOffset; + const different = bindingUpdated4(bindingIndex, exp1, exp2, exp3, exp4); + return bindingUpdated4(bindingIndex + 4, exp5, exp6, exp7, exp8) || different ? + updateBinding( + bindingIndex + 8, thisArg ? + pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5, exp6, exp7, exp8) : + pureFn(exp1, exp2, exp3, exp4, exp5, exp6, exp7, exp8)) : + getBinding(bindingIndex + 8); } /** @@ -257,7 +254,7 @@ export function pureFunction8( * If the value of any provided exp has changed, calls the pure function to return * an updated value. Or if no values have changed, returns cached value. * - * @param slotOffset the offset in the reserved slot space {@link reserveSlots} + * @param slotOffset the offset from binding root to the reserved slot * @param pureFn A pure function that takes binding values and builds an object or array * containing those values. * @param exps An array of binding values @@ -266,14 +263,12 @@ export function pureFunction8( */ export function pureFunctionV( slotOffset: number, pureFn: (...v: any[]) => any, exps: any[], thisArg?: any): any { - ngDevMode && assertReservedSlotInitialized(slotOffset, exps.length + 1); - const index = moveBindingIndexToReservedSlot(slotOffset); - + initBindings(); // TODO(kara): remove this check when we have pre-filled array + let bindingIndex = getTView().bindingStartIndex + slotOffset; let different = false; for (let i = 0; i < exps.length; i++) { - bindingUpdated(exps[i]) && (different = true); + bindingUpdated(bindingIndex++, exps[i]) && (different = true); } - const value = different ? checkAndUpdateBinding(pureFn.apply(thisArg, exps)) : consumeBinding(); - restoreBindingIndex(index); - return value; + return different ? updateBinding(bindingIndex, pureFn.apply(thisArg, exps)) : + getBinding(bindingIndex); } diff --git a/packages/core/test/render3/compiler_canonical/component_directives_spec.ts b/packages/core/test/render3/compiler_canonical/component_directives_spec.ts index 7b21572ba4..5e9f03d931 100644 --- a/packages/core/test/render3/compiler_canonical/component_directives_spec.ts +++ b/packages/core/test/render3/compiler_canonical/component_directives_spec.ts @@ -503,7 +503,6 @@ describe('components & directives', () => { template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) { if (rf & 1) { $r3$.ɵelement(0, 'my-array-comp'); - $r3$.ɵreserveSlots(1); } if (rf & 2) { $r3$.ɵelementProperty( @@ -570,7 +569,6 @@ describe('components & directives', () => { template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) { if (rf & 1) { $r3$.ɵelement(0, 'my-comp'); - $r3$.ɵreserveSlots(1); } if (rf & 2) { $r3$.ɵelementProperty( @@ -614,7 +612,6 @@ describe('components & directives', () => { template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) { if (rf & 1) { $r3$.ɵelement(0, 'my-array-comp'); - $r3$.ɵreserveSlots(2); } if (rf & 2) { $r3$.ɵelementProperty( @@ -726,7 +723,6 @@ describe('components & directives', () => { template: function MyApp_Template(rf: $RenderFlags$, c: $any$) { if (rf & 1) { $r3$.ɵelement(0, 'my-comp'); - $r3$.ɵreserveSlots(10); } if (rf & 2) { $r3$.ɵelementProperty( @@ -805,7 +801,6 @@ describe('components & directives', () => { template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) { if (rf & 1) { $r3$.ɵelement(0, 'object-comp'); - $r3$.ɵreserveSlots(2); } if (rf & 2) { $r3$.ɵelementProperty( @@ -892,7 +887,6 @@ describe('components & directives', () => { template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) { if (rf & 1) { $r3$.ɵelement(0, 'nested-comp'); - $r3$.ɵreserveSlots(7); } if (rf & 2) { $r3$.ɵelementProperty( diff --git a/packages/core/test/render3/compiler_canonical/pipes_spec.ts b/packages/core/test/render3/compiler_canonical/pipes_spec.ts index 54cd348ae7..d680ac642a 100644 --- a/packages/core/test/render3/compiler_canonical/pipes_spec.ts +++ b/packages/core/test/render3/compiler_canonical/pipes_spec.ts @@ -88,7 +88,6 @@ describe('pipes', () => { $r3$.ɵtext(0); $r3$.ɵpipe(1, 'myPipe'); $r3$.ɵpipe(2, 'myPurePipe'); - $r3$.ɵreserveSlots(6); } if (rf & 2) { $r3$.ɵtextBinding( @@ -150,6 +149,21 @@ describe('pipes', () => { // /NORMATIVE } + function MyApp_div_Template_4(rf: $RenderFlags$, ctx: any) { + if (rf & 1) { + $r3$.ɵelementStart(0, 'div'); + $r3$.ɵtext(1); + $r3$.ɵpipe(2, 'myPurePipe'); + $r3$.ɵelementEnd(); + $r3$.ɵreserveSlots(3); + } + if (rf & 2) { + const $comp$ = $r3$.ɵnextContext(); + $r3$.ɵtextBinding( + 1, $r3$.ɵinterpolation1('', $r3$.ɵpipeBind2(2, 3, $comp$.name, $comp$.size), '')); + } + } + @Component({ template: `{{name | myPurePipe:size}}{{name | myPurePipe:size}}
{{name | myPurePipe:size}}
` @@ -170,7 +184,7 @@ describe('pipes', () => { $r3$.ɵpipe(1, 'myPurePipe'); $r3$.ɵtext(2); $r3$.ɵpipe(3, 'myPurePipe'); - $r3$.ɵtemplate(4, C4, '', ['oneTimeIf', '']); + $r3$.ɵtemplate(4, MyApp_div_Template_4, '', ['oneTimeIf', '']); $r3$.ɵreserveSlots(6); } if (rf & 2) { @@ -182,20 +196,6 @@ describe('pipes', () => { $r3$.ɵcontainerRefreshStart(4); $r3$.ɵcontainerRefreshEnd(); } - - function C4(rf: $RenderFlags$, ctx1: $any$) { - if (rf & 1) { - $r3$.ɵelementStart(0, 'div'); - $r3$.ɵtext(1); - $r3$.ɵpipe(2, 'myPurePipe'); - $r3$.ɵelementEnd(); - $r3$.ɵreserveSlots(3); - } - if (rf & 2) { - $r3$.ɵtextBinding( - 1, $r3$.ɵinterpolation1('', $r3$.ɵpipeBind2(2, 3, ctx.name, ctx.size), '')); - } - } } }); // /NORMATIVE diff --git a/packages/core/test/render3/pipe_spec.ts b/packages/core/test/render3/pipe_spec.ts index 3d7c0369d6..b81731583e 100644 --- a/packages/core/test/render3/pipe_spec.ts +++ b/packages/core/test/render3/pipe_spec.ts @@ -10,7 +10,7 @@ import {Directive, OnChanges, OnDestroy, Pipe, PipeTransform} from '@angular/cor import {expect} from '@angular/platform-browser/testing/src/matchers'; import {defineDirective, definePipe} from '../../src/render3/definition'; -import {bind, container, containerRefreshEnd, containerRefreshStart, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, load, loadDirective, reserveSlots, text, textBinding} from '../../src/render3/instructions'; +import {bind, container, containerRefreshEnd, containerRefreshStart, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, load, loadDirective, text, textBinding} from '../../src/render3/instructions'; import {RenderFlags} from '../../src/render3/interfaces/definition'; import {pipe, pipeBind1, pipeBind3, pipeBind4, pipeBindV} from '../../src/render3/pipe'; @@ -38,10 +38,9 @@ describe('pipe', () => { if (rf & RenderFlags.Create) { text(0); pipe(1, 'countingPipe'); - reserveSlots(2); } if (rf & RenderFlags.Update) { - textBinding(0, interpolation1('', pipeBind1(1, 2, person.name), '')); + textBinding(0, interpolation1('', pipeBind1(1, 1, person.name), '')); } } @@ -54,10 +53,9 @@ describe('pipe', () => { if (rf & RenderFlags.Create) { text(0); pipe(1, 'randomPipeName'); - reserveSlots(2); } if (rf & RenderFlags.Update) { - textBinding(0, interpolation1('', pipeBind1(1, 2, ctx.value), '')); + textBinding(0, interpolation1('', pipeBind1(1, 1, ctx.value), '')); } }, [], pipes); @@ -99,10 +97,9 @@ describe('pipe', () => { elementStart(0, 'div', ['myDir', '']); pipe(1, 'double'); elementEnd(); - reserveSlots(2); } if (rf & RenderFlags.Update) { - elementProperty(0, 'elprop', bind(pipeBind1(1, 2, ctx))); + elementProperty(0, 'elprop', bind(pipeBind1(1, 1, ctx))); directive = loadDirective(0); } } @@ -115,11 +112,10 @@ describe('pipe', () => { if (rf & RenderFlags.Create) { text(0); pipe(1, 'multiArgPipe'); - reserveSlots(4); } if (rf & RenderFlags.Update) { textBinding( - 0, interpolation1('', pipeBind3(1, 4, person.name, 'one', person.address !.city), '')); + 0, interpolation1('', pipeBind3(1, 1, person.name, 'one', person.address !.city), '')); } } @@ -133,12 +129,11 @@ describe('pipe', () => { text(0); pipe(1, 'multiArgPipe'); pipe(2, 'multiArgPipe'); - reserveSlots(9); } if (rf & RenderFlags.Update) { textBinding( 0, interpolation1( - '', pipeBind4(2, 9, pipeBindV(1, 4, [person.name, 'a', 'b']), 0, 1, 2), '')); + '', pipeBind4(2, 5, pipeBindV(1, 1, [person.name, 'a', 'b']), 0, 1, 2), '')); } } @@ -163,10 +158,9 @@ describe('pipe', () => { elementStart(0, 'div'); pipe(1, 'identityPipe'); elementEnd(); - reserveSlots(2); } if (rf & RenderFlags.Update) { - elementProperty(0, 'someProp', bind(pipeBind1(1, 2, 'Megatron'))); + elementProperty(0, 'someProp', bind(pipeBind1(1, 1, 'Megatron'))); } } @@ -184,10 +178,9 @@ describe('pipe', () => { if (rf & RenderFlags.Create) { text(0); pipe(1, 'countingPipe'); - reserveSlots(2); } if (rf & RenderFlags.Update) { - textBinding(0, interpolation1('', pipeBind1(1, 2, person.name), '')); + textBinding(0, interpolation1('', pipeBind1(1, 1, person.name), '')); } } @@ -214,10 +207,9 @@ describe('pipe', () => { if (rf & RenderFlags.Create) { text(0); pipe(1, 'countingImpurePipe'); - reserveSlots(2); } if (rf & RenderFlags.Update) { - textBinding(0, interpolation1('', pipeBind1(1, 2, person.name), '')); + textBinding(0, interpolation1('', pipeBind1(1, 1, person.name), '')); } } @@ -236,7 +228,6 @@ describe('pipe', () => { pipe(3, 'countingImpurePipe'); elementEnd(); container(4); - reserveSlots(4); } if (rf & RenderFlags.Update) { elementProperty(0, 'someProp', bind(pipeBind1(1, 2, true))); @@ -251,10 +242,9 @@ describe('pipe', () => { elementStart(0, 'div'); pipe(1, 'countingImpurePipe'); elementEnd(); - reserveSlots(2); } if (rf1 & RenderFlags.Update) { - elementProperty(0, 'someProp', bind(pipeBind1(1, 2, true))); + elementProperty(0, 'someProp', bind(pipeBind1(1, 1, true))); pipeInstances.push(load(1)); } } @@ -306,10 +296,9 @@ describe('pipe', () => { if (rf1 & RenderFlags.Create) { text(0); pipe(1, 'pipeWithOnDestroy'); - reserveSlots(2); } if (rf & RenderFlags.Update) { - textBinding(0, interpolation1('', pipeBind1(1, 2, person.age), '')); + textBinding(0, interpolation1('', pipeBind1(1, 1, person.age), '')); } } embeddedViewEnd(); diff --git a/packages/core/test/render3/pure_function_spec.ts b/packages/core/test/render3/pure_function_spec.ts index 593c08cf33..6591cc6c6f 100644 --- a/packages/core/test/render3/pure_function_spec.ts +++ b/packages/core/test/render3/pure_function_spec.ts @@ -5,11 +5,12 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {defineComponent} from '../../src/render3/index'; -import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, load, loadDirective, reserveSlots} from '../../src/render3/instructions'; +import {AttributeMarker, defineComponent, template} from '../../src/render3/index'; +import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, loadDirective, nextContext} from '../../src/render3/instructions'; import {RenderFlags} from '../../src/render3/interfaces/definition'; import {pureFunction1, pureFunction2, pureFunction3, pureFunction4, pureFunction5, pureFunction6, pureFunction7, pureFunction8, pureFunctionV} from '../../src/render3/pure_function'; -import {renderToHtml} from '../../test/render3/render_util'; +import {ComponentFixture, createComponent, renderToHtml} from '../../test/render3/render_util'; +import {NgIf} from './common_with_def'; describe('array literals', () => { let myComp: MyComp; @@ -36,10 +37,9 @@ describe('array literals', () => { function Template(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { element(0, 'my-comp'); - reserveSlots(2); } if (rf & RenderFlags.Update) { - elementProperty(0, 'names', bind(pureFunction1(2, e0_ff, ctx.customName))); + elementProperty(0, 'names', bind(pureFunction1(1, e0_ff, ctx.customName))); } } @@ -64,6 +64,39 @@ describe('array literals', () => { expect(myComp !.names).toEqual(['should not be overwritten']); }); + it('should support array literals in dynamic views', () => { + const e0_ff = (v: any) => ['Nancy', v, 'Bess']; + + function IfTemplate(rf: RenderFlags, ctx: any) { + if (rf & RenderFlags.Create) { + element(0, 'my-comp'); + } + if (rf & RenderFlags.Update) { + const comp = nextContext(); + elementProperty(0, 'names', bind(pureFunction1(1, e0_ff, comp.customName))); + } + } + + /** + * + */ + const App = createComponent('app', function(rf: RenderFlags, ctx: any) { + if (rf & RenderFlags.Create) { + template(0, IfTemplate, null, [AttributeMarker.SelectOnly, 'ngIf']); + } + if (rf & RenderFlags.Update) { + elementProperty(0, 'ngIf', bind(ctx.showing)); + } + }, [MyComp, NgIf]); + + const fixture = new ComponentFixture(App); + fixture.component.showing = true; + fixture.component.customName = 'Carson'; + fixture.update(); + + expect(myComp !.names).toEqual(['Nancy', 'Carson', 'Bess']); + }); + it('should support multiple array literals passed through to one node', () => { let manyPropComp: ManyPropComp; @@ -92,7 +125,6 @@ describe('array literals', () => { function Template(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { element(0, 'many-prop-comp'); - reserveSlots(4); } if (rf & RenderFlags.Update) { elementProperty(0, 'names1', bind(pureFunction1(2, e0_ff, ctx.customName))); @@ -133,10 +165,9 @@ describe('array literals', () => { elementStart(0, 'my-comp'); myComps.push(loadDirective(0)); elementEnd(); - reserveSlots(2); } if (rf & RenderFlags.Update) { - elementProperty(0, 'names', bind(ctx.someFn(pureFunction1(2, e0_ff, ctx.customName)))); + elementProperty(0, 'names', bind(ctx.someFn(pureFunction1(1, e0_ff, ctx.customName)))); } }, directives: directives @@ -171,10 +202,9 @@ describe('array literals', () => { function Template(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { element(0, 'my-comp'); - reserveSlots(3); } if (rf & RenderFlags.Update) { - elementProperty(0, 'names', bind(pureFunction2(3, e0_ff, ctx.customName, ctx.customName2))); + elementProperty(0, 'names', bind(pureFunction2(1, e0_ff, ctx.customName, ctx.customName2))); } } @@ -243,19 +273,18 @@ describe('array literals', () => { elementStart(5, 'my-comp'); f8Comp = loadDirective(5); elementEnd(); - reserveSlots(39); } if (rf & RenderFlags.Update) { - elementProperty(0, 'names', bind(pureFunction3(4, e0_ff, c[5], c[6], c[7]))); - elementProperty(1, 'names', bind(pureFunction4(9, e2_ff, c[4], c[5], c[6], c[7]))); + elementProperty(0, 'names', bind(pureFunction3(6, e0_ff, c[5], c[6], c[7]))); + elementProperty(1, 'names', bind(pureFunction4(10, e2_ff, c[4], c[5], c[6], c[7]))); elementProperty(2, 'names', bind(pureFunction5(15, e4_ff, c[3], c[4], c[5], c[6], c[7]))); elementProperty( - 3, 'names', bind(pureFunction6(22, e6_ff, c[2], c[3], c[4], c[5], c[6], c[7]))); + 3, 'names', bind(pureFunction6(21, e6_ff, c[2], c[3], c[4], c[5], c[6], c[7]))); elementProperty( - 4, 'names', bind(pureFunction7(30, e8_ff, c[1], c[2], c[3], c[4], c[5], c[6], c[7]))); + 4, 'names', bind(pureFunction7(28, e8_ff, c[1], c[2], c[3], c[4], c[5], c[6], c[7]))); elementProperty( 5, 'names', - bind(pureFunction8(39, e10_ff, c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]))); + bind(pureFunction8(36, e10_ff, c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]))); } } @@ -298,12 +327,11 @@ describe('array literals', () => { function Template(rf: RenderFlags, c: any) { if (rf & RenderFlags.Create) { element(0, 'my-comp'); - reserveSlots(12); } if (rf & RenderFlags.Update) { elementProperty( - 0, 'names', bind(pureFunctionV(12, e0_ff, [ - c[0], c[1], c[2], c[3], pureFunction1(2, e0_ff_1, c[4]), c[5], c[6], c[7], c[8] + 0, 'names', bind(pureFunctionV(3, e0_ff, [ + c[0], c[1], c[2], c[3], pureFunction1(1, e0_ff_1, c[4]), c[5], c[6], c[7], c[8] ]))); } } @@ -349,10 +377,9 @@ describe('object literals', () => { function Template(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { element(0, 'object-comp'); - reserveSlots(2); } if (rf & RenderFlags.Update) { - elementProperty(0, 'config', bind(pureFunction1(2, e0_ff, ctx.name))); + elementProperty(0, 'config', bind(pureFunction1(1, e0_ff, ctx.name))); } } @@ -384,13 +411,12 @@ describe('object literals', () => { function Template(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { element(0, 'object-comp'); - reserveSlots(7); } if (rf & RenderFlags.Update) { elementProperty( 0, 'config', bind(pureFunction2( - 7, e0_ff, ctx.name, - pureFunction1(4, e0_ff_1, pureFunction1(2, e0_ff_2, ctx.duration))))); + 5, e0_ff, ctx.name, + pureFunction1(3, e0_ff_1, pureFunction1(1, e0_ff_2, ctx.duration))))); } } @@ -456,12 +482,11 @@ describe('object literals', () => { elementStart(0, 'object-comp'); objectComps.push(loadDirective(0)); elementEnd(); - reserveSlots(3); } if (rf1 & RenderFlags.Update) { elementProperty( 0, 'config', - bind(pureFunction2(3, e0_ff, ctx.configs[i].opacity, ctx.configs[i].duration))); + bind(pureFunction2(1, e0_ff, ctx.configs[i].opacity, ctx.configs[i].duration))); } embeddedViewEnd(); } diff --git a/packages/core/test/render3/view_container_ref_spec.ts b/packages/core/test/render3/view_container_ref_spec.ts index f9af7da7f7..6570f8be3d 100644 --- a/packages/core/test/render3/view_container_ref_spec.ts +++ b/packages/core/test/render3/view_container_ref_spec.ts @@ -9,7 +9,7 @@ import {Component, ComponentFactoryResolver, ElementRef, EmbeddedViewRef, NgModuleRef, Pipe, PipeTransform, RendererFactory2, TemplateRef, ViewContainerRef, createInjector, defineInjector, ɵAPP_ROOT as APP_ROOT, ɵNgModuleDef as NgModuleDef} from '../../src/core'; import {templateRefExtractor} from '../../src/render3/di'; import {AttributeMarker, NgOnChangesFeature, defineComponent, defineDirective, definePipe, injectComponentFactoryResolver, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index'; -import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation3, loadDirective, nextContext, projection, projectionDef, reference, reserveSlots, template, text, textBinding} from '../../src/render3/instructions'; +import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation3, loadDirective, nextContext, projection, projectionDef, reference, template, text, textBinding} from '../../src/render3/instructions'; import {RenderFlags} from '../../src/render3/interfaces/definition'; import {NgModuleFactory} from '../../src/render3/ng_module_ref'; import {pipe, pipeBind1} from '../../src/render3/pipe'; @@ -410,6 +410,16 @@ describe('ViewContainerRef', () => { }); } + function SomeComponent_Template_0(rf: RenderFlags, ctx: any) { + if (rf & RenderFlags.Create) { + element(0, 'child'); + pipe(1, 'starPipe'); + } + if (rf & RenderFlags.Update) { + elementProperty(0, 'name', bind(pipeBind1(1, 2, 'C'))); + } + } + @Component({ template: ` @@ -426,21 +436,11 @@ describe('ViewContainerRef', () => { factory: () => new SomeComponent(), template: (rf: RenderFlags, cmp: SomeComponent) => { if (rf & RenderFlags.Create) { - template(0, (rf: RenderFlags, ctx: any) => { - if (rf & RenderFlags.Create) { - element(0, 'child'); - pipe(1, 'starPipe'); - reserveSlots(2); - } - if (rf & RenderFlags.Update) { - elementProperty(0, 'name', bind(pipeBind1(1, 2, 'C'))); - } - }, null, [], ['foo', ''], templateRefExtractor); + template(0, SomeComponent_Template_0, null, [], ['foo', ''], templateRefExtractor); pipe(2, 'starPipe'); element(3, 'child', ['vcref', '']); pipe(4, 'starPipe'); element(5, 'child'); - reserveSlots(4); } if (rf & RenderFlags.Update) { const tplRef = reference(1);