wip: bypass pure functions in input computations

This commit is contained in:
Alex Rickabaugh
2023-06-07 13:29:09 -07:00
committed by Paul Gschwendtner
parent 49cbecaeab
commit d56cbc2fdf
2 changed files with 61 additions and 23 deletions
+41 -22
View File
@@ -7,11 +7,19 @@
*/
import {assertIndexInRange} from '../util/assert';
import {bindingUpdated, bindingUpdated2, bindingUpdated3, bindingUpdated4, getBinding, updateBinding} from './bindings';
import {LView} from './interfaces/view';
import {getBindingRoot, getLView} from './state';
import {NO_CHANGE} from './tokens';
let pureFunctionsEnabled = true;
export function setPureFunctionsEnabled(value: boolean): boolean {
const prev = pureFunctionsEnabled;
pureFunctionsEnabled = value;
return prev;
}
/**
* Bindings for pure functions are stored after regular bindings.
@@ -43,6 +51,8 @@ import {NO_CHANGE} from './tokens';
* @codeGenApi
*/
export function ɵɵpureFunction0<T>(slotOffset: number, pureFn: () => T, thisArg?: any): T {
if (!pureFunctionsEnabled) return thisArg ? pureFn.call(thisArg) : pureFn();
const bindingIndex = getBindingRoot() + slotOffset;
const lView = getLView();
return lView[bindingIndex] === NO_CHANGE ?
@@ -308,9 +318,11 @@ 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)) :
getPureFunctionReturnValue(lView, bindingIndex + 1);
if (pureFunctionsEnabled && !bindingUpdated(lView, bindingIndex, exp)) {
return getPureFunctionReturnValue(lView, bindingIndex + 1);
}
const value = thisArg ? pureFn.call(thisArg, exp) : pureFn(exp);
return pureFunctionsEnabled ? updateBinding(lView, bindingIndex + 1, value) : value;
}
@@ -331,11 +343,11 @@ 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)) :
getPureFunctionReturnValue(lView, bindingIndex + 2);
if (pureFunctionsEnabled && !bindingUpdated2(lView, bindingIndex, exp1, exp2)) {
return getPureFunctionReturnValue(lView, bindingIndex + 2);
}
const value = thisArg ? pureFn.call(thisArg, exp1, exp2) : pureFn(exp1, exp2);
return pureFunctionsEnabled ? updateBinding(lView, bindingIndex + 2, value) : value;
}
/**
@@ -357,11 +369,11 @@ export function pureFunction3Internal(
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)) :
getPureFunctionReturnValue(lView, bindingIndex + 3);
if (pureFunctionsEnabled && !bindingUpdated3(lView, bindingIndex, exp1, exp2, exp3)) {
return getPureFunctionReturnValue(lView, bindingIndex + 3);
}
const value = thisArg ? pureFn.call(thisArg, exp1, exp2, exp3) : pureFn(exp1, exp2, exp3);
return pureFunctionsEnabled ? updateBinding(lView, bindingIndex + 3, value) : value;
}
@@ -386,11 +398,12 @@ export function pureFunction4Internal(
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)) :
getPureFunctionReturnValue(lView, bindingIndex + 4);
if (pureFunctionsEnabled && !bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4)) {
return getPureFunctionReturnValue(lView, bindingIndex + 4);
}
const value =
thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4) : pureFn(exp1, exp2, exp3, exp4);
return pureFunctionsEnabled ? updateBinding(lView, bindingIndex + 4, value) : value;
}
/**
@@ -413,9 +426,15 @@ export function pureFunctionVInternal(
exps: any[], thisArg?: any): any {
let bindingIndex = bindingRoot + slotOffset;
let different = false;
for (let i = 0; i < exps.length; i++) {
bindingUpdated(lView, bindingIndex++, exps[i]) && (different = true);
if (pureFunctionsEnabled) {
for (let i = 0; i < exps.length; i++) {
bindingUpdated(lView, bindingIndex++, exps[i]) && (different = true);
}
if (!different) {
return getPureFunctionReturnValue(lView, bindingIndex);
}
}
return different ? updateBinding(lView, bindingIndex, pureFn.apply(thisArg, exps)) :
getPureFunctionReturnValue(lView, bindingIndex);
const value = pureFn.apply(thisArg, exps);
return pureFunctionsEnabled ? updateBinding(lView, bindingIndex, value) : value;
}
@@ -9,6 +9,8 @@
import {COMPUTED_NODE, ComputedNode, UNSET} from '@angular/core/primitives/signals';
import {setPureFunctionsEnabled} from '../pure_function';
import {Signal} from './api';
export const BRAND_WRITE_TYPE = Symbol();
@@ -67,7 +69,24 @@ export const INPUT_SIGNAL_NODE: InputSignalNode<unknown, unknown> = /* @__PURE__
// TODO: Make this a proper RuntimeError
throw new Error(`InputSignal not yet initialized`);
}
return this.transform(this._boundComputation?.() ?? this._boundValue);
if (this._boundComputation !== undefined) {
// TODO(signals): Do we need this?
// Disable pure function memoization when running computations of input signals.
// ---
// Bound computations are generated with instructions in place to memoize allocations like
// object literals, or for pipe transformations. Such operations do not need to be memoized
// in input computations as the `InputSignal` naturally memoizes the whole expression.
const prevPureFunctionsEnabled = setPureFunctionsEnabled(false);
try {
return this.transform(this._boundComputation());
} finally {
setPureFunctionsEnabled(prevPureFunctionsEnabled);
}
}
// Alternatively, if there no bound computation, we use the bound value.
return this.transform(this._boundValue);
},
bind: function<ReadT, WriteT>(