refactor(ivy): remove reserveSlots instruction (#25533)

PR Close #25533
This commit is contained in:
Kara Erickson 2018-08-15 16:32:08 -07:00 committed by Jason Aden
parent 21d22ce4ad
commit 4708cb91ef
8 changed files with 281 additions and 295 deletions

View File

@ -7,7 +7,7 @@
*/ */
import {assertEqual, assertLessThan} from './assert'; 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 {RENDER_PARENT} from './interfaces/container';
import {LContainerNode, LNode, TContainerNode, TElementNode, TNodeType} from './interfaces/node'; import {LContainerNode, LNode, TContainerNode, TElementNode, TNodeType} from './interfaces/node';
import {BINDING_INDEX, HEADER_OFFSET, TVIEW} from './interfaces/view'; 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. * @returns The concatenated string when any of the arguments changes, `NO_CHANGE` otherwise.
*/ */
export function i18nInterpolation1(instructions: I18nExpInstruction[], v0: any): string|NO_CHANGE { export function i18nInterpolation1(instructions: I18nExpInstruction[], v0: any): string|NO_CHANGE {
const different = bindingUpdated(v0); initBindings();
const different = bindingUpdated(_getViewData()[BINDING_INDEX]++, v0);
if (!different) { if (!different) {
return NO_CHANGE; return NO_CHANGE;
@ -414,7 +415,10 @@ export function i18nInterpolation1(instructions: I18nExpInstruction[], v0: any):
*/ */
export function i18nInterpolation2(instructions: I18nExpInstruction[], v0: any, v1: any): string| export function i18nInterpolation2(instructions: I18nExpInstruction[], v0: any, v1: any): string|
NO_CHANGE { 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) { if (!different) {
return NO_CHANGE; return NO_CHANGE;
@ -452,8 +456,10 @@ export function i18nInterpolation2(instructions: I18nExpInstruction[], v0: any,
*/ */
export function i18nInterpolation3( export function i18nInterpolation3(
instructions: I18nExpInstruction[], v0: any, v1: any, v2: any): string|NO_CHANGE { instructions: I18nExpInstruction[], v0: any, v1: any, v2: any): string|NO_CHANGE {
let different = bindingUpdated2(v0, v1); initBindings();
different = bindingUpdated(v2) || different; const viewData = _getViewData();
const different = bindingUpdated3(viewData[BINDING_INDEX], v0, v1, v2);
viewData[BINDING_INDEX] += 3;
if (!different) { if (!different) {
return NO_CHANGE; return NO_CHANGE;
@ -493,7 +499,10 @@ export function i18nInterpolation3(
*/ */
export function i18nInterpolation4( export function i18nInterpolation4(
instructions: I18nExpInstruction[], v0: any, v1: any, v2: any, v3: any): string|NO_CHANGE { 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) { if (!different) {
return NO_CHANGE; return NO_CHANGE;
@ -535,8 +544,11 @@ export function i18nInterpolation4(
export function i18nInterpolation5( export function i18nInterpolation5(
instructions: I18nExpInstruction[], v0: any, v1: any, v2: any, v3: any, v4: any): string| instructions: I18nExpInstruction[], v0: any, v1: any, v2: any, v3: any, v4: any): string|
NO_CHANGE { NO_CHANGE {
let different = bindingUpdated4(v0, v1, v2, v3); initBindings();
different = bindingUpdated(v4) || different; 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) { if (!different) {
return NO_CHANGE; return NO_CHANGE;
@ -580,8 +592,11 @@ export function i18nInterpolation5(
i18nInterpolation6( i18nInterpolation6(
instructions: I18nExpInstruction[], v0: any, v1: any, v2: any, v3: any, v4: any, v5: any): instructions: I18nExpInstruction[], v0: any, v1: any, v2: any, v3: any, v4: any, v5: any):
string|NO_CHANGE { string|NO_CHANGE {
let different = bindingUpdated4(v0, v1, v2, v3); initBindings();
different = bindingUpdated2(v4, v5) || different; 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) { if (!different) {
return NO_CHANGE; return NO_CHANGE;
@ -626,9 +641,11 @@ i18nInterpolation6(
export function i18nInterpolation7( export function i18nInterpolation7(
instructions: I18nExpInstruction[], v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, instructions: I18nExpInstruction[], v0: any, v1: any, v2: any, v3: any, v4: any, v5: any,
v6: any): string|NO_CHANGE { v6: any): string|NO_CHANGE {
let different = bindingUpdated4(v0, v1, v2, v3); initBindings();
different = bindingUpdated2(v4, v5) || different; const viewData = _getViewData();
different = bindingUpdated(v6) || different; 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) { if (!different) {
return NO_CHANGE; return NO_CHANGE;
@ -674,8 +691,11 @@ export function i18nInterpolation7(
export function i18nInterpolation8( export function i18nInterpolation8(
instructions: I18nExpInstruction[], v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, instructions: I18nExpInstruction[], v0: any, v1: any, v2: any, v3: any, v4: any, v5: any,
v6: any, v7: any): string|NO_CHANGE { v6: any, v7: any): string|NO_CHANGE {
let different = bindingUpdated4(v0, v1, v2, v3); initBindings();
different = bindingUpdated4(v4, v5, v6, v7) || different; 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) { if (!different) {
return NO_CHANGE; return NO_CHANGE;
@ -713,10 +733,12 @@ export function i18nInterpolation8(
*/ */
export function i18nInterpolationV(instructions: I18nExpInstruction[], values: any[]): string| export function i18nInterpolationV(instructions: I18nExpInstruction[], values: any[]): string|
NO_CHANGE { NO_CHANGE {
initBindings();
const viewData = _getViewData();
let different = false; let different = false;
for (let i = 0; i < values.length; i++) { for (let i = 0; i < values.length; i++) {
// Check if bindings have changed // Check if bindings have changed
bindingUpdated(values[i]) && (different = true); bindingUpdated(viewData[BINDING_INDEX]++, values[i]) && (different = true);
} }
if (!different) { if (!different) {

View File

@ -369,6 +369,7 @@ export function executeInitAndContentHooks(): void {
export function createLViewData<T>( export function createLViewData<T>(
renderer: Renderer3, tView: TView, context: T | null, flags: LViewFlags, renderer: Renderer3, tView: TView, context: T | null, flags: LViewFlags,
sanitizer?: Sanitizer | null): LViewData { sanitizer?: Sanitizer | null): LViewData {
// TODO(kara): create from blueprint
return [ return [
tView, // tView tView, // tView
viewData, // parent viewData, // parent
@ -2506,15 +2507,15 @@ export const NO_CHANGE = {} as NO_CHANGE;
* This function must be called before any binding related function is called * This function must be called before any binding related function is called
* (ie `bind()`, `interpolationX()`, `pureFunctionX()`) * (ie `bind()`, `interpolationX()`, `pureFunctionX()`)
*/ */
function initBindings() { export function initBindings() {
ngDevMode && assertEqual( // TODO(kara): remove this check when we have pre-filled array
viewData[BINDING_INDEX], -1,
'Binding index should not yet be set ' + viewData[BINDING_INDEX]);
if (tView.bindingStartIndex === -1) { if (tView.bindingStartIndex === -1) {
tView.bindingStartIndex = viewData.length; tView.bindingStartIndex = viewData.length;
} }
if (viewData[BINDING_INDEX] === -1) {
viewData[BINDING_INDEX] = tView.bindingStartIndex; viewData[BINDING_INDEX] = tView.bindingStartIndex;
} }
}
/** /**
* Creates a single value binding. * Creates a single value binding.
@ -2522,26 +2523,11 @@ function initBindings() {
* @param value Value to diff * @param value Value to diff
*/ */
export function bind<T>(value: T): T|NO_CHANGE { export function bind<T>(value: T): T|NO_CHANGE {
return bindingUpdated(value) ? value : NO_CHANGE; initBindings();
return bindingUpdated(viewData[BINDING_INDEX]++, value) ? value : NO_CHANGE;
} }
/** // TODO(kara): Remove this when updating the compiler (cannot remove without breaking JIT test)
* 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
*/
export function reserveSlots(numSlots: number) { export function reserveSlots(numSlots: number) {
// Init the slots with a unique `NO_CHANGE` value so that the first change is always detected // 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 // whether it happens or not during the first change detection pass - pure functions checks
@ -2553,29 +2539,6 @@ export function reserveSlots(numSlots: number) {
initBindings(); 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. * 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 { export function interpolationV(values: any[]): string|NO_CHANGE {
ngDevMode && assertLessThan(2, values.length, 'should have at least 3 values'); ngDevMode && assertLessThan(2, values.length, 'should have at least 3 values');
ngDevMode && assertEqual(values.length % 2, 1, 'should have an odd number of values'); ngDevMode && assertEqual(values.length % 2, 1, 'should have an odd number of values');
initBindings();
let different = false; let different = false;
for (let i = 1; i < values.length; i += 2) { for (let i = 1; i < values.length; i += 2) {
// Check if bindings (odd indexes) have changed // Check if bindings (odd indexes) have changed
bindingUpdated(values[i]) && (different = true); bindingUpdated(viewData[BINDING_INDEX]++, values[i]) && (different = true);
} }
if (!different) { if (!different) {
@ -2620,15 +2583,17 @@ export function interpolationV(values: any[]): string|NO_CHANGE {
* @param suffix static value used for concatenation only. * @param suffix static value used for concatenation only.
*/ */
export function interpolation1(prefix: string, v0: any, suffix: string): string|NO_CHANGE { 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; return different ? prefix + stringify(v0) + suffix : NO_CHANGE;
} }
/** Creates an interpolation binding with 2 expressions. */ /** Creates an interpolation binding with 2 expressions. */
export function interpolation2( export function interpolation2(
prefix: string, v0: any, i0: string, v1: any, suffix: string): string|NO_CHANGE { 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; return different ? prefix + stringify(v0) + i0 + stringify(v1) + suffix : NO_CHANGE;
} }
@ -2637,8 +2602,9 @@ export function interpolation2(
export function interpolation3( export function interpolation3(
prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, suffix: string): string| prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, suffix: string): string|
NO_CHANGE { NO_CHANGE {
let different = bindingUpdated2(v0, v1); initBindings();
different = bindingUpdated(v2) || different; 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 : return different ? prefix + stringify(v0) + i0 + stringify(v1) + i1 + stringify(v2) + suffix :
NO_CHANGE; NO_CHANGE;
@ -2648,7 +2614,9 @@ export function interpolation3(
export function interpolation4( export function interpolation4(
prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any,
suffix: string): string|NO_CHANGE { 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 ? return different ?
prefix + stringify(v0) + i0 + stringify(v1) + i1 + stringify(v2) + i2 + stringify(v3) + prefix + stringify(v0) + i0 + stringify(v1) + i1 + stringify(v2) + i2 + stringify(v3) +
@ -2660,8 +2628,10 @@ export function interpolation4(
export function interpolation5( export function interpolation5(
prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, 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 { i3: string, v4: any, suffix: string): string|NO_CHANGE {
let different = bindingUpdated4(v0, v1, v2, v3); initBindings();
different = bindingUpdated(v4) || different; let different = bindingUpdated4(viewData[BINDING_INDEX], v0, v1, v2, v3);
different = bindingUpdated(viewData[BINDING_INDEX] + 4, v4) || different;
viewData[BINDING_INDEX] += 5;
return different ? return different ?
prefix + stringify(v0) + i0 + stringify(v1) + i1 + stringify(v2) + i2 + stringify(v3) + i3 + prefix + stringify(v0) + i0 + stringify(v1) + i1 + stringify(v2) + i2 + stringify(v3) + i3 +
@ -2673,8 +2643,10 @@ export function interpolation5(
export function interpolation6( export function interpolation6(
prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any, 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 { i3: string, v4: any, i4: string, v5: any, suffix: string): string|NO_CHANGE {
let different = bindingUpdated4(v0, v1, v2, v3); initBindings();
different = bindingUpdated2(v4, v5) || different; 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 ? return different ?
prefix + stringify(v0) + i0 + stringify(v1) + i1 + stringify(v2) + i2 + stringify(v3) + i3 + 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, 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| i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, suffix: string): string|
NO_CHANGE { NO_CHANGE {
let different = bindingUpdated4(v0, v1, v2, v3); initBindings();
different = bindingUpdated2(v4, v5) || different; let different = bindingUpdated4(viewData[BINDING_INDEX], v0, v1, v2, v3);
different = bindingUpdated(v6) || different; different = bindingUpdated3(viewData[BINDING_INDEX] + 4, v4, v5, v6) || different;
viewData[BINDING_INDEX] += 7;
return different ? return different ?
prefix + stringify(v0) + i0 + stringify(v1) + i1 + stringify(v2) + i2 + stringify(v3) + i3 + 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, 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, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, i6: string, v7: any,
suffix: string): string|NO_CHANGE { suffix: string): string|NO_CHANGE {
let different = bindingUpdated4(v0, v1, v2, v3); initBindings();
different = bindingUpdated4(v4, v5, v6, v7) || different; 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 ? return different ?
prefix + stringify(v0) + i0 + stringify(v1) + i1 + stringify(v2) + i2 + stringify(v3) + i3 + 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); return loadElementInternal(index, viewData);
} }
/** Gets the current binding value and increments the binding index. */ /** Gets the current binding value. */
export function consumeBinding(): any { export function getBinding(bindingIndex: number): any {
ngDevMode && assertDataInRange(viewData[BINDING_INDEX]); ngDevMode && assertDataInRange(viewData[bindingIndex]);
ngDevMode && ngDevMode &&
assertNotEqual( assertNotEqual(viewData[bindingIndex], NO_CHANGE, 'Stored value should never be NO_CHANGE.');
viewData[viewData[BINDING_INDEX]], NO_CHANGE, 'Stored value should never be NO_CHANGE.'); return viewData[bindingIndex];
return viewData[viewData[BINDING_INDEX]++];
} }
/** Updates binding if changed, then returns whether it was updated. */ /** 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.'); 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) { if (bindingIndex >= viewData.length) {
viewData[viewData[BINDING_INDEX]++] = value; viewData[bindingIndex] = value;
} else if (isDifferent(viewData[bindingIndex], value, checkNoChangesMode)) { } else if (isDifferent(viewData[bindingIndex], value, checkNoChangesMode)) {
throwErrorIfNoChangesMode(creationMode, checkNoChangesMode, viewData[bindingIndex], value); throwErrorIfNoChangesMode(creationMode, checkNoChangesMode, viewData[bindingIndex], value);
viewData[viewData[BINDING_INDEX]++] = value; viewData[bindingIndex] = value;
} else { } else {
viewData[BINDING_INDEX]++;
return false; return false;
} }
return true; return true;
} }
/** Updates binding if changed, then returns the latest value. */ /** Updates binding and returns the value. */
export function checkAndUpdateBinding(value: any): any { export function updateBinding(bindingIndex: number, value: any): any {
bindingUpdated(value); return viewData[bindingIndex] = value;
return value;
} }
/** Updates 2 bindings if changed, then returns whether either was updated. */ /** Updates 2 bindings if changed, then returns whether either was updated. */
export function bindingUpdated2(exp1: any, exp2: any): boolean { export function bindingUpdated2(bindingIndex: number, exp1: any, exp2: any): boolean {
const different = bindingUpdated(exp1); const different = bindingUpdated(bindingIndex, exp1);
return bindingUpdated(exp2) || different; 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. */ /** Updates 4 bindings if changed, then returns whether any was updated. */
export function bindingUpdated4(exp1: any, exp2: any, exp3: any, exp4: any): boolean { export function bindingUpdated4(
const different = bindingUpdated2(exp1, exp2); bindingIndex: number, exp1: any, exp2: any, exp3: any, exp4: any): boolean {
return bindingUpdated2(exp3, exp4) || different; const different = bindingUpdated2(bindingIndex, exp1, exp2);
return bindingUpdated2(bindingIndex + 2, exp3, exp4) || different;
} }
export function getTView(): TView { 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})`); 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<T>(component: T): LElementNode { export function _getComponentHostLElementNode<T>(component: T): LElementNode {
ngDevMode && assertDefined(component, 'expecting component got null'); ngDevMode && assertDefined(component, 'expecting component got null');
const lElementNode = (component as any)[NG_HOST_SYMBOL] as LElementNode; const lElementNode = (component as any)[NG_HOST_SYMBOL] as LElementNode;

View File

@ -6,34 +6,44 @@
* found in the LICENSE file at https://angular.io/license * 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 * 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. * 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 pureFn Function that returns a value
* @param slotOffset the offset in the reserved slot space {@link reserveSlots}
* @param thisArg Optional calling context of pureFn * @param thisArg Optional calling context of pureFn
* @returns value * @returns value
*/ */
export function pureFunction0<T>(slotOffset: number, pureFn: () => T, thisArg?: any): T { export function pureFunction0<T>(slotOffset: number, pureFn: () => T, thisArg?: any): T {
ngDevMode && assertReservedSlotInitialized(slotOffset, 1); initBindings(); // TODO(kara): remove this check when we have pre-filled array
const index = moveBindingIndexToReservedSlot(slotOffset); const bindingIndex = getTView().bindingStartIndex + slotOffset;
const value = getCreationMode() ? return getCreationMode() ?
checkAndUpdateBinding(thisArg ? pureFn.call(thisArg) : pureFn()) : updateBinding(bindingIndex, thisArg ? pureFn.call(thisArg) : pureFn()) :
consumeBinding(); getBinding(bindingIndex);
restoreBindingIndex(index);
return value;
} }
/** /**
* If the value of the provided exp has changed, calls the pure function to return * 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. * 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 pureFn Function that returns an updated value
* @param exp Updated expression value * @param exp Updated expression value
* @param thisArg Optional calling context of pureFn * @param thisArg Optional calling context of pureFn
@ -41,20 +51,18 @@ export function pureFunction0<T>(slotOffset: number, pureFn: () => T, thisArg?:
*/ */
export function pureFunction1( export function pureFunction1(
slotOffset: number, pureFn: (v: any) => any, exp: any, thisArg?: any): any { slotOffset: number, pureFn: (v: any) => any, exp: any, thisArg?: any): any {
ngDevMode && assertReservedSlotInitialized(slotOffset, 2); initBindings(); // TODO(kara): remove this check when we have pre-filled array
const index = moveBindingIndexToReservedSlot(slotOffset); const bindingIndex = getTView().bindingStartIndex + slotOffset;
const value = bindingUpdated(exp) ? return bindingUpdated(bindingIndex, exp) ?
checkAndUpdateBinding(thisArg ? pureFn.call(thisArg, exp) : pureFn(exp)) : updateBinding(bindingIndex + 1, thisArg ? pureFn.call(thisArg, exp) : pureFn(exp)) :
consumeBinding(); getBinding(bindingIndex + 1);
restoreBindingIndex(index);
return value;
} }
/** /**
* If the value of any provided exp has changed, calls the pure function to return * 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. * 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 pureFn
* @param exp1 * @param exp1
* @param exp2 * @param exp2
@ -64,20 +72,19 @@ export function pureFunction1(
export function pureFunction2( export function pureFunction2(
slotOffset: number, pureFn: (v1: any, v2: any) => any, exp1: any, exp2: any, slotOffset: number, pureFn: (v1: any, v2: any) => any, exp1: any, exp2: any,
thisArg?: any): any { thisArg?: any): any {
ngDevMode && assertReservedSlotInitialized(slotOffset, 3); initBindings(); // TODO(kara): remove this check when we have pre-filled array
const index = moveBindingIndexToReservedSlot(slotOffset); const bindingIndex = getTView().bindingStartIndex + slotOffset;
const value = bindingUpdated2(exp1, exp2) ? return bindingUpdated2(bindingIndex, exp1, exp2) ?
checkAndUpdateBinding(thisArg ? pureFn.call(thisArg, exp1, exp2) : pureFn(exp1, exp2)) : updateBinding(
consumeBinding(); bindingIndex + 2, thisArg ? pureFn.call(thisArg, exp1, exp2) : pureFn(exp1, exp2)) :
restoreBindingIndex(index); getBinding(bindingIndex + 2);
return value;
} }
/** /**
* If the value of any provided exp has changed, calls the pure function to return * 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. * 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 pureFn
* @param exp1 * @param exp1
* @param exp2 * @param exp2
@ -88,22 +95,20 @@ export function pureFunction2(
export function pureFunction3( export function pureFunction3(
slotOffset: number, pureFn: (v1: any, v2: any, v3: any) => any, exp1: any, exp2: any, exp3: any, slotOffset: number, pureFn: (v1: any, v2: any, v3: any) => any, exp1: any, exp2: any, exp3: any,
thisArg?: any): any { thisArg?: any): any {
ngDevMode && assertReservedSlotInitialized(slotOffset, 4); initBindings(); // TODO(kara): remove this check when we have pre-filled array
const index = moveBindingIndexToReservedSlot(slotOffset); const bindingIndex = getTView().bindingStartIndex + slotOffset;
const different = bindingUpdated2(exp1, exp2); return bindingUpdated3(bindingIndex, exp1, exp2, exp3) ?
const value = bindingUpdated(exp3) || different ? updateBinding(
checkAndUpdateBinding( bindingIndex + 3,
thisArg ? pureFn.call(thisArg, exp1, exp2, exp3) : pureFn(exp1, exp2, exp3)) : thisArg ? pureFn.call(thisArg, exp1, exp2, exp3) : pureFn(exp1, exp2, exp3)) :
consumeBinding(); getBinding(bindingIndex + 3);
restoreBindingIndex(index);
return value;
} }
/** /**
* If the value of any provided exp has changed, calls the pure function to return * 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. * 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 pureFn
* @param exp1 * @param exp1
* @param exp2 * @param exp2
@ -115,21 +120,20 @@ export function pureFunction3(
export function pureFunction4( export function pureFunction4(
slotOffset: number, pureFn: (v1: any, v2: any, v3: any, v4: any) => any, exp1: any, exp2: any, slotOffset: number, pureFn: (v1: any, v2: any, v3: any, v4: any) => any, exp1: any, exp2: any,
exp3: any, exp4: any, thisArg?: any): any { exp3: any, exp4: any, thisArg?: any): any {
ngDevMode && assertReservedSlotInitialized(slotOffset, 5); initBindings(); // TODO(kara): remove this check when we have pre-filled array
const index = moveBindingIndexToReservedSlot(slotOffset); const bindingIndex = getTView().bindingStartIndex + slotOffset;
const value = bindingUpdated4(exp1, exp2, exp3, exp4) ? return bindingUpdated4(bindingIndex, exp1, exp2, exp3, exp4) ?
checkAndUpdateBinding( updateBinding(
bindingIndex + 4,
thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4) : pureFn(exp1, exp2, exp3, exp4)) : thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4) : pureFn(exp1, exp2, exp3, exp4)) :
consumeBinding(); getBinding(bindingIndex + 4);
restoreBindingIndex(index);
return value;
} }
/** /**
* If the value of any provided exp has changed, calls the pure function to return * 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. * 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 pureFn
* @param exp1 * @param exp1
* @param exp2 * @param exp2
@ -142,23 +146,21 @@ export function pureFunction4(
export function pureFunction5( export function pureFunction5(
slotOffset: number, pureFn: (v1: any, v2: any, v3: any, v4: any, v5: any) => any, exp1: any, 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 { exp2: any, exp3: any, exp4: any, exp5: any, thisArg?: any): any {
ngDevMode && assertReservedSlotInitialized(slotOffset, 6); initBindings(); // TODO(kara): remove this check when we have pre-filled array
const index = moveBindingIndexToReservedSlot(slotOffset); const bindingIndex = getTView().bindingStartIndex + slotOffset;
const different = bindingUpdated4(exp1, exp2, exp3, exp4); const different = bindingUpdated4(bindingIndex, exp1, exp2, exp3, exp4);
const value = bindingUpdated(exp5) || different ? return bindingUpdated(bindingIndex + 4, exp5) || different ?
checkAndUpdateBinding( updateBinding(
thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5) : bindingIndex + 5, thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5) :
pureFn(exp1, exp2, exp3, exp4, exp5)) : pureFn(exp1, exp2, exp3, exp4, exp5)) :
consumeBinding(); getBinding(bindingIndex + 5);
restoreBindingIndex(index);
return value;
} }
/** /**
* If the value of any provided exp has changed, calls the pure function to return * 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. * 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 pureFn
* @param exp1 * @param exp1
* @param exp2 * @param exp2
@ -172,23 +174,21 @@ export function pureFunction5(
export function pureFunction6( export function pureFunction6(
slotOffset: number, pureFn: (v1: any, v2: any, v3: any, v4: any, v5: any, v6: any) => any, 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 { exp1: any, exp2: any, exp3: any, exp4: any, exp5: any, exp6: any, thisArg?: any): any {
ngDevMode && assertReservedSlotInitialized(slotOffset, 7); initBindings(); // TODO(kara): remove this check when we have pre-filled array
const index = moveBindingIndexToReservedSlot(slotOffset); const bindingIndex = getTView().bindingStartIndex + slotOffset;
const different = bindingUpdated4(exp1, exp2, exp3, exp4); const different = bindingUpdated4(bindingIndex, exp1, exp2, exp3, exp4);
const value = bindingUpdated2(exp5, exp6) || different ? return bindingUpdated2(bindingIndex + 4, exp5, exp6) || different ?
checkAndUpdateBinding( updateBinding(
thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5, exp6) : bindingIndex + 6, thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5, exp6) :
pureFn(exp1, exp2, exp3, exp4, exp5, exp6)) : pureFn(exp1, exp2, exp3, exp4, exp5, exp6)) :
consumeBinding(); getBinding(bindingIndex + 6);
restoreBindingIndex(index);
return value;
} }
/** /**
* If the value of any provided exp has changed, calls the pure function to return * 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. * 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 pureFn
* @param exp1 * @param exp1
* @param exp2 * @param exp2
@ -204,24 +204,22 @@ export function pureFunction7(
slotOffset: number, slotOffset: number,
pureFn: (v1: any, v2: any, v3: any, v4: any, v5: any, v6: any, v7: any) => any, exp1: any, 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 { exp2: any, exp3: any, exp4: any, exp5: any, exp6: any, exp7: any, thisArg?: any): any {
ngDevMode && assertReservedSlotInitialized(slotOffset, 8); initBindings(); // TODO(kara): remove this check when we have pre-filled array
const index = moveBindingIndexToReservedSlot(slotOffset); const bindingIndex = getTView().bindingStartIndex + slotOffset;
let different = bindingUpdated4(exp1, exp2, exp3, exp4); let different = bindingUpdated4(bindingIndex, exp1, exp2, exp3, exp4);
different = bindingUpdated2(exp5, exp6) || different; return bindingUpdated3(bindingIndex + 4, exp5, exp6, exp7) || different ?
const value = bindingUpdated(exp7) || different ? updateBinding(
checkAndUpdateBinding( bindingIndex + 7, thisArg ?
thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5, exp6, exp7) : pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5, exp6, exp7) :
pureFn(exp1, exp2, exp3, exp4, exp5, exp6, exp7)) : pureFn(exp1, exp2, exp3, exp4, exp5, exp6, exp7)) :
consumeBinding(); getBinding(bindingIndex + 7);
restoreBindingIndex(index);
return value;
} }
/** /**
* If the value of any provided exp has changed, calls the pure function to return * 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. * 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 pureFn
* @param exp1 * @param exp1
* @param exp2 * @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, 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, exp1: any, exp2: any, exp3: any, exp4: any, exp5: any, exp6: any, exp7: any, exp8: any,
thisArg?: any): any { thisArg?: any): any {
ngDevMode && assertReservedSlotInitialized(slotOffset, 9); initBindings(); // TODO(kara): remove this check when we have pre-filled array
const index = moveBindingIndexToReservedSlot(slotOffset); const bindingIndex = getTView().bindingStartIndex + slotOffset;
const different = bindingUpdated4(exp1, exp2, exp3, exp4); const different = bindingUpdated4(bindingIndex, exp1, exp2, exp3, exp4);
const value = bindingUpdated4(exp5, exp6, exp7, exp8) || different ? return bindingUpdated4(bindingIndex + 4, exp5, exp6, exp7, exp8) || different ?
checkAndUpdateBinding( updateBinding(
thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5, exp6, exp7, exp8) : bindingIndex + 8, thisArg ?
pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5, exp6, exp7, exp8) :
pureFn(exp1, exp2, exp3, exp4, exp5, exp6, exp7, exp8)) : pureFn(exp1, exp2, exp3, exp4, exp5, exp6, exp7, exp8)) :
consumeBinding(); getBinding(bindingIndex + 8);
restoreBindingIndex(index);
return value;
} }
/** /**
@ -257,7 +254,7 @@ export function pureFunction8(
* If the value of any provided exp has changed, calls the pure function to return * 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. * 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 * @param pureFn A pure function that takes binding values and builds an object or array
* containing those values. * containing those values.
* @param exps An array of binding values * @param exps An array of binding values
@ -266,14 +263,12 @@ export function pureFunction8(
*/ */
export function pureFunctionV( export function pureFunctionV(
slotOffset: number, pureFn: (...v: any[]) => any, exps: any[], thisArg?: any): any { slotOffset: number, pureFn: (...v: any[]) => any, exps: any[], thisArg?: any): any {
ngDevMode && assertReservedSlotInitialized(slotOffset, exps.length + 1); initBindings(); // TODO(kara): remove this check when we have pre-filled array
const index = moveBindingIndexToReservedSlot(slotOffset); let bindingIndex = getTView().bindingStartIndex + slotOffset;
let different = false; let different = false;
for (let i = 0; i < exps.length; i++) { 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(); return different ? updateBinding(bindingIndex, pureFn.apply(thisArg, exps)) :
restoreBindingIndex(index); getBinding(bindingIndex);
return value;
} }

View File

@ -503,7 +503,6 @@ describe('components & directives', () => {
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) { template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {
if (rf & 1) { if (rf & 1) {
$r3$.ɵelement(0, 'my-array-comp'); $r3$.ɵelement(0, 'my-array-comp');
$r3$.ɵreserveSlots(1);
} }
if (rf & 2) { if (rf & 2) {
$r3$.ɵelementProperty( $r3$.ɵelementProperty(
@ -570,7 +569,6 @@ describe('components & directives', () => {
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) { template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {
if (rf & 1) { if (rf & 1) {
$r3$.ɵelement(0, 'my-comp'); $r3$.ɵelement(0, 'my-comp');
$r3$.ɵreserveSlots(1);
} }
if (rf & 2) { if (rf & 2) {
$r3$.ɵelementProperty( $r3$.ɵelementProperty(
@ -614,7 +612,6 @@ describe('components & directives', () => {
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) { template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {
if (rf & 1) { if (rf & 1) {
$r3$.ɵelement(0, 'my-array-comp'); $r3$.ɵelement(0, 'my-array-comp');
$r3$.ɵreserveSlots(2);
} }
if (rf & 2) { if (rf & 2) {
$r3$.ɵelementProperty( $r3$.ɵelementProperty(
@ -726,7 +723,6 @@ describe('components & directives', () => {
template: function MyApp_Template(rf: $RenderFlags$, c: $any$) { template: function MyApp_Template(rf: $RenderFlags$, c: $any$) {
if (rf & 1) { if (rf & 1) {
$r3$.ɵelement(0, 'my-comp'); $r3$.ɵelement(0, 'my-comp');
$r3$.ɵreserveSlots(10);
} }
if (rf & 2) { if (rf & 2) {
$r3$.ɵelementProperty( $r3$.ɵelementProperty(
@ -805,7 +801,6 @@ describe('components & directives', () => {
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) { template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {
if (rf & 1) { if (rf & 1) {
$r3$.ɵelement(0, 'object-comp'); $r3$.ɵelement(0, 'object-comp');
$r3$.ɵreserveSlots(2);
} }
if (rf & 2) { if (rf & 2) {
$r3$.ɵelementProperty( $r3$.ɵelementProperty(
@ -892,7 +887,6 @@ describe('components & directives', () => {
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) { template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {
if (rf & 1) { if (rf & 1) {
$r3$.ɵelement(0, 'nested-comp'); $r3$.ɵelement(0, 'nested-comp');
$r3$.ɵreserveSlots(7);
} }
if (rf & 2) { if (rf & 2) {
$r3$.ɵelementProperty( $r3$.ɵelementProperty(

View File

@ -88,7 +88,6 @@ describe('pipes', () => {
$r3$.ɵtext(0); $r3$.ɵtext(0);
$r3$.ɵpipe(1, 'myPipe'); $r3$.ɵpipe(1, 'myPipe');
$r3$.ɵpipe(2, 'myPurePipe'); $r3$.ɵpipe(2, 'myPurePipe');
$r3$.ɵreserveSlots(6);
} }
if (rf & 2) { if (rf & 2) {
$r3$.ɵtextBinding( $r3$.ɵtextBinding(
@ -150,6 +149,21 @@ describe('pipes', () => {
// /NORMATIVE // /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({ @Component({
template: `{{name | myPurePipe:size}}{{name | myPurePipe:size}} template: `{{name | myPurePipe:size}}{{name | myPurePipe:size}}
<div *oneTimeIf="more">{{name | myPurePipe:size}}</div>` <div *oneTimeIf="more">{{name | myPurePipe:size}}</div>`
@ -170,7 +184,7 @@ describe('pipes', () => {
$r3$.ɵpipe(1, 'myPurePipe'); $r3$.ɵpipe(1, 'myPurePipe');
$r3$.ɵtext(2); $r3$.ɵtext(2);
$r3$.ɵpipe(3, 'myPurePipe'); $r3$.ɵpipe(3, 'myPurePipe');
$r3$.ɵtemplate(4, C4, '', ['oneTimeIf', '']); $r3$.ɵtemplate(4, MyApp_div_Template_4, '', ['oneTimeIf', '']);
$r3$.ɵreserveSlots(6); $r3$.ɵreserveSlots(6);
} }
if (rf & 2) { if (rf & 2) {
@ -182,20 +196,6 @@ describe('pipes', () => {
$r3$.ɵcontainerRefreshStart(4); $r3$.ɵcontainerRefreshStart(4);
$r3$.ɵcontainerRefreshEnd(); $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 // /NORMATIVE

View File

@ -10,7 +10,7 @@ import {Directive, OnChanges, OnDestroy, Pipe, PipeTransform} from '@angular/cor
import {expect} from '@angular/platform-browser/testing/src/matchers'; import {expect} from '@angular/platform-browser/testing/src/matchers';
import {defineDirective, definePipe} from '../../src/render3/definition'; 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 {RenderFlags} from '../../src/render3/interfaces/definition';
import {pipe, pipeBind1, pipeBind3, pipeBind4, pipeBindV} from '../../src/render3/pipe'; import {pipe, pipeBind1, pipeBind3, pipeBind4, pipeBindV} from '../../src/render3/pipe';
@ -38,10 +38,9 @@ describe('pipe', () => {
if (rf & RenderFlags.Create) { if (rf & RenderFlags.Create) {
text(0); text(0);
pipe(1, 'countingPipe'); pipe(1, 'countingPipe');
reserveSlots(2);
} }
if (rf & RenderFlags.Update) { 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) { if (rf & RenderFlags.Create) {
text(0); text(0);
pipe(1, 'randomPipeName'); pipe(1, 'randomPipeName');
reserveSlots(2);
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
textBinding(0, interpolation1('', pipeBind1(1, 2, ctx.value), '')); textBinding(0, interpolation1('', pipeBind1(1, 1, ctx.value), ''));
} }
}, [], pipes); }, [], pipes);
@ -99,10 +97,9 @@ describe('pipe', () => {
elementStart(0, 'div', ['myDir', '']); elementStart(0, 'div', ['myDir', '']);
pipe(1, 'double'); pipe(1, 'double');
elementEnd(); elementEnd();
reserveSlots(2);
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
elementProperty(0, 'elprop', bind(pipeBind1(1, 2, ctx))); elementProperty(0, 'elprop', bind(pipeBind1(1, 1, ctx)));
directive = loadDirective(0); directive = loadDirective(0);
} }
} }
@ -115,11 +112,10 @@ describe('pipe', () => {
if (rf & RenderFlags.Create) { if (rf & RenderFlags.Create) {
text(0); text(0);
pipe(1, 'multiArgPipe'); pipe(1, 'multiArgPipe');
reserveSlots(4);
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
textBinding( 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); text(0);
pipe(1, 'multiArgPipe'); pipe(1, 'multiArgPipe');
pipe(2, 'multiArgPipe'); pipe(2, 'multiArgPipe');
reserveSlots(9);
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
textBinding( textBinding(
0, interpolation1( 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'); elementStart(0, 'div');
pipe(1, 'identityPipe'); pipe(1, 'identityPipe');
elementEnd(); elementEnd();
reserveSlots(2);
} }
if (rf & RenderFlags.Update) { 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) { if (rf & RenderFlags.Create) {
text(0); text(0);
pipe(1, 'countingPipe'); pipe(1, 'countingPipe');
reserveSlots(2);
} }
if (rf & RenderFlags.Update) { 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) { if (rf & RenderFlags.Create) {
text(0); text(0);
pipe(1, 'countingImpurePipe'); pipe(1, 'countingImpurePipe');
reserveSlots(2);
} }
if (rf & RenderFlags.Update) { 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'); pipe(3, 'countingImpurePipe');
elementEnd(); elementEnd();
container(4); container(4);
reserveSlots(4);
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
elementProperty(0, 'someProp', bind(pipeBind1(1, 2, true))); elementProperty(0, 'someProp', bind(pipeBind1(1, 2, true)));
@ -251,10 +242,9 @@ describe('pipe', () => {
elementStart(0, 'div'); elementStart(0, 'div');
pipe(1, 'countingImpurePipe'); pipe(1, 'countingImpurePipe');
elementEnd(); elementEnd();
reserveSlots(2);
} }
if (rf1 & RenderFlags.Update) { if (rf1 & RenderFlags.Update) {
elementProperty(0, 'someProp', bind(pipeBind1(1, 2, true))); elementProperty(0, 'someProp', bind(pipeBind1(1, 1, true)));
pipeInstances.push(load<CountingImpurePipe>(1)); pipeInstances.push(load<CountingImpurePipe>(1));
} }
} }
@ -306,10 +296,9 @@ describe('pipe', () => {
if (rf1 & RenderFlags.Create) { if (rf1 & RenderFlags.Create) {
text(0); text(0);
pipe(1, 'pipeWithOnDestroy'); pipe(1, 'pipeWithOnDestroy');
reserveSlots(2);
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
textBinding(0, interpolation1('', pipeBind1(1, 2, person.age), '')); textBinding(0, interpolation1('', pipeBind1(1, 1, person.age), ''));
} }
} }
embeddedViewEnd(); embeddedViewEnd();

View File

@ -5,11 +5,12 @@
* Use of this source code is governed by an MIT-style license that can be * 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 * found in the LICENSE file at https://angular.io/license
*/ */
import {defineComponent} from '../../src/render3/index'; import {AttributeMarker, defineComponent, template} from '../../src/render3/index';
import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, load, loadDirective, reserveSlots} from '../../src/render3/instructions'; 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 {RenderFlags} from '../../src/render3/interfaces/definition';
import {pureFunction1, pureFunction2, pureFunction3, pureFunction4, pureFunction5, pureFunction6, pureFunction7, pureFunction8, pureFunctionV} from '../../src/render3/pure_function'; 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', () => { describe('array literals', () => {
let myComp: MyComp; let myComp: MyComp;
@ -36,10 +37,9 @@ describe('array literals', () => {
function Template(rf: RenderFlags, ctx: any) { function Template(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) { if (rf & RenderFlags.Create) {
element(0, 'my-comp'); element(0, 'my-comp');
reserveSlots(2);
} }
if (rf & RenderFlags.Update) { 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']); 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)));
}
}
/**
* <my-comp *ngIf="showing" [names]="['Nancy', customName, 'Bess']"></my-comp>
*/
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', () => { it('should support multiple array literals passed through to one node', () => {
let manyPropComp: ManyPropComp; let manyPropComp: ManyPropComp;
@ -92,7 +125,6 @@ describe('array literals', () => {
function Template(rf: RenderFlags, ctx: any) { function Template(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) { if (rf & RenderFlags.Create) {
element(0, 'many-prop-comp'); element(0, 'many-prop-comp');
reserveSlots(4);
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
elementProperty(0, 'names1', bind(pureFunction1(2, e0_ff, ctx.customName))); elementProperty(0, 'names1', bind(pureFunction1(2, e0_ff, ctx.customName)));
@ -133,10 +165,9 @@ describe('array literals', () => {
elementStart(0, 'my-comp'); elementStart(0, 'my-comp');
myComps.push(loadDirective(0)); myComps.push(loadDirective(0));
elementEnd(); elementEnd();
reserveSlots(2);
} }
if (rf & RenderFlags.Update) { 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 directives: directives
@ -171,10 +202,9 @@ describe('array literals', () => {
function Template(rf: RenderFlags, ctx: any) { function Template(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) { if (rf & RenderFlags.Create) {
element(0, 'my-comp'); element(0, 'my-comp');
reserveSlots(3);
} }
if (rf & RenderFlags.Update) { 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'); elementStart(5, 'my-comp');
f8Comp = loadDirective(5); f8Comp = loadDirective(5);
elementEnd(); elementEnd();
reserveSlots(39);
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
elementProperty(0, 'names', bind(pureFunction3(4, e0_ff, 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(9, e2_ff, c[4], 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(2, 'names', bind(pureFunction5(15, e4_ff, c[3], c[4], c[5], c[6], c[7])));
elementProperty( 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( 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( elementProperty(
5, 'names', 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) { function Template(rf: RenderFlags, c: any) {
if (rf & RenderFlags.Create) { if (rf & RenderFlags.Create) {
element(0, 'my-comp'); element(0, 'my-comp');
reserveSlots(12);
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
elementProperty( elementProperty(
0, 'names', bind(pureFunctionV(12, e0_ff, [ 0, 'names', bind(pureFunctionV(3, 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] 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) { function Template(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) { if (rf & RenderFlags.Create) {
element(0, 'object-comp'); element(0, 'object-comp');
reserveSlots(2);
} }
if (rf & RenderFlags.Update) { 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) { function Template(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) { if (rf & RenderFlags.Create) {
element(0, 'object-comp'); element(0, 'object-comp');
reserveSlots(7);
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
elementProperty( elementProperty(
0, 'config', bind(pureFunction2( 0, 'config', bind(pureFunction2(
7, e0_ff, ctx.name, 5, e0_ff, ctx.name,
pureFunction1(4, e0_ff_1, pureFunction1(2, e0_ff_2, ctx.duration))))); pureFunction1(3, e0_ff_1, pureFunction1(1, e0_ff_2, ctx.duration)))));
} }
} }
@ -456,12 +482,11 @@ describe('object literals', () => {
elementStart(0, 'object-comp'); elementStart(0, 'object-comp');
objectComps.push(loadDirective(0)); objectComps.push(loadDirective(0));
elementEnd(); elementEnd();
reserveSlots(3);
} }
if (rf1 & RenderFlags.Update) { if (rf1 & RenderFlags.Update) {
elementProperty( elementProperty(
0, 'config', 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(); embeddedViewEnd();
} }

View File

@ -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 {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 {templateRefExtractor} from '../../src/render3/di';
import {AttributeMarker, NgOnChangesFeature, defineComponent, defineDirective, definePipe, injectComponentFactoryResolver, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index'; 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 {RenderFlags} from '../../src/render3/interfaces/definition';
import {NgModuleFactory} from '../../src/render3/ng_module_ref'; import {NgModuleFactory} from '../../src/render3/ng_module_ref';
import {pipe, pipeBind1} from '../../src/render3/pipe'; 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({ @Component({
template: ` template: `
<ng-template #foo> <ng-template #foo>
@ -426,21 +436,11 @@ describe('ViewContainerRef', () => {
factory: () => new SomeComponent(), factory: () => new SomeComponent(),
template: (rf: RenderFlags, cmp: SomeComponent) => { template: (rf: RenderFlags, cmp: SomeComponent) => {
if (rf & RenderFlags.Create) { if (rf & RenderFlags.Create) {
template(0, (rf: RenderFlags, ctx: any) => { template(0, SomeComponent_Template_0, null, [], ['foo', ''], templateRefExtractor);
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);
pipe(2, 'starPipe'); pipe(2, 'starPipe');
element(3, 'child', ['vcref', '']); element(3, 'child', ['vcref', '']);
pipe(4, 'starPipe'); pipe(4, 'starPipe');
element(5, 'child'); element(5, 'child');
reserveSlots(4);
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
const tplRef = reference(1); const tplRef = reference(1);