feat(ivy): add support for short-circuiting (#24039)
Short-circuitable expressions (using ternary & binary operators) could not use the regular binding mechanism as it relies on the bindings being checked every single time - the index is incremented as part of checking the bindings. Then for pure function kind of bindings we use a different mechanism with a fixed index. As such short circuiting a binding check does not mess with the expected binding index. Note that all pure function bindings are handled the same wether or not they actually are short-circuitable. This allows to keep the compiler and compiled code simple - and there is no runtime perf cost anyway. PR Close #24039
This commit is contained in:
parent
83bb5d1922
commit
4f36340de7
|
@ -121,4 +121,7 @@ export class Identifiers {
|
|||
static NgOnChangesFeature: o.ExternalReference = {name: 'ɵNgOnChangesFeature', moduleName: CORE};
|
||||
|
||||
static listener: o.ExternalReference = {name: 'ɵL', moduleName: CORE};
|
||||
|
||||
// Reserve slots for pure functions
|
||||
static reserveSlots: o.ExternalReference = {name: 'ɵrS', moduleName: CORE};
|
||||
}
|
||||
|
|
|
@ -57,6 +57,9 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
// Maps of placeholder to node indexes for each of the i18n section
|
||||
private _phToNodeIdxes: {[phName: string]: number[]}[] = [{}];
|
||||
|
||||
// Number of slots to reserve for pureFunctions
|
||||
private _pureFunctionSlots = 0;
|
||||
|
||||
constructor(
|
||||
private constantPool: ConstantPool, private contextParameter: string,
|
||||
parentBindingScope: BindingScope, private level = 0, private contextName: string|null,
|
||||
|
@ -70,6 +73,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
});
|
||||
this._valueConverter = new ValueConverter(
|
||||
constantPool, () => this.allocateDataSlot(),
|
||||
(numSlots: number): number => this._pureFunctionSlots += numSlots,
|
||||
(name, localName, slot, value: o.ReadVarExpr) => {
|
||||
const pipeType = pipeTypeByName.get(name);
|
||||
if (pipeType) {
|
||||
|
@ -139,6 +143,11 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
|
||||
t.visitAll(this, nodes);
|
||||
|
||||
if (this._pureFunctionSlots > 0) {
|
||||
this.instruction(
|
||||
this._creationCode, null, R3.reserveSlots, o.literal(this._pureFunctionSlots));
|
||||
}
|
||||
|
||||
const creationCode = this._creationCode.length > 0 ?
|
||||
[o.ifStmt(
|
||||
o.variable(RENDER_FLAGS).bitwiseAnd(o.literal(core.RenderFlags.Create), null, false),
|
||||
|
@ -501,6 +510,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
class ValueConverter extends AstMemoryEfficientTransformer {
|
||||
constructor(
|
||||
private constantPool: ConstantPool, private allocateSlot: () => number,
|
||||
private allocatePureFunctionSlots: (numSlots: number) => number,
|
||||
private definePipe:
|
||||
(name: string, localName: string, slot: number, value: o.Expression) => void) {
|
||||
super();
|
||||
|
@ -511,14 +521,20 @@ class ValueConverter extends AstMemoryEfficientTransformer {
|
|||
// Allocate a slot to create the pipe
|
||||
const slot = this.allocateSlot();
|
||||
const slotPseudoLocal = `PIPE:${slot}`;
|
||||
// Allocate one slot for the result plus one slot per pipe argument
|
||||
const pureFunctionSlot = this.allocatePureFunctionSlots(2 + pipe.args.length);
|
||||
const target = new PropertyRead(pipe.span, new ImplicitReceiver(pipe.span), slotPseudoLocal);
|
||||
const bindingId = pipeBinding(pipe.args);
|
||||
this.definePipe(pipe.name, slotPseudoLocal, slot, o.importExpr(bindingId));
|
||||
const value = pipe.exp.visit(this);
|
||||
const args = this.visitAll(pipe.args);
|
||||
|
||||
return new FunctionCall(
|
||||
pipe.span, target, [new LiteralPrimitive(pipe.span, slot), value, ...args]);
|
||||
return new FunctionCall(pipe.span, target, [
|
||||
new LiteralPrimitive(pipe.span, slot),
|
||||
new LiteralPrimitive(pipe.span, pureFunctionSlot),
|
||||
value,
|
||||
...args,
|
||||
]);
|
||||
}
|
||||
|
||||
visitLiteralArray(array: LiteralArray, context: any): AST {
|
||||
|
@ -527,8 +543,9 @@ class ValueConverter extends AstMemoryEfficientTransformer {
|
|||
// calls to literal factories that compose the literal and will cache intermediate
|
||||
// values. Otherwise, just return an literal array that contains the values.
|
||||
const literal = o.literalArr(values);
|
||||
return values.every(a => a.isConstant()) ? this.constantPool.getConstLiteral(literal, true) :
|
||||
getLiteralFactory(this.constantPool, literal);
|
||||
return values.every(a => a.isConstant()) ?
|
||||
this.constantPool.getConstLiteral(literal, true) :
|
||||
getLiteralFactory(this.constantPool, literal, this.allocatePureFunctionSlots);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -539,14 +556,13 @@ class ValueConverter extends AstMemoryEfficientTransformer {
|
|||
// values. Otherwise, just return an literal array that contains the values.
|
||||
const literal = o.literalMap(values.map(
|
||||
(value, index) => ({key: map.keys[index].key, value, quoted: map.keys[index].quoted})));
|
||||
return values.every(a => a.isConstant()) ? this.constantPool.getConstLiteral(literal, true) :
|
||||
getLiteralFactory(this.constantPool, literal);
|
||||
return values.every(a => a.isConstant()) ?
|
||||
this.constantPool.getConstLiteral(literal, true) :
|
||||
getLiteralFactory(this.constantPool, literal, this.allocatePureFunctionSlots);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Pipes always have at least one parameter, the value they operate on
|
||||
const pipeBindingIdentifiers = [R3.pipeBind1, R3.pipeBind2, R3.pipeBind3, R3.pipeBind4];
|
||||
|
||||
|
@ -559,15 +575,20 @@ const pureFunctionIdentifiers = [
|
|||
R3.pureFunction5, R3.pureFunction6, R3.pureFunction7, R3.pureFunction8
|
||||
];
|
||||
function getLiteralFactory(
|
||||
constantPool: ConstantPool, literal: o.LiteralArrayExpr | o.LiteralMapExpr): o.Expression {
|
||||
constantPool: ConstantPool, literal: o.LiteralArrayExpr | o.LiteralMapExpr,
|
||||
allocateSlots: (numSlots: number) => number): o.Expression {
|
||||
const {literalFactory, literalFactoryArguments} = constantPool.getLiteralFactory(literal);
|
||||
// Allocate 1 slot for the result plus 1 per argument
|
||||
const startSlot = allocateSlots(1 + literalFactoryArguments.length);
|
||||
literalFactoryArguments.length > 0 || error(`Expected arguments to a literal factory function`);
|
||||
let pureFunctionIdent =
|
||||
pureFunctionIdentifiers[literalFactoryArguments.length] || R3.pureFunctionV;
|
||||
|
||||
// Literal factories are pure functions that only need to be re-invoked when the parameters
|
||||
// change.
|
||||
return o.importExpr(pureFunctionIdent).callFn([literalFactory, ...literalFactoryArguments]);
|
||||
return o.importExpr(pureFunctionIdent).callFn([
|
||||
o.literal(startSlot), literalFactory, ...literalFactoryArguments
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -107,6 +107,57 @@ describe('compiler compliance', () => {
|
|||
expectEmit(result.source, template, 'Incorrect template');
|
||||
});
|
||||
|
||||
it('should reserve slots for pure functions', () => {
|
||||
const files = {
|
||||
app: {
|
||||
'spec.ts': `
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
template: \`<div
|
||||
[ternary]="cond ? [a] : [0]"
|
||||
[pipe]="value | pipe:1:2"
|
||||
[and]="cond && [b]"
|
||||
[or]="cond || [c]"
|
||||
></div>\`
|
||||
})
|
||||
export class MyComponent {
|
||||
id = 'one';
|
||||
}
|
||||
|
||||
@NgModule({declarations: [MyComponent]})
|
||||
export class MyModule {}
|
||||
`
|
||||
}
|
||||
};
|
||||
|
||||
const factory = 'factory: function MyComponent_Factory() { return new MyComponent(); }';
|
||||
const template = `
|
||||
…
|
||||
template: function MyComponent_Template(rf: IDENT, ctx: IDENT) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵE(0, 'div');
|
||||
$r3$.ɵPp(1,'pipe');
|
||||
$r3$.ɵe();
|
||||
$r3$.ɵrS(10);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵp(0, 'ternary', $r3$.ɵb((ctx.cond ? $r3$.ɵf1(2, _c0, ctx.a): _c1)));
|
||||
$r3$.ɵp(0, 'pipe', $r3$.ɵb($r3$.ɵpb3(6, 1, ctx.value, 1, 2)));
|
||||
$r3$.ɵp(0, 'and', $r3$.ɵb((ctx.cond && $r3$.ɵf1(4, _c0, ctx.b))));
|
||||
$r3$.ɵp(0, 'or', $r3$.ɵb((ctx.cond || $r3$.ɵf1(6, _c0, ctx.c))));
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
const result = compile(files, angularFiles);
|
||||
|
||||
expectEmit(result.source, factory, 'Incorrect factory');
|
||||
expectEmit(result.source, template, 'Incorrect template');
|
||||
});
|
||||
|
||||
it('should bind to class and style names', () => {
|
||||
const files = {
|
||||
app: {
|
||||
|
@ -415,9 +466,10 @@ describe('compiler compliance', () => {
|
|||
if (rf & 1) {
|
||||
$r3$.ɵE(0, 'my-comp');
|
||||
$r3$.ɵe();
|
||||
$r3$.ɵrS(2);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵp(0, 'names', $r3$.ɵb($r3$.ɵf1($e0_ff$, ctx.customName)));
|
||||
$r3$.ɵp(0, 'names', $r3$.ɵb($r3$.ɵf1(2, $e0_ff$, ctx.customName)));
|
||||
}
|
||||
},
|
||||
directives: [MyComp]
|
||||
|
@ -494,11 +546,12 @@ describe('compiler compliance', () => {
|
|||
if (rf & 1) {
|
||||
$r3$.ɵE(0, 'my-comp');
|
||||
$r3$.ɵe();
|
||||
$r3$.ɵrS(10);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵp(
|
||||
0, 'names',
|
||||
$r3$.ɵb($r3$.ɵfV($e0_ff$, ctx.n0, ctx.n1, ctx.n2, ctx.n3, ctx.n4, ctx.n5, ctx.n6, ctx.n7, ctx.n8)));
|
||||
$r3$.ɵb($r3$.ɵfV(10, $e0_ff$, ctx.n0, ctx.n1, ctx.n2, ctx.n3, ctx.n4, ctx.n5, ctx.n6, ctx.n7, ctx.n8)));
|
||||
}
|
||||
},
|
||||
directives: [MyComp]
|
||||
|
@ -555,9 +608,10 @@ describe('compiler compliance', () => {
|
|||
if (rf & 1) {
|
||||
$r3$.ɵE(0, 'object-comp');
|
||||
$r3$.ɵe();
|
||||
$r3$.ɵrS(2);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵp(0, 'config', $r3$.ɵb($r3$.ɵf1($e0_ff$, ctx.name)));
|
||||
$r3$.ɵp(0, 'config', $r3$.ɵb($r3$.ɵf1(2, $e0_ff$, ctx.name)));
|
||||
}
|
||||
},
|
||||
directives: [ObjectComp]
|
||||
|
@ -620,12 +674,12 @@ describe('compiler compliance', () => {
|
|||
if (rf & 1) {
|
||||
$r3$.ɵE(0, 'nested-comp');
|
||||
$r3$.ɵe();
|
||||
$r3$.ɵrS(7);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵp(
|
||||
0, 'config',
|
||||
$r3$.ɵb($r3$.ɵf2(
|
||||
$e0_ff_2$, ctx.name, $r3$.ɵf1($e0_ff_1$, $r3$.ɵf1($e0_ff$, ctx.duration)))));
|
||||
$r3$.ɵb($r3$.ɵf2(7, $e0_ff_2$, ctx.name, $r3$.ɵf1(4, $e0_ff_1$, $r3$.ɵf1(2, $e0_ff$, ctx.duration)))));
|
||||
}
|
||||
},
|
||||
directives: [NestedComp]
|
||||
|
@ -912,10 +966,11 @@ describe('compiler compliance', () => {
|
|||
$r3$.ɵT(4);
|
||||
$r3$.ɵPp(5, 'myPurePipe');
|
||||
$r3$.ɵe();
|
||||
$r3$.ɵrS(9);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵt(0, $r3$.ɵi1('', $r3$.ɵpb2(1, $r3$.ɵpb2(2,ctx.name, ctx.size), ctx.size), ''));
|
||||
$r3$.ɵt(4, $r3$.ɵi1('', $r3$.ɵpb2(5, ctx.name, ctx.size), ''));
|
||||
$r3$.ɵt(0, $r3$.ɵi1('', $r3$.ɵpb2(1, 3, $r3$.ɵpb2(2, 6, ctx.name, ctx.size), ctx.size), ''));
|
||||
$r3$.ɵt(4, $r3$.ɵi1('', $r3$.ɵpb2(5, 9, ctx.name, ctx.size), ''));
|
||||
}
|
||||
},
|
||||
pipes: [MyPurePipe, MyPipe]
|
||||
|
|
|
@ -65,6 +65,7 @@ export {
|
|||
e as ɵe,
|
||||
p as ɵp,
|
||||
pD as ɵpD,
|
||||
rS as ɵrS,
|
||||
a as ɵa,
|
||||
s as ɵs,
|
||||
sn as ɵsn,
|
||||
|
|
|
@ -64,6 +64,8 @@ export {
|
|||
text as T,
|
||||
textBinding as t,
|
||||
|
||||
reserveSlots as rS,
|
||||
|
||||
embeddedViewStart as V,
|
||||
embeddedViewEnd as v,
|
||||
detectChanges,
|
||||
|
|
|
@ -1446,12 +1446,10 @@ function generateInitialInputs(
|
|||
return initialInputData;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////
|
||||
//// ViewContainer & View
|
||||
//////////////////////////
|
||||
|
||||
|
||||
export function createLContainer(
|
||||
parentLNode: LNode, currentView: LView, template?: ComponentTemplate<any>): LContainer {
|
||||
ngDevMode && assertNotNull(parentLNode, 'containers should have a parent');
|
||||
|
@ -2146,6 +2144,57 @@ export function bind<T>(value: T | NO_CHANGE): T|NO_CHANGE {
|
|||
return changed ? value : NO_CHANGE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reserves slots for pure functions (`pureFunctionX` instructions)
|
||||
*
|
||||
* Binding for pure functions are store after the LNodes in the data array but before the binding.
|
||||
*
|
||||
* ----------------------------------------------------------------------------
|
||||
* | LNodes ... | pure function bindings | regular bindings / interpolations |
|
||||
* ----------------------------------------------------------------------------
|
||||
* ^
|
||||
* LView.bindingStartIndex
|
||||
*
|
||||
* Pure function instructions are given an offset from LView.bindingStartIndex.
|
||||
* Subtracting the offset from LView.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) {
|
||||
// Init the slots with a unique `NO_CHANGE` value so that the first change is always detected
|
||||
// whether is happens or not during the first change detection pass - pure functions checks
|
||||
// might be skipped when short-circuited.
|
||||
data.length += numSlots;
|
||||
data.fill(NO_CHANGE, -numSlots);
|
||||
// We need to initialize the binding in case a `pureFunctionX` kind of binding instruction is
|
||||
// called first in the update section.
|
||||
initBindings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the binding index before execute any `pureFunctionX` instructions.
|
||||
*
|
||||
* The index must be restored after the pure function is executed
|
||||
*
|
||||
* {@link reserveSlots}
|
||||
*/
|
||||
export function moveBindingIndexToReservedSlot(offset: number): number {
|
||||
const currentSlot = currentView.bindingIndex;
|
||||
currentView.bindingIndex = currentView.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 {
|
||||
currentView.bindingIndex = index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create interpolation bindings with a variable number of expressions.
|
||||
*
|
||||
|
@ -2378,6 +2427,22 @@ 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 = currentView.bindingStartIndex - slotOffset;
|
||||
for (let i = 0; i < numSlots; i++) {
|
||||
assertEqual(
|
||||
data[startIndex + i], NO_CHANGE,
|
||||
'The reserved slots should be set to `NO_CHANGE` on first template pass');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function _getComponentHostLElementNode<T>(component: T): LElementNode {
|
||||
ngDevMode && assertNotNull(component, 'expecting component got null');
|
||||
const lElementNode = (component as any)[NG_HOST_SYMBOL] as LElementNode;
|
||||
|
|
|
@ -65,11 +65,12 @@ function getPipeDef(name: string, registry: PipeDefList | null): PipeDef<any> {
|
|||
* the pipe only when an input to the pipe changes.
|
||||
*
|
||||
* @param index Pipe index where the pipe was stored on creation.
|
||||
* @param slotOffset the offset in the reserved slot space {@link reserveSlots}
|
||||
* @param v1 1st argument to {@link PipeTransform#transform}.
|
||||
*/
|
||||
export function pipeBind1(index: number, v1: any): any {
|
||||
export function pipeBind1(index: number, slotOffset: number, v1: any): any {
|
||||
const pipeInstance = load<PipeTransform>(index);
|
||||
return isPure(index) ? pureFunction1(pipeInstance.transform, v1, pipeInstance) :
|
||||
return isPure(index) ? pureFunction1(slotOffset, pipeInstance.transform, v1, pipeInstance) :
|
||||
pipeInstance.transform(v1);
|
||||
}
|
||||
|
||||
|
@ -80,12 +81,13 @@ export function pipeBind1(index: number, v1: any): any {
|
|||
* the pipe only when an input to the pipe changes.
|
||||
*
|
||||
* @param index Pipe index where the pipe was stored on creation.
|
||||
* @param slotOffset the offset in the reserved slot space {@link reserveSlots}
|
||||
* @param v1 1st argument to {@link PipeTransform#transform}.
|
||||
* @param v2 2nd argument to {@link PipeTransform#transform}.
|
||||
*/
|
||||
export function pipeBind2(index: number, v1: any, v2: any): any {
|
||||
export function pipeBind2(index: number, slotOffset: number, v1: any, v2: any): any {
|
||||
const pipeInstance = load<PipeTransform>(index);
|
||||
return isPure(index) ? pureFunction2(pipeInstance.transform, v1, v2, pipeInstance) :
|
||||
return isPure(index) ? pureFunction2(slotOffset, pipeInstance.transform, v1, v2, pipeInstance) :
|
||||
pipeInstance.transform(v1, v2);
|
||||
}
|
||||
|
||||
|
@ -96,14 +98,16 @@ export function pipeBind2(index: number, v1: any, v2: any): any {
|
|||
* the pipe only when an input to the pipe changes.
|
||||
*
|
||||
* @param index Pipe index where the pipe was stored on creation.
|
||||
* @param slotOffset the offset in the reserved slot space {@link reserveSlots}
|
||||
* @param v1 1st argument to {@link PipeTransform#transform}.
|
||||
* @param v2 2nd argument to {@link PipeTransform#transform}.
|
||||
* @param v3 4rd argument to {@link PipeTransform#transform}.
|
||||
*/
|
||||
export function pipeBind3(index: number, v1: any, v2: any, v3: any): any {
|
||||
export function pipeBind3(index: number, slotOffset: number, v1: any, v2: any, v3: any): any {
|
||||
const pipeInstance = load<PipeTransform>(index);
|
||||
return isPure(index) ? pureFunction3(pipeInstance.transform, v1, v2, v3, pipeInstance) :
|
||||
pipeInstance.transform(v1, v2, v3);
|
||||
return isPure(index) ?
|
||||
pureFunction3(slotOffset, pipeInstance.transform, v1, v2, v3, pipeInstance) :
|
||||
pipeInstance.transform(v1, v2, v3);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -113,15 +117,18 @@ export function pipeBind3(index: number, v1: any, v2: any, v3: any): any {
|
|||
* the pipe only when an input to the pipe changes.
|
||||
*
|
||||
* @param index Pipe index where the pipe was stored on creation.
|
||||
* @param slotOffset the offset in the reserved slot space {@link reserveSlots}
|
||||
* @param v1 1st argument to {@link PipeTransform#transform}.
|
||||
* @param v2 2nd argument to {@link PipeTransform#transform}.
|
||||
* @param v3 3rd argument to {@link PipeTransform#transform}.
|
||||
* @param v4 4th argument to {@link PipeTransform#transform}.
|
||||
*/
|
||||
export function pipeBind4(index: number, v1: any, v2: any, v3: any, v4: any): any {
|
||||
export function pipeBind4(
|
||||
index: number, slotOffset: number, v1: any, v2: any, v3: any, v4: any): any {
|
||||
const pipeInstance = load<PipeTransform>(index);
|
||||
return isPure(index) ? pureFunction4(pipeInstance.transform, v1, v2, v3, v4, pipeInstance) :
|
||||
pipeInstance.transform(v1, v2, v3, v4);
|
||||
return isPure(index) ?
|
||||
pureFunction4(slotOffset, pipeInstance.transform, v1, v2, v3, v4, pipeInstance) :
|
||||
pipeInstance.transform(v1, v2, v3, v4);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -131,11 +138,12 @@ export function pipeBind4(index: number, v1: any, v2: any, v3: any, v4: any): an
|
|||
* the pipe only when an input to the pipe changes.
|
||||
*
|
||||
* @param index Pipe index where the pipe was stored on creation.
|
||||
* @param slotOffset the offset in the reserved slot space {@link reserveSlots}
|
||||
* @param values Array of arguments to pass to {@link PipeTransform#transform} method.
|
||||
*/
|
||||
export function pipeBindV(index: number, values: any[]): any {
|
||||
export function pipeBindV(index: number, slotOffset: number, values: any[]): any {
|
||||
const pipeInstance = load<PipeTransform>(index);
|
||||
return isPure(index) ? pureFunctionV(pipeInstance.transform, values, pipeInstance) :
|
||||
return isPure(index) ? pureFunctionV(slotOffset, pipeInstance.transform, values, pipeInstance) :
|
||||
pipeInstance.transform.apply(pipeInstance, values);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {bindingUpdated, bindingUpdated2, bindingUpdated4, checkAndUpdateBinding, consumeBinding, getCreationMode} from './instructions';
|
||||
import {assertReservedSlotInitialized, bindingUpdated, bindingUpdated2, bindingUpdated4, checkAndUpdateBinding, consumeBinding, getCreationMode, moveBindingIndexToReservedSlot, restoreBindingIndex} from './instructions';
|
||||
|
||||
|
||||
|
||||
|
@ -15,92 +15,121 @@ import {bindingUpdated, bindingUpdated2, bindingUpdated4, checkAndUpdateBinding,
|
|||
* value. If it has been saved, returns the saved 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
|
||||
* @returns value
|
||||
*/
|
||||
export function pureFunction0<T>(pureFn: () => T, thisArg?: any): T {
|
||||
return getCreationMode() ? checkAndUpdateBinding(thisArg ? pureFn.call(thisArg) : pureFn()) :
|
||||
consumeBinding();
|
||||
export function pureFunction0<T>(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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 pureFn Function that returns an updated value
|
||||
* @param exp Updated expression value
|
||||
* @param thisArg Optional calling context of pureFn
|
||||
* @returns Updated value
|
||||
* @returns Updated or cached value
|
||||
*/
|
||||
export function pureFunction1(pureFn: (v: any) => any, exp: any, thisArg?: any): any {
|
||||
return bindingUpdated(exp) ?
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 pureFn
|
||||
* @param exp1
|
||||
* @param exp2
|
||||
* @param thisArg Optional calling context of pureFn
|
||||
* @returns Updated value
|
||||
* @returns Updated or cached value
|
||||
*/
|
||||
export function pureFunction2(
|
||||
pureFn: (v1: any, v2: any) => any, exp1: any, exp2: any, thisArg?: any): any {
|
||||
return bindingUpdated2(exp1, exp2) ?
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 pureFn
|
||||
* @param exp1
|
||||
* @param exp2
|
||||
* @param exp3
|
||||
* @param thisArg Optional calling context of pureFn
|
||||
* @returns Updated value
|
||||
* @returns Updated or cached value
|
||||
*/
|
||||
export function pureFunction3(
|
||||
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 {
|
||||
ngDevMode && assertReservedSlotInitialized(slotOffset, 4);
|
||||
const index = moveBindingIndexToReservedSlot(slotOffset);
|
||||
const different = bindingUpdated2(exp1, exp2);
|
||||
return bindingUpdated(exp3) || different ?
|
||||
const value = bindingUpdated(exp3) || different ?
|
||||
checkAndUpdateBinding(
|
||||
thisArg ? pureFn.call(thisArg, exp1, exp2, exp3) : pureFn(exp1, exp2, exp3)) :
|
||||
consumeBinding();
|
||||
restoreBindingIndex(index);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 pureFn
|
||||
* @param exp1
|
||||
* @param exp2
|
||||
* @param exp3
|
||||
* @param exp4
|
||||
* @param thisArg Optional calling context of pureFn
|
||||
* @returns Updated value
|
||||
* @returns Updated or cached value
|
||||
*/
|
||||
export function pureFunction4(
|
||||
pureFn: (v1: any, v2: any, v3: any, v4: any) => any, exp1: any, exp2: any, exp3: any, exp4: any,
|
||||
thisArg?: any): any {
|
||||
return bindingUpdated4(exp1, exp2, exp3, exp4) ?
|
||||
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(
|
||||
thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4) : pureFn(exp1, exp2, exp3, exp4)) :
|
||||
consumeBinding();
|
||||
restoreBindingIndex(index);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 pureFn
|
||||
* @param exp1
|
||||
* @param exp2
|
||||
|
@ -108,23 +137,28 @@ export function pureFunction4(
|
|||
* @param exp4
|
||||
* @param exp5
|
||||
* @param thisArg Optional calling context of pureFn
|
||||
* @returns Updated value
|
||||
* @returns Updated or cached value
|
||||
*/
|
||||
export function pureFunction5(
|
||||
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 {
|
||||
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);
|
||||
return bindingUpdated(exp5) || different ?
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 pureFn
|
||||
* @param exp1
|
||||
* @param exp2
|
||||
|
@ -133,23 +167,28 @@ export function pureFunction5(
|
|||
* @param exp5
|
||||
* @param exp6
|
||||
* @param thisArg Optional calling context of pureFn
|
||||
* @returns Updated value
|
||||
* @returns Updated or cached value
|
||||
*/
|
||||
export function pureFunction6(
|
||||
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 {
|
||||
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);
|
||||
return bindingUpdated2(exp5, exp6) || different ?
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 pureFn
|
||||
* @param exp1
|
||||
* @param exp2
|
||||
|
@ -159,24 +198,30 @@ export function pureFunction6(
|
|||
* @param exp6
|
||||
* @param exp7
|
||||
* @param thisArg Optional calling context of pureFn
|
||||
* @returns Updated value
|
||||
* @returns Updated or cached value
|
||||
*/
|
||||
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;
|
||||
return bindingUpdated(exp7) || 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 pureFn
|
||||
* @param exp1
|
||||
* @param exp2
|
||||
|
@ -187,18 +232,23 @@ export function pureFunction7(
|
|||
* @param exp7
|
||||
* @param exp8
|
||||
* @param thisArg Optional calling context of pureFn
|
||||
* @returns Updated value
|
||||
* @returns Updated or cached value
|
||||
*/
|
||||
export function pureFunction8(
|
||||
slotOffset: number,
|
||||
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);
|
||||
return bindingUpdated4(exp5, exp6, exp7, exp8) || different ?
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -207,17 +257,23 @@ 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 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 value
|
||||
* @returns Updated or cached value
|
||||
*/
|
||||
export function pureFunctionV(pureFn: (...v: any[]) => any, exps: any[], thisArg?: any): any {
|
||||
let different = false;
|
||||
export function pureFunctionV(
|
||||
slotOffset: number, pureFn: (...v: any[]) => any, exps: any[], thisArg?: any): any {
|
||||
ngDevMode && assertReservedSlotInitialized(slotOffset, exps.length + 1);
|
||||
const index = moveBindingIndexToReservedSlot(slotOffset);
|
||||
|
||||
let different = false;
|
||||
for (let i = 0; i < exps.length; i++) {
|
||||
bindingUpdated(exps[i]) && (different = true);
|
||||
}
|
||||
return different ? checkAndUpdateBinding(pureFn.apply(thisArg, exps)) : consumeBinding();
|
||||
const value = different ? checkAndUpdateBinding(pureFn.apply(thisArg, exps)) : consumeBinding();
|
||||
restoreBindingIndex(index);
|
||||
return value;
|
||||
}
|
||||
|
|
|
@ -502,9 +502,10 @@ describe('components & directives', () => {
|
|||
if (rf & 1) {
|
||||
$r3$.ɵE(0, 'my-array-comp');
|
||||
$r3$.ɵe();
|
||||
$r3$.ɵrS(1);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵp(0, 'names', $r3$.ɵb(ctx.someFn($r3$.ɵf0($e0_ff$))));
|
||||
$r3$.ɵp(0, 'names', $r3$.ɵb(ctx.someFn($r3$.ɵf0(1, $e0_ff$))));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -563,9 +564,10 @@ describe('components & directives', () => {
|
|||
if (rf & 1) {
|
||||
$r3$.ɵE(0, 'my-comp');
|
||||
$r3$.ɵe();
|
||||
$r3$.ɵrS(1);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵp(0, 'num', $r3$.ɵb($r3$.ɵf0($e0_ff$).length + 1));
|
||||
$r3$.ɵp(0, 'num', $r3$.ɵb($r3$.ɵf0(1, $e0_ff$).length + 1));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -606,9 +608,10 @@ describe('components & directives', () => {
|
|||
if (rf & 1) {
|
||||
$r3$.ɵE(0, 'my-array-comp');
|
||||
$r3$.ɵe();
|
||||
$r3$.ɵrS(2);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵp(0, 'names', $r3$.ɵb($r3$.ɵf1($e0_ff$, ctx.customName)));
|
||||
$r3$.ɵp(0, 'names', $r3$.ɵb($r3$.ɵf1(2, $e0_ff$, ctx.customName)));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -716,12 +719,13 @@ describe('components & directives', () => {
|
|||
if (rf & 1) {
|
||||
$r3$.ɵE(0, 'my-comp');
|
||||
$r3$.ɵe();
|
||||
$r3$.ɵrS(10);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵp(
|
||||
0, 'names',
|
||||
$r3$.ɵb(
|
||||
$r3$.ɵfV($e0_ff$, [c.n0, c.n1, c.n2, c.n3, c.n4, c.n5, c.n6, c.n7, c.n8])));
|
||||
$r3$.ɵb($r3$.ɵfV(
|
||||
10, $e0_ff$, [c.n0, c.n1, c.n2, c.n3, c.n4, c.n5, c.n6, c.n7, c.n8])));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -794,9 +798,10 @@ describe('components & directives', () => {
|
|||
if (rf & 1) {
|
||||
$r3$.ɵE(0, 'object-comp');
|
||||
$r3$.ɵe();
|
||||
$r3$.ɵrS(2);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵp(0, 'config', $r3$.ɵb($r3$.ɵf1($e0_ff$, ctx.name)));
|
||||
$r3$.ɵp(0, 'config', $r3$.ɵb($r3$.ɵf1(2, $e0_ff$, ctx.name)));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -879,12 +884,13 @@ describe('components & directives', () => {
|
|||
if (rf & 1) {
|
||||
$r3$.ɵE(0, 'nested-comp');
|
||||
$r3$.ɵe();
|
||||
$r3$.ɵrS(7);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵp(
|
||||
0, 'config', $r3$.ɵf2(
|
||||
$e0_ff_2$, ctx.name,
|
||||
$r3$.ɵb($r3$.ɵf1($e0_ff_1$, $r3$.ɵf1($e0_ff$, ctx.duration)))));
|
||||
0, 'config', $r3$.ɵb($r3$.ɵf2(
|
||||
7, $e0_ff_2$, ctx.name,
|
||||
$r3$.ɵf1(4, $e0_ff_1$, $r3$.ɵf1(2, $e0_ff$, ctx.duration)))));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -88,9 +88,12 @@ describe('pipes', () => {
|
|||
$r3$.ɵT(0);
|
||||
$r3$.ɵPp(1, 'myPipe');
|
||||
$r3$.ɵPp(2, 'myPurePipe');
|
||||
$r3$.ɵrS(6);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵt(0, $r3$.ɵi1('', $r3$.ɵpb2(1, $r3$.ɵpb2(2, ctx.name, ctx.size), ctx.size), ''));
|
||||
$r3$.ɵt(
|
||||
0,
|
||||
$r3$.ɵi1('', $r3$.ɵpb2(1, 6, $r3$.ɵpb2(2, 3, ctx.name, ctx.size), ctx.size), ''));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -166,10 +169,11 @@ describe('pipes', () => {
|
|||
$r3$.ɵT(2);
|
||||
$r3$.ɵPp(3, 'myPurePipe');
|
||||
$r3$.ɵC(4, C4, '', ['oneTimeIf', '']);
|
||||
$r3$.ɵrS(6);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵt(0, $r3$.ɵi1('', $r3$.ɵpb2(1, ctx.name, ctx.size), ''));
|
||||
$r3$.ɵt(2, $r3$.ɵi1('', $r3$.ɵpb2(3, ctx.name, ctx.size), ''));
|
||||
$r3$.ɵt(0, $r3$.ɵi1('', $r3$.ɵpb2(1, 3, ctx.name, ctx.size), ''));
|
||||
$r3$.ɵt(2, $r3$.ɵi1('', $r3$.ɵpb2(3, 6, ctx.name, ctx.size), ''));
|
||||
$r3$.ɵp(4, 'oneTimeIf', $r3$.ɵb(ctx.more));
|
||||
$r3$.ɵcR(4);
|
||||
$r3$.ɵcr();
|
||||
|
@ -181,9 +185,10 @@ describe('pipes', () => {
|
|||
$r3$.ɵT(1);
|
||||
$r3$.ɵPp(2, 'myPurePipe');
|
||||
$r3$.ɵe();
|
||||
$r3$.ɵrS(3);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵt(1, $r3$.ɵi1('', $r3$.ɵpb2(2, ctx.name, ctx.size), ''));
|
||||
$r3$.ɵt(1, $r3$.ɵi1('', $r3$.ɵpb2(2, 3, ctx.name, ctx.size), ''));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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, text, textBinding} from '../../src/render3/instructions';
|
||||
import {bind, container, containerRefreshEnd, containerRefreshStart, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, load, loadDirective, reserveSlots, text, textBinding} from '../../src/render3/instructions';
|
||||
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
||||
import {pipe, pipeBind1, pipeBind3, pipeBind4, pipeBindV} from '../../src/render3/pipe';
|
||||
|
||||
|
@ -38,9 +38,10 @@ describe('pipe', () => {
|
|||
if (rf & RenderFlags.Create) {
|
||||
text(0);
|
||||
pipe(1, 'countingPipe');
|
||||
reserveSlots(2);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
textBinding(0, interpolation1('', pipeBind1(1, person.name), ''));
|
||||
textBinding(0, interpolation1('', pipeBind1(1, 2, person.name), ''));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,9 +54,10 @@ describe('pipe', () => {
|
|||
if (rf & RenderFlags.Create) {
|
||||
text(0);
|
||||
pipe(1, 'randomPipeName');
|
||||
reserveSlots(2);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
textBinding(0, interpolation1('', pipeBind1(1, ctx.value), ''));
|
||||
textBinding(0, interpolation1('', pipeBind1(1, 2, ctx.value), ''));
|
||||
}
|
||||
}, [], pipes);
|
||||
|
||||
|
@ -97,9 +99,10 @@ describe('pipe', () => {
|
|||
elementStart(0, 'div', ['myDir', '']);
|
||||
pipe(1, 'double');
|
||||
elementEnd();
|
||||
reserveSlots(2);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(0, 'elprop', bind(pipeBind1(1, ctx)));
|
||||
elementProperty(0, 'elprop', bind(pipeBind1(1, 2, ctx)));
|
||||
directive = loadDirective(0);
|
||||
}
|
||||
}
|
||||
|
@ -112,10 +115,11 @@ describe('pipe', () => {
|
|||
if (rf & RenderFlags.Create) {
|
||||
text(0);
|
||||
pipe(1, 'multiArgPipe');
|
||||
reserveSlots(4);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
textBinding(
|
||||
0, interpolation1('', pipeBind3(1, person.name, 'one', person.address !.city), ''));
|
||||
0, interpolation1('', pipeBind3(1, 4, person.name, 'one', person.address !.city), ''));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,11 +133,12 @@ describe('pipe', () => {
|
|||
text(0);
|
||||
pipe(1, 'multiArgPipe');
|
||||
pipe(2, 'multiArgPipe');
|
||||
reserveSlots(9);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
textBinding(
|
||||
0,
|
||||
interpolation1('', pipeBind4(2, pipeBindV(1, [person.name, 'a', 'b']), 0, 1, 2), ''));
|
||||
0, interpolation1(
|
||||
'', pipeBind4(2, 9, pipeBindV(1, 4, [person.name, 'a', 'b']), 0, 1, 2), ''));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -158,9 +163,10 @@ describe('pipe', () => {
|
|||
elementStart(0, 'div');
|
||||
pipe(1, 'identityPipe');
|
||||
elementEnd();
|
||||
reserveSlots(2);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(0, 'someProp', bind(pipeBind1(1, 'Megatron')));
|
||||
elementProperty(0, 'someProp', bind(pipeBind1(1, 2, 'Megatron')));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -178,9 +184,10 @@ describe('pipe', () => {
|
|||
if (rf & RenderFlags.Create) {
|
||||
text(0);
|
||||
pipe(1, 'countingPipe');
|
||||
reserveSlots(2);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
textBinding(0, interpolation1('', pipeBind1(1, person.name), ''));
|
||||
textBinding(0, interpolation1('', pipeBind1(1, 2, person.name), ''));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -207,9 +214,10 @@ describe('pipe', () => {
|
|||
if (rf & RenderFlags.Create) {
|
||||
text(0);
|
||||
pipe(1, 'countingImpurePipe');
|
||||
reserveSlots(2);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
textBinding(0, interpolation1('', pipeBind1(1, person.name), ''));
|
||||
textBinding(0, interpolation1('', pipeBind1(1, 2, person.name), ''));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -228,10 +236,11 @@ describe('pipe', () => {
|
|||
pipe(3, 'countingImpurePipe');
|
||||
elementEnd();
|
||||
container(4);
|
||||
reserveSlots(4);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(0, 'someProp', bind(pipeBind1(1, true)));
|
||||
elementProperty(2, 'someProp', bind(pipeBind1(3, true)));
|
||||
elementProperty(0, 'someProp', bind(pipeBind1(1, 2, true)));
|
||||
elementProperty(2, 'someProp', bind(pipeBind1(3, 4, true)));
|
||||
pipeInstances.push(load<CountingImpurePipe>(1), load(3));
|
||||
containerRefreshStart(4);
|
||||
{
|
||||
|
@ -242,9 +251,10 @@ describe('pipe', () => {
|
|||
elementStart(0, 'div');
|
||||
pipe(1, 'countingImpurePipe');
|
||||
elementEnd();
|
||||
reserveSlots(2);
|
||||
}
|
||||
if (rf1 & RenderFlags.Update) {
|
||||
elementProperty(0, 'someProp', bind(pipeBind1(1, true)));
|
||||
elementProperty(0, 'someProp', bind(pipeBind1(1, 2, true)));
|
||||
pipeInstances.push(load<CountingImpurePipe>(1));
|
||||
}
|
||||
}
|
||||
|
@ -296,9 +306,10 @@ describe('pipe', () => {
|
|||
if (rf1 & RenderFlags.Create) {
|
||||
text(0);
|
||||
pipe(1, 'pipeWithOnDestroy');
|
||||
reserveSlots(2);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
textBinding(0, interpolation1('', pipeBind1(1, person.age), ''));
|
||||
textBinding(0, interpolation1('', pipeBind1(1, 2, person.age), ''));
|
||||
}
|
||||
}
|
||||
embeddedViewEnd();
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {defineComponent} from '../../src/render3/index';
|
||||
import {bind, container, containerRefreshEnd, containerRefreshStart, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, load, loadDirective} from '../../src/render3/instructions';
|
||||
import {bind, container, containerRefreshEnd, containerRefreshStart, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, load, loadDirective, reserveSlots} 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';
|
||||
|
@ -36,9 +36,10 @@ describe('array literals', () => {
|
|||
if (rf & RenderFlags.Create) {
|
||||
elementStart(0, 'my-comp');
|
||||
elementEnd();
|
||||
reserveSlots(2);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(0, 'names', bind(pureFunction1(e0_ff, ctx.customName)));
|
||||
elementProperty(0, 'names', bind(pureFunction1(2, e0_ff, ctx.customName)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,10 +91,11 @@ describe('array literals', () => {
|
|||
if (rf & RenderFlags.Create) {
|
||||
elementStart(0, 'many-prop-comp');
|
||||
elementEnd();
|
||||
reserveSlots(4);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(0, 'names1', bind(pureFunction1(e0_ff, ctx.customName)));
|
||||
elementProperty(0, 'names2', bind(pureFunction1(e0_ff_1, ctx.customName2)));
|
||||
elementProperty(0, 'names1', bind(pureFunction1(2, e0_ff, ctx.customName)));
|
||||
elementProperty(0, 'names2', bind(pureFunction1(4, e0_ff_1, ctx.customName2)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,9 +132,10 @@ 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(e0_ff, ctx.customName))));
|
||||
elementProperty(0, 'names', bind(ctx.someFn(pureFunction1(2, e0_ff, ctx.customName))));
|
||||
}
|
||||
},
|
||||
directives: directives
|
||||
|
@ -170,9 +173,10 @@ describe('array literals', () => {
|
|||
if (rf & RenderFlags.Create) {
|
||||
elementStart(0, 'my-comp');
|
||||
elementEnd();
|
||||
reserveSlots(3);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(0, 'names', bind(pureFunction2(e0_ff, ctx.customName, ctx.customName2)));
|
||||
elementProperty(0, 'names', bind(pureFunction2(3, e0_ff, ctx.customName, ctx.customName2)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -241,17 +245,19 @@ describe('array literals', () => {
|
|||
elementStart(5, 'my-comp');
|
||||
f8Comp = loadDirective(5);
|
||||
elementEnd();
|
||||
reserveSlots(39);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(0, 'names', bind(pureFunction3(e0_ff, c[5], c[6], c[7])));
|
||||
elementProperty(1, 'names', bind(pureFunction4(e2_ff, c[4], c[5], c[6], c[7])));
|
||||
elementProperty(2, 'names', bind(pureFunction5(e4_ff, c[3], c[4], c[5], c[6], c[7])));
|
||||
elementProperty(3, 'names', bind(pureFunction6(e6_ff, c[2], c[3], c[4], c[5], c[6], c[7])));
|
||||
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(2, 'names', bind(pureFunction5(15, e4_ff, c[3], c[4], c[5], c[6], c[7])));
|
||||
elementProperty(
|
||||
4, 'names', bind(pureFunction7(e8_ff, c[1], c[2], c[3], c[4], c[5], c[6], c[7])));
|
||||
3, 'names', bind(pureFunction6(22, 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])));
|
||||
elementProperty(
|
||||
5, 'names',
|
||||
bind(pureFunction8(e10_ff, c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7])));
|
||||
bind(pureFunction8(39, e10_ff, c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7])));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -295,11 +301,12 @@ describe('array literals', () => {
|
|||
if (rf & RenderFlags.Create) {
|
||||
elementStart(0, 'my-comp');
|
||||
elementEnd();
|
||||
reserveSlots(12);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(
|
||||
0, 'names', bind(pureFunctionV(e0_ff, [
|
||||
c[0], c[1], c[2], c[3], pureFunction1(e0_ff_1, c[4]), c[5], c[6], c[7], c[8]
|
||||
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]
|
||||
])));
|
||||
}
|
||||
}
|
||||
|
@ -345,9 +352,10 @@ describe('object literals', () => {
|
|||
if (rf & RenderFlags.Create) {
|
||||
elementStart(0, 'object-comp');
|
||||
elementEnd();
|
||||
reserveSlots(2);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(0, 'config', bind(pureFunction1(e0_ff, ctx.name)));
|
||||
elementProperty(0, 'config', bind(pureFunction1(2, e0_ff, ctx.name)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -380,12 +388,13 @@ describe('object literals', () => {
|
|||
if (rf & RenderFlags.Create) {
|
||||
elementStart(0, 'object-comp');
|
||||
elementEnd();
|
||||
reserveSlots(7);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(
|
||||
0, 'config',
|
||||
bind(pureFunction2(
|
||||
e0_ff, ctx.name, pureFunction1(e0_ff_1, pureFunction1(e0_ff_2, ctx.duration)))));
|
||||
0, 'config', bind(pureFunction2(
|
||||
7, e0_ff, ctx.name,
|
||||
pureFunction1(4, e0_ff_1, pureFunction1(2, e0_ff_2, ctx.duration)))));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -451,11 +460,12 @@ describe('object literals', () => {
|
|||
elementStart(0, 'object-comp');
|
||||
objectComps.push(loadDirective(0));
|
||||
elementEnd();
|
||||
reserveSlots(3);
|
||||
}
|
||||
if (rf1 & RenderFlags.Update) {
|
||||
elementProperty(
|
||||
0, 'config',
|
||||
bind(pureFunction2(e0_ff, ctx.configs[i].opacity, ctx.configs[i].duration)));
|
||||
bind(pureFunction2(3, e0_ff, ctx.configs[i].opacity, ctx.configs[i].duration)));
|
||||
}
|
||||
embeddedViewEnd();
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
import {Component, Directive, Pipe, PipeTransform, TemplateRef, ViewContainerRef} from '../../src/core';
|
||||
import {getOrCreateNodeInjectorForNode, getOrCreateTemplateRef} from '../../src/render3/di';
|
||||
import {NgOnChangesFeature, defineComponent, defineDirective, definePipe, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index';
|
||||
import {bind, container, containerRefreshEnd, containerRefreshStart, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, load, loadDirective, projection, projectionDef, text, textBinding} from '../../src/render3/instructions';
|
||||
import {bind, container, containerRefreshEnd, containerRefreshStart, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, load, loadDirective, projection, projectionDef, reserveSlots, text, textBinding} from '../../src/render3/instructions';
|
||||
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
||||
import {pipe, pipeBind1} from '../../src/render3/pipe';
|
||||
|
||||
|
@ -383,22 +383,25 @@ describe('ViewContainerRef', () => {
|
|||
elementStart(0, 'child');
|
||||
elementEnd();
|
||||
pipe(1, 'starPipe');
|
||||
reserveSlots(2);
|
||||
}
|
||||
if (rf & RenderFlags.Create) {
|
||||
elementProperty(0, 'name', bind(pipeBind1(1, 'C')));
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(0, 'name', bind(pipeBind1(1, 2, 'C')));
|
||||
}
|
||||
});
|
||||
pipe(1, 'starPipe');
|
||||
elementStart(2, 'child', ['vcref', '']);
|
||||
elementEnd();
|
||||
elementStart(3, 'child');
|
||||
pipe(3, 'starPipe');
|
||||
elementStart(4, 'child');
|
||||
elementEnd();
|
||||
reserveSlots(4);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
const tplRef = getOrCreateTemplateRef(getOrCreateNodeInjectorForNode(load(0)));
|
||||
elementProperty(2, 'tplRef', bind(tplRef));
|
||||
elementProperty(2, 'name', bind(pipeBind1(1, 'A')));
|
||||
elementProperty(3, 'name', bind(pipeBind1(1, 'B')));
|
||||
elementProperty(2, 'name', bind(pipeBind1(1, 2, 'A')));
|
||||
elementProperty(4, 'name', bind(pipeBind1(1, 4, 'B')));
|
||||
}
|
||||
},
|
||||
directives: [Child, DirectiveWithVCRef],
|
||||
|
|
Loading…
Reference in New Issue