refactor(ivy): remove calls into other instructions from pipe instructions (#33714)

A while ago we made a pass through all instructions to make sure that none of them call directly into other instructions, however it seems like missed the `pipeBind*` since they still call into the pure functions. The result is that we have some unnecessary duplicated accesses of global state like `getLView` which are called twice in a row with nothing changing.

These changes move the common functionality into a shared file and make the pipe instructions call into them with the global state instead.

PR Close #33714
This commit is contained in:
crisbeto 2019-12-19 21:54:35 +01:00 committed by atscott
parent 794dcb58d3
commit b1eb84c57e
2 changed files with 148 additions and 49 deletions

View File

@ -13,8 +13,8 @@ import {getFactoryDef} from './definition';
import {store} from './instructions/all'; import {store} from './instructions/all';
import {PipeDef, PipeDefList} from './interfaces/definition'; import {PipeDef, PipeDefList} from './interfaces/definition';
import {HEADER_OFFSET, LView, TVIEW} from './interfaces/view'; import {HEADER_OFFSET, LView, TVIEW} from './interfaces/view';
import {ɵɵpureFunction1, ɵɵpureFunction2, ɵɵpureFunction3, ɵɵpureFunction4, ɵɵpureFunctionV} from './pure_function'; import {pureFunction1Internal, pureFunction2Internal, pureFunction3Internal, pureFunction4Internal, pureFunctionVInternal} from './pure_function';
import {getBindingIndex, getLView} from './state'; import {getBindingIndex, getBindingRoot, getLView} from './state';
import {NO_CHANGE} from './tokens'; import {NO_CHANGE} from './tokens';
import {load} from './util/view_utils'; import {load} from './util/view_utils';
@ -89,7 +89,8 @@ export function ɵɵpipeBind1(index: number, slotOffset: number, v1: any): any {
const pipeInstance = load<PipeTransform>(lView, index); const pipeInstance = load<PipeTransform>(lView, index);
return unwrapValue( return unwrapValue(
lView, isPure(lView, index) ? lView, isPure(lView, index) ?
ɵɵpureFunction1(slotOffset, pipeInstance.transform, v1, pipeInstance) : pureFunction1Internal(
lView, getBindingRoot(), slotOffset, pipeInstance.transform, v1, pipeInstance) :
pipeInstance.transform(v1)); pipeInstance.transform(v1));
} }
@ -111,7 +112,8 @@ export function ɵɵpipeBind2(index: number, slotOffset: number, v1: any, v2: an
const pipeInstance = load<PipeTransform>(lView, index); const pipeInstance = load<PipeTransform>(lView, index);
return unwrapValue( return unwrapValue(
lView, isPure(lView, index) ? lView, isPure(lView, index) ?
ɵɵpureFunction2(slotOffset, pipeInstance.transform, v1, v2, pipeInstance) : pureFunction2Internal(
lView, getBindingRoot(), slotOffset, pipeInstance.transform, v1, v2, pipeInstance) :
pipeInstance.transform(v1, v2)); pipeInstance.transform(v1, v2));
} }
@ -133,8 +135,9 @@ export function ɵɵpipeBind3(index: number, slotOffset: number, v1: any, v2: an
const lView = getLView(); const lView = getLView();
const pipeInstance = load<PipeTransform>(lView, index); const pipeInstance = load<PipeTransform>(lView, index);
return unwrapValue( return unwrapValue(
lView, isPure(lView, index) ? lView, isPure(lView, index) ? pureFunction3Internal(
ɵɵpureFunction3(slotOffset, pipeInstance.transform, v1, v2, v3, pipeInstance) : lView, getBindingRoot(), slotOffset, pipeInstance.transform,
v1, v2, v3, pipeInstance) :
pipeInstance.transform(v1, v2, v3)); pipeInstance.transform(v1, v2, v3));
} }
@ -158,8 +161,9 @@ export function ɵɵpipeBind4(
const lView = getLView(); const lView = getLView();
const pipeInstance = load<PipeTransform>(lView, index); const pipeInstance = load<PipeTransform>(lView, index);
return unwrapValue( return unwrapValue(
lView, isPure(lView, index) ? lView, isPure(lView, index) ? pureFunction4Internal(
ɵɵpureFunction4(slotOffset, pipeInstance.transform, v1, v2, v3, v4, pipeInstance) : lView, getBindingRoot(), slotOffset, pipeInstance.transform,
v1, v2, v3, v4, pipeInstance) :
pipeInstance.transform(v1, v2, v3, v4)); pipeInstance.transform(v1, v2, v3, v4));
} }
@ -180,7 +184,8 @@ export function ɵɵpipeBindV(index: number, slotOffset: number, values: [any, .
const pipeInstance = load<PipeTransform>(lView, index); const pipeInstance = load<PipeTransform>(lView, index);
return unwrapValue( return unwrapValue(
lView, isPure(lView, index) ? lView, isPure(lView, index) ?
ɵɵpureFunctionV(slotOffset, pipeInstance.transform, values, pipeInstance) : pureFunctionVInternal(
lView, getBindingRoot(), slotOffset, pipeInstance.transform, values, pipeInstance) :
pipeInstance.transform.apply(pipeInstance, values)); pipeInstance.transform.apply(pipeInstance, values));
} }

