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:
Victor Berchet 2018-05-21 15:59:25 -07:00 committed by Matias Niemelä
parent 83bb5d1922
commit 4f36340de7
13 changed files with 362 additions and 116 deletions

View File

@ -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};
}

View File

@ -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
]);
}
/**

View File

@ -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]

View File

@ -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,

View File

@ -64,6 +64,8 @@ export {
text as T,
textBinding as t,
reserveSlots as rS,
embeddedViewStart as V,
embeddedViewEnd as v,
detectChanges,

View File

@ -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;

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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)))));
}
}
});

View File

@ -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), ''));
}
}
}

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 {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();

View File

@ -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();
}

View File

@ -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],