refactor(ivy): simplify property binding metadata storage (#32457)

Since property binding metadata storage is guarded with the ngDevMode now
and several instructions were merged together, we can simplify the way we
store and read property binding metadata.

PR Close #32457
This commit is contained in:
Pawel Kozlowski
2019-09-03 17:21:57 +02:00
committed by Miško Hevery
parent cfa09b84dd
commit a383a5a165
11 changed files with 211 additions and 288 deletions

View File

@ -5,12 +5,13 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {assertNotEqual} from '../../util/assert';
import {bindingUpdated} from '../bindings';
import {SanitizerFn} from '../interfaces/sanitization';
import {BINDING_INDEX, TVIEW} from '../interfaces/view';
import {getLView, getSelectedIndex} from '../state';
import {NO_CHANGE} from '../tokens';
import {bind} from './property';
import {TsickleIssue1009, elementPropertyInternal, loadComponentRenderer} from './shared';
import {TsickleIssue1009, elementPropertyInternal, loadComponentRenderer, storePropertyBindingMetadata} from './shared';
/**
* Update a property on a host element. Only applies to native node properties, not inputs.
@ -28,12 +29,12 @@ import {TsickleIssue1009, elementPropertyInternal, loadComponentRenderer} from '
*/
export function ɵɵhostProperty<T>(
propName: string, value: T, sanitizer?: SanitizerFn | null): TsickleIssue1009 {
const index = getSelectedIndex();
ngDevMode && assertNotEqual(index, -1, 'selected index cannot be -1');
const lView = getLView();
const bindReconciledValue = bind(lView, value);
if (bindReconciledValue !== NO_CHANGE) {
elementPropertyInternal(index, propName, bindReconciledValue, sanitizer, true);
const bindingIndex = lView[BINDING_INDEX]++;
if (bindingUpdated(lView, bindingIndex, value)) {
const nodeIndex = getSelectedIndex();
elementPropertyInternal(nodeIndex, propName, value, sanitizer, true);
ngDevMode && storePropertyBindingMetadata(lView[TVIEW].data, nodeIndex, propName, bindingIndex);
}
return ɵɵhostProperty;
}
@ -62,12 +63,12 @@ export function ɵɵhostProperty<T>(
*/
export function ɵɵupdateSyntheticHostBinding<T>(
propName: string, value: T | NO_CHANGE, sanitizer?: SanitizerFn | null): TsickleIssue1009 {
const index = getSelectedIndex();
const lView = getLView();
// TODO(benlesh): remove bind call here.
const bound = bind(lView, value);
if (bound !== NO_CHANGE) {
elementPropertyInternal(index, propName, bound, sanitizer, true, loadComponentRenderer);
const bindingIndex = lView[BINDING_INDEX]++;
if (bindingUpdated(lView, bindingIndex, value)) {
const nodeIndex = getSelectedIndex();
elementPropertyInternal(nodeIndex, propName, value, sanitizer, true, loadComponentRenderer);
ngDevMode && storePropertyBindingMetadata(lView[TVIEW].data, nodeIndex, propName, bindingIndex);
}
return ɵɵupdateSyntheticHostBinding;
}

View File

@ -8,10 +8,9 @@
import {assertEqual, assertLessThan} from '../../util/assert';
import {bindingUpdated, bindingUpdated2, bindingUpdated3, bindingUpdated4} from '../bindings';
import {BINDING_INDEX, LView, TVIEW} from '../interfaces/view';
import {BINDING_INDEX, LView} from '../interfaces/view';
import {NO_CHANGE} from '../tokens';
import {renderStringify} from '../util/misc_utils';
import {storeBindingMetadata} from './shared';
@ -31,23 +30,13 @@ export function interpolationV(lView: LView, values: any[]): string|NO_CHANGE {
ngDevMode && assertLessThan(2, values.length, 'should have at least 3 values');
ngDevMode && assertEqual(values.length % 2, 1, 'should have an odd number of values');
let isBindingUpdated = false;
const tData = lView[TVIEW].data;
let bindingIndex = lView[BINDING_INDEX];
if (tData[bindingIndex] == null) {
// 2 is the index of the first static interstitial value (ie. not prefix)
for (let i = 2; i < values.length; i += 2) {
tData[bindingIndex++] = values[i];
}
bindingIndex = lView[BINDING_INDEX];
}
for (let i = 1; i < values.length; i += 2) {
// Check if bindings (odd indexes) have changed
isBindingUpdated = bindingUpdated(lView, bindingIndex++, values[i]) || isBindingUpdated;
}
lView[BINDING_INDEX] = bindingIndex;
storeBindingMetadata(lView, values[0], values[values.length - 1]);
if (!isBindingUpdated) {
return NO_CHANGE;
@ -72,7 +61,6 @@ export function interpolationV(lView: LView, values: any[]): string|NO_CHANGE {
export function interpolation1(lView: LView, prefix: string, v0: any, suffix: string): string|
NO_CHANGE {
const different = bindingUpdated(lView, lView[BINDING_INDEX]++, v0);
ngDevMode && storeBindingMetadata(lView, prefix, suffix);
return different ? prefix + renderStringify(v0) + suffix : NO_CHANGE;
}
@ -85,14 +73,6 @@ export function interpolation2(
const different = bindingUpdated2(lView, bindingIndex, v0, v1);
lView[BINDING_INDEX] += 2;
if (ngDevMode) {
// Only set static strings the first time (data will be null subsequent runs).
const data = storeBindingMetadata(lView, prefix, suffix);
if (data) {
lView[TVIEW].data[bindingIndex] = i0;
}
}
return different ? prefix + renderStringify(v0) + i0 + renderStringify(v1) + suffix : NO_CHANGE;
}
@ -106,16 +86,6 @@ export function interpolation3(
const different = bindingUpdated3(lView, bindingIndex, v0, v1, v2);
lView[BINDING_INDEX] += 3;
if (ngDevMode) {
// Only set static strings the first time (data will be null subsequent runs).
const data = storeBindingMetadata(lView, prefix, suffix);
if (data) {
const tData = lView[TVIEW].data;
tData[bindingIndex] = i0;
tData[bindingIndex + 1] = i1;
}
}
return different ?
prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + renderStringify(v2) + suffix :
NO_CHANGE;
@ -131,17 +101,6 @@ export function interpolation4(
const different = bindingUpdated4(lView, bindingIndex, v0, v1, v2, v3);
lView[BINDING_INDEX] += 4;
if (ngDevMode) {
// Only set static strings the first time (data will be null subsequent runs).
const data = storeBindingMetadata(lView, prefix, suffix);
if (data) {
const tData = lView[TVIEW].data;
tData[bindingIndex] = i0;
tData[bindingIndex + 1] = i1;
tData[bindingIndex + 2] = i2;
}
}
return different ?
prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + renderStringify(v2) + i2 +
renderStringify(v3) + suffix :
@ -159,18 +118,6 @@ export function interpolation5(
different = bindingUpdated(lView, bindingIndex + 4, v4) || different;
lView[BINDING_INDEX] += 5;
if (ngDevMode) {
// Only set static strings the first time (data will be null subsequent runs).
const data = storeBindingMetadata(lView, prefix, suffix);
if (data) {
const tData = lView[TVIEW].data;
tData[bindingIndex] = i0;
tData[bindingIndex + 1] = i1;
tData[bindingIndex + 2] = i2;
tData[bindingIndex + 3] = i3;
}
}
return different ?
prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + renderStringify(v2) + i2 +
renderStringify(v3) + i3 + renderStringify(v4) + suffix :
@ -188,19 +135,6 @@ export function interpolation6(
different = bindingUpdated2(lView, bindingIndex + 4, v4, v5) || different;
lView[BINDING_INDEX] += 6;
if (ngDevMode) {
// Only set static strings the first time (data will be null subsequent runs).
const data = storeBindingMetadata(lView, prefix, suffix);
if (data) {
const tData = lView[TVIEW].data;
tData[bindingIndex] = i0;
tData[bindingIndex + 1] = i1;
tData[bindingIndex + 2] = i2;
tData[bindingIndex + 3] = i3;
tData[bindingIndex + 4] = i4;
}
}
return different ?
prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + renderStringify(v2) + i2 +
renderStringify(v3) + i3 + renderStringify(v4) + i4 + renderStringify(v5) + suffix :
@ -219,20 +153,6 @@ export function interpolation7(
different = bindingUpdated3(lView, bindingIndex + 4, v4, v5, v6) || different;
lView[BINDING_INDEX] += 7;
if (ngDevMode) {
// Only set static strings the first time (data will be null subsequent runs).
const data = storeBindingMetadata(lView, prefix, suffix);
if (data) {
const tData = lView[TVIEW].data;
tData[bindingIndex] = i0;
tData[bindingIndex + 1] = i1;
tData[bindingIndex + 2] = i2;
tData[bindingIndex + 3] = i3;
tData[bindingIndex + 4] = i4;
tData[bindingIndex + 5] = i5;
}
}
return different ?
prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + renderStringify(v2) + i2 +
renderStringify(v3) + i3 + renderStringify(v4) + i4 + renderStringify(v5) + i5 +
@ -252,21 +172,6 @@ export function interpolation8(
different = bindingUpdated4(lView, bindingIndex + 4, v4, v5, v6, v7) || different;
lView[BINDING_INDEX] += 8;
if (ngDevMode) {
// Only set static strings the first time (data will be null subsequent runs).
const data = storeBindingMetadata(lView, prefix, suffix);
if (data) {
const tData = lView[TVIEW].data;
tData[bindingIndex] = i0;
tData[bindingIndex + 1] = i1;
tData[bindingIndex + 2] = i2;
tData[bindingIndex + 3] = i3;
tData[bindingIndex + 4] = i4;
tData[bindingIndex + 5] = i5;
tData[bindingIndex + 6] = i6;
}
}
return different ?
prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + renderStringify(v2) + i2 +
renderStringify(v3) + i3 + renderStringify(v4) + i4 + renderStringify(v5) + i5 +

View File

@ -118,8 +118,7 @@ export const TNodeConstructor = class TNode implements ITNode {
public injectorIndex: number, //
public directiveStart: number, //
public directiveEnd: number, //
public propertyMetadataStartIndex: number, //
public propertyMetadataEndIndex: number, //
public propertyBindings: number[]|null, //
public flags: TNodeFlags, //
public providerIndexes: TNodeProviderIndexes, //
public tagName: string|null, //

View File

@ -7,11 +7,11 @@
*/
import {bindingUpdated} from '../bindings';
import {SanitizerFn} from '../interfaces/sanitization';
import {BINDING_INDEX, LView} from '../interfaces/view';
import {BINDING_INDEX, LView, TVIEW} from '../interfaces/view';
import {getLView, getSelectedIndex} from '../state';
import {NO_CHANGE} from '../tokens';
import {TsickleIssue1009, elementPropertyInternal, storeBindingMetadata} from './shared';
import {TsickleIssue1009, elementPropertyInternal, storePropertyBindingMetadata} from './shared';
/**
@ -35,9 +35,11 @@ import {TsickleIssue1009, elementPropertyInternal, storeBindingMetadata} from '.
export function ɵɵproperty<T>(
propName: string, value: T, sanitizer?: SanitizerFn | null): TsickleIssue1009 {
const lView = getLView();
const bindReconciledValue = bind(lView, value);
if (bindReconciledValue !== NO_CHANGE) {
elementPropertyInternal(getSelectedIndex(), propName, bindReconciledValue, sanitizer);
const bindingIndex = lView[BINDING_INDEX]++;
if (bindingUpdated(lView, bindingIndex, value)) {
const nodeIndex = getSelectedIndex();
elementPropertyInternal(nodeIndex, propName, value, sanitizer);
ngDevMode && storePropertyBindingMetadata(lView[TVIEW].data, nodeIndex, propName, bindingIndex);
}
return ɵɵproperty;
}
@ -49,7 +51,5 @@ export function ɵɵproperty<T>(
* @param value Value to diff
*/
export function bind<T>(lView: LView, value: T): T|NO_CHANGE {
const bindingIndex = lView[BINDING_INDEX]++;
ngDevMode && storeBindingMetadata(lView);
return bindingUpdated(lView, bindingIndex, value) ? value : NO_CHANGE;
return bindingUpdated(lView, lView[BINDING_INDEX]++, value) ? value : NO_CHANGE;
}

View File

@ -6,11 +6,12 @@
* found in the LICENSE file at https://angular.io/license
*/
import {SanitizerFn} from '../interfaces/sanitization';
import {BINDING_INDEX, TVIEW} from '../interfaces/view';
import {getLView, getSelectedIndex} from '../state';
import {NO_CHANGE} from '../tokens';
import {interpolation1, interpolation2, interpolation3, interpolation4, interpolation5, interpolation6, interpolation7, interpolation8, interpolationV} from './interpolation';
import {TsickleIssue1009, elementPropertyInternal} from './shared';
import {TsickleIssue1009, elementPropertyInternal, storePropertyBindingMetadata} from './shared';
@ -81,9 +82,13 @@ export function ɵɵpropertyInterpolate(
export function ɵɵpropertyInterpolate1(
propName: string, prefix: string, v0: any, suffix: string,
sanitizer?: SanitizerFn): TsickleIssue1009 {
const interpolatedValue = interpolation1(getLView(), prefix, v0, suffix);
const lView = getLView();
const interpolatedValue = interpolation1(lView, prefix, v0, suffix);
if (interpolatedValue !== NO_CHANGE) {
elementPropertyInternal(getSelectedIndex(), propName, interpolatedValue, sanitizer);
ngDevMode && storePropertyBindingMetadata(
lView[TVIEW].data, getSelectedIndex(), propName, lView[BINDING_INDEX] - 1,
prefix, suffix);
}
return ɵɵpropertyInterpolate1;
}
@ -121,9 +126,14 @@ export function ɵɵpropertyInterpolate1(
export function ɵɵpropertyInterpolate2(
propName: string, prefix: string, v0: any, i0: string, v1: any, suffix: string,
sanitizer?: SanitizerFn): TsickleIssue1009 {
const interpolatedValue = interpolation2(getLView(), prefix, v0, i0, v1, suffix);
const lView = getLView();
const interpolatedValue = interpolation2(lView, prefix, v0, i0, v1, suffix);
if (interpolatedValue !== NO_CHANGE) {
elementPropertyInternal(getSelectedIndex(), propName, interpolatedValue, sanitizer);
const nodeIndex = getSelectedIndex();
elementPropertyInternal(nodeIndex, propName, interpolatedValue, sanitizer);
ngDevMode &&
storePropertyBindingMetadata(
lView[TVIEW].data, nodeIndex, propName, lView[BINDING_INDEX] - 2, prefix, i0, suffix);
}
return ɵɵpropertyInterpolate2;
}
@ -164,9 +174,14 @@ export function ɵɵpropertyInterpolate2(
export function ɵɵpropertyInterpolate3(
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any,
suffix: string, sanitizer?: SanitizerFn): TsickleIssue1009 {
const interpolatedValue = interpolation3(getLView(), prefix, v0, i0, v1, i1, v2, suffix);
const lView = getLView();
const interpolatedValue = interpolation3(lView, prefix, v0, i0, v1, i1, v2, suffix);
if (interpolatedValue !== NO_CHANGE) {
elementPropertyInternal(getSelectedIndex(), propName, interpolatedValue, sanitizer);
const nodeIndex = getSelectedIndex();
elementPropertyInternal(nodeIndex, propName, interpolatedValue, sanitizer);
ngDevMode && storePropertyBindingMetadata(
lView[TVIEW].data, nodeIndex, propName, lView[BINDING_INDEX] - 3, prefix, i0,
i1, suffix);
}
return ɵɵpropertyInterpolate3;
}
@ -209,9 +224,14 @@ export function ɵɵpropertyInterpolate3(
export function ɵɵpropertyInterpolate4(
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
v3: any, suffix: string, sanitizer?: SanitizerFn): TsickleIssue1009 {
const interpolatedValue = interpolation4(getLView(), prefix, v0, i0, v1, i1, v2, i2, v3, suffix);
const lView = getLView();
const interpolatedValue = interpolation4(lView, prefix, v0, i0, v1, i1, v2, i2, v3, suffix);
if (interpolatedValue !== NO_CHANGE) {
elementPropertyInternal(getSelectedIndex(), propName, interpolatedValue, sanitizer);
const nodeIndex = getSelectedIndex();
elementPropertyInternal(nodeIndex, propName, interpolatedValue, sanitizer);
ngDevMode && storePropertyBindingMetadata(
lView[TVIEW].data, nodeIndex, propName, lView[BINDING_INDEX] - 4, prefix, i0,
i1, i2, suffix);
}
return ɵɵpropertyInterpolate4;
}
@ -256,10 +276,15 @@ export function ɵɵpropertyInterpolate4(
export function ɵɵpropertyInterpolate5(
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
v3: any, i3: string, v4: any, suffix: string, sanitizer?: SanitizerFn): TsickleIssue1009 {
const lView = getLView();
const interpolatedValue =
interpolation5(getLView(), prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix);
interpolation5(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix);
if (interpolatedValue !== NO_CHANGE) {
elementPropertyInternal(getSelectedIndex(), propName, interpolatedValue, sanitizer);
const nodeIndex = getSelectedIndex();
elementPropertyInternal(nodeIndex, propName, interpolatedValue, sanitizer);
ngDevMode && storePropertyBindingMetadata(
lView[TVIEW].data, nodeIndex, propName, lView[BINDING_INDEX] - 5, prefix, i0,
i1, i2, i3, suffix);
}
return ɵɵpropertyInterpolate5;
}
@ -307,10 +332,15 @@ export function ɵɵpropertyInterpolate6(
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
v3: any, i3: string, v4: any, i4: string, v5: any, suffix: string,
sanitizer?: SanitizerFn): TsickleIssue1009 {
const lView = getLView();
const interpolatedValue =
interpolation6(getLView(), prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix);
interpolation6(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix);
if (interpolatedValue !== NO_CHANGE) {
elementPropertyInternal(getSelectedIndex(), propName, interpolatedValue, sanitizer);
const nodeIndex = getSelectedIndex();
elementPropertyInternal(nodeIndex, propName, interpolatedValue, sanitizer);
ngDevMode && storePropertyBindingMetadata(
lView[TVIEW].data, nodeIndex, propName, lView[BINDING_INDEX] - 6, prefix, i0,
i1, i2, i3, i4, suffix);
}
return ɵɵpropertyInterpolate6;
}
@ -360,10 +390,15 @@ export function ɵɵpropertyInterpolate7(
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, suffix: string,
sanitizer?: SanitizerFn): TsickleIssue1009 {
const interpolatedValue = interpolation7(
getLView(), prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix);
const lView = getLView();
const interpolatedValue =
interpolation7(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix);
if (interpolatedValue !== NO_CHANGE) {
elementPropertyInternal(getSelectedIndex(), propName, interpolatedValue, sanitizer);
const nodeIndex = getSelectedIndex();
elementPropertyInternal(nodeIndex, propName, interpolatedValue, sanitizer);
ngDevMode && storePropertyBindingMetadata(
lView[TVIEW].data, nodeIndex, propName, lView[BINDING_INDEX] - 7, prefix, i0,
i1, i2, i3, i4, i5, suffix);
}
return ɵɵpropertyInterpolate7;
}
@ -415,10 +450,15 @@ export function ɵɵpropertyInterpolate8(
propName: string, prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string,
v3: any, i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, i6: string, v7: any,
suffix: string, sanitizer?: SanitizerFn): TsickleIssue1009 {
const lView = getLView();
const interpolatedValue = interpolation8(
getLView(), prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix);
lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix);
if (interpolatedValue !== NO_CHANGE) {
elementPropertyInternal(getSelectedIndex(), propName, interpolatedValue, sanitizer);
const nodeIndex = getSelectedIndex();
elementPropertyInternal(nodeIndex, propName, interpolatedValue, sanitizer);
ngDevMode && storePropertyBindingMetadata(
lView[TVIEW].data, nodeIndex, propName, lView[BINDING_INDEX] - 8, prefix, i0,
i1, i2, i3, i4, i5, i6, suffix);
}
return ɵɵpropertyInterpolate8;
}
@ -455,9 +495,20 @@ export function ɵɵpropertyInterpolate8(
*/
export function ɵɵpropertyInterpolateV(
propName: string, values: any[], sanitizer?: SanitizerFn): TsickleIssue1009 {
const interpolatedValue = interpolationV(getLView(), values);
const lView = getLView();
const interpolatedValue = interpolationV(lView, values);
if (interpolatedValue !== NO_CHANGE) {
elementPropertyInternal(getSelectedIndex(), propName, interpolatedValue, sanitizer);
const nodeIndex = getSelectedIndex();
elementPropertyInternal(nodeIndex, propName, interpolatedValue, sanitizer);
if (ngDevMode) {
const interpolationInBetween = [values[0]]; // prefix
for (let i = 2; i < values.length; i += 2) {
interpolationInBetween.push(values[i]);
}
storePropertyBindingMetadata(
lView[TVIEW].data, nodeIndex, propName,
lView[BINDING_INDEX] - interpolationInBetween.length + 1, ...interpolationInBetween);
}
}
return ɵɵpropertyInterpolateV;
}

View File

@ -759,8 +759,7 @@ export function createTNode(
injectorIndex, // injectorIndex: number
-1, // directiveStart: number
-1, // directiveEnd: number
-1, // propertyMetadataStartIndex: number
-1, // propertyMetadataEndIndex: number
null, // propertyBindings: number[]|null
0, // flags: TNodeFlags
0, // providerIndexes: TNodeProviderIndexes
tagName, // tagName: string|null
@ -784,8 +783,7 @@ export function createTNode(
injectorIndex: injectorIndex,
directiveStart: -1,
directiveEnd: -1,
propertyMetadataStartIndex: -1,
propertyMetadataEndIndex: -1,
propertyBindings: null,
flags: 0,
providerIndexes: 0,
tagName: tagName,
@ -860,11 +858,12 @@ export function elementPropertyInternal<T>(
loadRendererFn?: ((tNode: TNode, lView: LView) => Renderer3) | null): void {
ngDevMode && assertNotSame(value, NO_CHANGE as any, 'Incoming value should never be NO_CHANGE.');
const lView = getLView();
const tView = lView[TVIEW];
const element = getNativeByIndex(index, lView) as RElement | RComment;
const tNode = getTNode(index, lView);
let inputData: PropertyAliases|null|undefined;
let dataValue: PropertyAliasValue|undefined;
if (!nativeOnly && (inputData = initializeTNodeInputs(lView[TVIEW], tNode)) &&
if (!nativeOnly && (inputData = initializeTNodeInputs(tView, tNode)) &&
(dataValue = inputData[propName])) {
setInputsForProperty(lView, dataValue, value);
if (isComponent(tNode)) markDirtyIfOnPush(lView, index + HEADER_OFFSET);
@ -893,8 +892,6 @@ export function elementPropertyInternal<T>(
ngDevMode.rendererSetProperty++;
}
ngDevMode && savePropertyDebugData(tNode, lView, propName, lView[TVIEW].data, nativeOnly);
const renderer = loadRendererFn ? loadRendererFn(tNode, lView) : lView[RENDERER];
// It is assumed that the sanitizer is only added when the compiler determines that the
// property
@ -982,34 +979,6 @@ function matchingSchemas(hostView: LView, tagName: string | null): boolean {
return false;
}
/**
* Stores debugging data for this property binding on first template pass.
* This enables features like DebugElement.properties.
*/
function savePropertyDebugData(
tNode: TNode, lView: LView, propName: string, tData: TData,
nativeOnly: boolean | undefined): void {
const lastBindingIndex = lView[BINDING_INDEX] - 1;
// Bind/interpolation functions save binding metadata in the last binding index,
// but leave the property name blank. If the interpolation delimiter is at the 0
// index, we know that this is our first pass and the property name still needs to
// be set.
const bindingMetadata = tData[lastBindingIndex] as string;
if (bindingMetadata[0] == INTERPOLATION_DELIMITER) {
tData[lastBindingIndex] = propName + bindingMetadata;
// We don't want to store indices for host bindings because they are stored in a
// different part of LView (the expando section).
if (!nativeOnly) {
if (tNode.propertyMetadataStartIndex == -1) {
tNode.propertyMetadataStartIndex = lastBindingIndex;
}
tNode.propertyMetadataEndIndex = lastBindingIndex + 1;
}
}
}
/**
* Creates an error that should be thrown when encountering an unknown property on an element.
* @param propName Name of the invalid property.
@ -1758,27 +1727,45 @@ function executeViewQueryFn<T>(
///////////////////////////////
/**
* Creates binding metadata for a particular binding and stores it in
* TView.data. These are generated in order to support DebugElement.properties.
* Stores meta-data for a property binding to be used by TestBed's `DebugElement.properties`.
*
* Each binding / interpolation will have one (including attribute bindings)
* because at the time of binding, we don't know to which instruction the binding
* belongs. It is always stored in TView.data at the index of the last binding
* value in LView (e.g. for interpolation8, it would be stored at the index of
* the 8th value).
* In order to support TestBed's `DebugElement.properties` we need to save, for each binding:
* - a bound property name;
* - a static parts of interpolated strings;
*
* @param lView The LView that contains the current binding index.
* @param prefix The static prefix string
* @param suffix The static suffix string
* A given property metadata is saved at the binding's index in the `TView.data` (in other words, a
* property binding metadata will be stored in `TView.data` at the same index as a bound value in
* `LView`). Metadata are represented as `INTERPOLATION_DELIMITER`-delimited string with the
* following format:
* - `propertyName` for bound properties;
* - `propertyName<6D>prefix<69>interpolation_static_part1<74>..interpolation_static_partN<74>suffix` for
* interpolated properties.
*
* @returns Newly created binding metadata string for this binding or null
* @param tData `TData` where meta-data will be saved;
* @param nodeIndex index of a `TNode` that is a target of the binding;
* @param propertyName bound property name;
* @param bindingIndex binding index in `LView`
* @param interpolationParts static interpolation parts (for property interpolations)
*/
export function storeBindingMetadata(lView: LView, prefix = '', suffix = ''): string|null {
const tData = lView[TVIEW].data;
const lastBindingIndex = lView[BINDING_INDEX] - 1;
const value = INTERPOLATION_DELIMITER + prefix + INTERPOLATION_DELIMITER + suffix;
return tData[lastBindingIndex] == null ? (tData[lastBindingIndex] = value) : null;
export function storePropertyBindingMetadata(
tData: TData, nodeIndex: number, propertyName: string, bindingIndex: number,
...interpolationParts: string[]) {
// Binding meta-data are stored only the first time a given property instruction is processed.
// Since we don't have a concept of the "first update pass" we need to check for presence of the
// binding meta-data to decide if one should be stored (or if was stored already).
if (tData[bindingIndex] === null) {
const tNode = tData[nodeIndex + HEADER_OFFSET] as TNode;
if (tNode.inputs == null || !tNode.inputs[propertyName]) {
const propBindingIdxs = tNode.propertyBindings || (tNode.propertyBindings = []);
propBindingIdxs.push(bindingIndex);
let bindingMetadata = propertyName;
if (interpolationParts.length > 0) {
bindingMetadata +=
INTERPOLATION_DELIMITER + interpolationParts.join(INTERPOLATION_DELIMITER);
}
tData[bindingIndex] = bindingMetadata;
}
}
}
export const CLEAN_PROMISE = _CLEAN_PROMISE;

View File

@ -260,19 +260,13 @@ export interface TNode {
directiveEnd: number;
/**
* Stores the first index where property binding metadata is stored for
* this node.
* Stores indexes of property bindings. This field is only set in the ngDevMode and holds indexes
* of property bindings so TestBed can get bound property metadata for a given node.
*/
propertyMetadataStartIndex: number;
propertyBindings: number[]|null;
/**
* Stores the exclusive final index where property binding metadata is
* stored for this node.
*/
propertyMetadataEndIndex: number;
/**
* Stores if Node isComponent, isProjected, hasContentQuery, hasClassInput and hasStyleInput
* Stores if Node isComponent, isProjected, hasContentQuery, hasClassInput and hasStyleInput etc.
*/
flags: TNodeFlags;

View File

@ -82,14 +82,6 @@ export function ɵɵresolveBody(element: RElement & {ownerDocument: Document}) {
*/
export const INTERPOLATION_DELIMITER = `<EFBFBD>`;
/**
* Determines whether or not the given string is a property metadata string.
* See storeBindingMetadata().
*/
export function isPropMetadataString(str: string): boolean {
return str.indexOf(INTERPOLATION_DELIMITER) >= 0;
}
/**
* Unwrap a value which might be behind a closure (for forward declaration reasons).
*/