View File

@ -7,6 +7,7 @@
*/ */
import {bindingUpdated, bindingUpdated2, bindingUpdated3, bindingUpdated4, getBinding, updateBinding} from './bindings'; import {bindingUpdated, bindingUpdated2, bindingUpdated3, bindingUpdated4, getBinding, updateBinding} from './bindings';
import {LView} from './interfaces/view';
import {getBindingRoot, getLView} from './state'; import {getBindingRoot, getLView} from './state';
import {NO_CHANGE} from './tokens'; import {NO_CHANGE} from './tokens';
@ -41,7 +42,6 @@ import {NO_CHANGE} from './tokens';
* @codeGenApi * @codeGenApi
*/ */
export function ɵɵpureFunction0<T>(slotOffset: number, pureFn: () => T, thisArg?: any): T { export function ɵɵpureFunction0<T>(slotOffset: number, pureFn: () => T, thisArg?: any): T {
// TODO(kara): use bindingRoot instead of bindingStartIndex when implementing host bindings
const bindingIndex = getBindingRoot() + slotOffset; const bindingIndex = getBindingRoot() + slotOffset;
const lView = getLView(); const lView = getLView();
return lView[bindingIndex] === NO_CHANGE ? return lView[bindingIndex] === NO_CHANGE ?
@ -63,12 +63,7 @@ export function ɵɵpureFunction0<T>(slotOffset: number, pureFn: () => T, thisAr
*/ */
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 {
// TODO(kara): use bindingRoot instead of bindingStartIndex when implementing host bindings return pureFunction1Internal(getLView(), getBindingRoot(), slotOffset, pureFn, exp, thisArg);
const lView = getLView();
const bindingIndex = getBindingRoot() + slotOffset;
return bindingUpdated(lView, bindingIndex, exp) ?
updateBinding(lView, bindingIndex + 1, thisArg ? pureFn.call(thisArg, exp) : pureFn(exp)) :
getBinding(lView, bindingIndex + 1);
} }
/** /**
@ -87,14 +82,8 @@ 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 {
// TODO(kara): use bindingRoot instead of bindingStartIndex when implementing host bindings return pureFunction2Internal(
const bindingIndex = getBindingRoot() + slotOffset; getLView(), getBindingRoot(), slotOffset, pureFn, exp1, exp2, thisArg);
const lView = getLView();
return bindingUpdated2(lView, bindingIndex, exp1, exp2) ?
updateBinding(
lView, bindingIndex + 2,
thisArg ? pureFn.call(thisArg, exp1, exp2) : pureFn(exp1, exp2)) :
getBinding(lView, bindingIndex + 2);
} }
/** /**
@ -114,14 +103,8 @@ 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 {
// TODO(kara): use bindingRoot instead of bindingStartIndex when implementing host bindings return pureFunction3Internal(
const bindingIndex = getBindingRoot() + slotOffset; getLView(), getBindingRoot(), slotOffset, pureFn, exp1, exp2, exp3, thisArg);
const lView = getLView();
return bindingUpdated3(lView, bindingIndex, exp1, exp2, exp3) ?
updateBinding(
lView, bindingIndex + 3,
thisArg ? pureFn.call(thisArg, exp1, exp2, exp3) : pureFn(exp1, exp2, exp3)) :
getBinding(lView, bindingIndex + 3);
} }
/** /**
@ -142,14 +125,8 @@ 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 {
// TODO(kara): use bindingRoot instead of bindingStartIndex when implementing host bindings return pureFunction4Internal(
const bindingIndex = getBindingRoot() + slotOffset; getLView(), getBindingRoot(), slotOffset, pureFn, exp1, exp2, exp3, exp4, thisArg);
const lView = getLView();
return bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4) ?
updateBinding(
lView, bindingIndex + 4,
thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4) : pureFn(exp1, exp2, exp3, exp4)) :
getBinding(lView, bindingIndex + 4);
} }
/** /**
@ -171,7 +148,6 @@ 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 {
// TODO(kara): use bindingRoot instead of bindingStartIndex when implementing host bindings
const bindingIndex = getBindingRoot() + slotOffset; const bindingIndex = getBindingRoot() + slotOffset;
const lView = getLView(); const lView = getLView();
const different = bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4); const different = bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4);
@ -202,7 +178,6 @@ 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 {
// TODO(kara): use bindingRoot instead of bindingStartIndex when implementing host bindings
const bindingIndex = getBindingRoot() + slotOffset; const bindingIndex = getBindingRoot() + slotOffset;
const lView = getLView(); const lView = getLView();
const different = bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4); const different = bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4);
@ -236,7 +211,6 @@ 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 {
// TODO(kara): use bindingRoot instead of bindingStartIndex when implementing host bindings
const bindingIndex = getBindingRoot() + slotOffset; const bindingIndex = getBindingRoot() + slotOffset;
const lView = getLView(); const lView = getLView();
let different = bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4); let different = bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4);
@ -272,7 +246,6 @@ 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 {
// TODO(kara): use bindingRoot instead of bindingStartIndex when implementing host bindings
const bindingIndex = getBindingRoot() + slotOffset; const bindingIndex = getBindingRoot() + slotOffset;
const lView = getLView(); const lView = getLView();
const different = bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4); const different = bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4);
@ -301,10 +274,131 @@ 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 {
// TODO(kara): use bindingRoot instead of bindingStartIndex when implementing host bindings return pureFunctionVInternal(getLView(), getBindingRoot(), slotOffset, pureFn, exps, thisArg);
let bindingIndex = getBindingRoot() + slotOffset; }
/**
* 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 lView LView in which the function is being executed.
* @param bindingRoot Binding root index.
* @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
* @returns Updated or cached value
*/
export function pureFunction1Internal(
lView: LView, bindingRoot: number, slotOffset: number, pureFn: (v: any) => any, exp: any,
thisArg?: any): any {
const bindingIndex = bindingRoot + slotOffset;
return bindingUpdated(lView, bindingIndex, exp) ?
updateBinding(lView, bindingIndex + 1, thisArg ? pureFn.call(thisArg, exp) : pureFn(exp)) :
getBinding(lView, 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 lView LView in which the function is being executed.
* @param bindingRoot Binding root index.
* @param slotOffset the offset from binding root to the reserved slot
* @param pureFn
* @param exp1
* @param exp2
* @param thisArg Optional calling context of pureFn
* @returns Updated or cached value
*/
export function pureFunction2Internal(
lView: LView, bindingRoot: number, slotOffset: number, pureFn: (v1: any, v2: any) => any,
exp1: any, exp2: any, thisArg?: any): any {
const bindingIndex = bindingRoot + slotOffset;
return bindingUpdated2(lView, bindingIndex, exp1, exp2) ?
updateBinding(
lView, bindingIndex + 2,
thisArg ? pureFn.call(thisArg, exp1, exp2) : pureFn(exp1, exp2)) :
getBinding(lView, 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 lView LView in which the function is being executed.
* @param bindingRoot Binding root index.
* @param slotOffset the offset from binding root to the reserved slot
* @param pureFn
* @param exp1
* @param exp2
* @param exp3
* @param thisArg Optional calling context of pureFn
* @returns Updated or cached value
*/
export function pureFunction3Internal(
lView: LView, bindingRoot: number, slotOffset: number,
pureFn: (v1: any, v2: any, v3: any) => any, exp1: any, exp2: any, exp3: any,
thisArg?: any): any {
const bindingIndex = bindingRoot + slotOffset;
return bindingUpdated3(lView, bindingIndex, exp1, exp2, exp3) ?
updateBinding(
lView, bindingIndex + 3,
thisArg ? pureFn.call(thisArg, exp1, exp2, exp3) : pureFn(exp1, exp2, exp3)) :
getBinding(lView, 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 lView LView in which the function is being executed.
* @param bindingRoot Binding root index.
* @param slotOffset the offset from binding root to the reserved slot
* @param pureFn
* @param exp1
* @param exp2
* @param exp3
* @param exp4
* @param thisArg Optional calling context of pureFn
* @returns Updated or cached value
*
*/
export function pureFunction4Internal(
lView: LView, bindingRoot: number, slotOffset: number,
pureFn: (v1: any, v2: any, v3: any, v4: any) => any, exp1: any, exp2: any, exp3: any, exp4: any,
thisArg?: any): any {
const bindingIndex = bindingRoot + slotOffset;
return bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4) ?
updateBinding(
lView, bindingIndex + 4,
thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4) : pureFn(exp1, exp2, exp3, exp4)) :
getBinding(lView, bindingIndex + 4);
}
/**
* pureFunction instruction that can support any number of bindings.
*
* 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 lView LView in which the function is being executed.
* @param bindingRoot Binding root index.
* @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
* @param thisArg Optional calling context of pureFn
* @returns Updated or cached value
*/
export function pureFunctionVInternal(
lView: LView, bindingRoot: number, slotOffset: number, pureFn: (...v: any[]) => any,
exps: any[], thisArg?: any): any {
let bindingIndex = bindingRoot + slotOffset;
let different = false; let different = false;
const lView = getLView();
for (let i = 0; i < exps.length; i++) { for (let i = 0; i < exps.length; i++) {
bindingUpdated(lView, bindingIndex++, exps[i]) && (different = true); bindingUpdated(lView, bindingIndex++, exps[i]) && (different = true);
} }