perf(ivy): avoid generating extra parameters for host property bindings (#31550)

Currently we reuse the same instruction both for regular property bindings and property bindings on the `host`. The only difference between the two is that when it's on the host we shouldn't support inputs. We have an optional parameter called `nativeOnly` which is used to differentiate the two, however since `nativeOnly` is preceeded by another optional parameter (`sanitizer`), we have to generate two extra parameters for each host property bindings every time (e.g. `property('someProp', 'someValue', null, true)`).

These changes add a new instruction called `hostProperty` which avoids the need for the two parameters by removing `nativeOnly` which is always set and it allows us to omit `sanitizer` when it isn't being used.

These changes also remove the `nativeOnly` parameter from the `updateSyntheticHostBinding` instruction, because it's only generated for host elements which means that we can assume that its value will always be `true`.

PR Close #31550
This commit is contained in:
crisbeto 2019-07-14 11:11:10 +02:00 committed by Matias Niemelä
parent 46c03bd866
commit 40d785f0a0
14 changed files with 106 additions and 73 deletions

View File

@ -441,12 +441,10 @@ describe('compiler compliance', () => {
$r3$.ɵɵpureFunction2(5, $_c1$, ctx.getExpandedState(),
$r3$.ɵɵpureFunction2(2, $_c0$, ctx.collapsedHeight, ctx.expandedHeight)
)
, null, true
)("@expansionWidth",
$r3$.ɵɵpureFunction2(11, $_c1$, ctx.getExpandedState(),
$r3$.ɵɵpureFunction2(8, $_c2$, ctx.collapsedWidth, ctx.expandedWidth)
)
, null, true
);
}
},

View File

@ -642,7 +642,7 @@ describe('compiler compliance: bindings', () => {
$r3$.ɵɵallocHostVars(1);
}
if (rf & 2) {
$r3$.ɵɵproperty("id", ctx.dirId, null, true);
$r3$.ɵɵhostProperty("id", ctx.dirId);
}
}
});
@ -689,7 +689,7 @@ describe('compiler compliance: bindings', () => {
$r3$.ɵɵallocHostVars(3);
}
if (rf & 2) {
$r3$.ɵɵproperty("id", $r3$.ɵɵpureFunction1(1, $ff$, ctx.id), null, true);
$r3$.ɵɵhostProperty("id", $r3$.ɵɵpureFunction1(1, $ff$, ctx.id));
}
},
consts: 0,
@ -888,7 +888,7 @@ describe('compiler compliance: bindings', () => {
hostBindings: function MyDirective_HostBindings(rf, ctx, elIndex) {
if (rf & 2) {
$r3$.ɵɵproperty("title", ctx.myTitle, null, true)("tabindex", 1, null, true)("id", ctx.myId, null, true);
$r3$.ɵɵhostProperty("title", ctx.myTitle)("tabindex", 1)("id", ctx.myId);
}
}
`;
@ -924,7 +924,7 @@ describe('compiler compliance: bindings', () => {
hostBindings: function MyDirective_HostBindings(rf, ctx, elIndex) {
if (rf & 2) {
$r3$.ɵɵproperty("tabindex", 1, null, true)("title", ctx.myTitle, null, true)("id", ctx.myId, null, true);
$r3$.ɵɵhostProperty("tabindex", 1)("title", ctx.myTitle)("id", ctx.myId);
}
}
`;
@ -956,7 +956,7 @@ describe('compiler compliance: bindings', () => {
hostBindings: function MyDirective_HostBindings(rf, ctx, elIndex) {
if (rf & 2) {
$r3$.ɵɵproperty("title", "my title", null, true)("id", "my-id", null, true);
$r3$.ɵɵhostProperty("title", "my title")("id", "my-id");
$r3$.ɵɵattribute("tabindex", 1);
}
}
@ -992,7 +992,7 @@ describe('compiler compliance: bindings', () => {
hostBindings: function MyDirective_HostBindings(rf, ctx, elIndex) {
if (rf & 2) {
$r3$.ɵɵupdateSyntheticHostBinding("@expand", ctx.expandedState, null, true)("@fadeOut", true, null, true)("@shrink", ctx.isSmall, null, true);
$r3$.ɵɵupdateSyntheticHostBinding("@expand", ctx.expandedState)("@fadeOut", true)("@shrink", ctx.isSmall);
}
}
`;
@ -1095,7 +1095,7 @@ describe('compiler compliance: bindings', () => {
hostBindings: function MyDirective_HostBindings(rf, ctx, elIndex) {
if (rf & 2) {
$r3$.ɵɵproperty("tabindex", 1, null, true);
$r3$.ɵɵhostProperty("tabindex", 1);
$r3$.ɵɵattribute("title", "my title")("id", "my-id");
}
}

View File

@ -348,7 +348,7 @@ describe('compiler compliance: styling', () => {
$r3$.ɵɵcomponentHostSyntheticListener("@myAnim.start", function MyAnimDir_animation_myAnim_start_HostBindingHandler($event) { return ctx.onStart(); });
$r3$.ɵɵcomponentHostSyntheticListener("@myAnim.done", function MyAnimDir_animation_myAnim_done_HostBindingHandler($event) { return ctx.onDone(); });
} if (rf & 2) {
$r3$.ɵɵupdateSyntheticHostBinding("@myAnim", ctx.myAnimState, null, true);
$r3$.ɵɵupdateSyntheticHostBinding("@myAnim", ctx.myAnimState);
}
}
@ -1621,7 +1621,7 @@ describe('compiler compliance: styling', () => {
$r3$.ɵɵstyling(null, null, $r3$.ɵɵdefaultStyleSanitizer);
}
if (rf & 2) {
$r3$.ɵɵproperty("id", ctx.id, null, true)("title", ctx.title, null, true);
$r3$.ɵɵhostProperty("id", ctx.id)("title", ctx.title);
$r3$.ɵɵstyleMap(ctx.myStyle);
$r3$.ɵɵclassMap(ctx.myClass);
$r3$.ɵɵstylingApply();
@ -1667,7 +1667,7 @@ describe('compiler compliance: styling', () => {
$r3$.ɵɵstyling($_c0$, $_c1$);
}
if (rf & 2) {
$r3$.ɵɵproperty("id", ctx.id, null, true)("title", ctx.title, null, true);
$r3$.ɵɵhostProperty("id", ctx.id)("title", ctx.title);
$r3$.ɵɵstyleProp(0, ctx.myWidth);
$r3$.ɵɵclassProp(0, ctx.myFooClass);
$r3$.ɵɵstylingApply();

View File

@ -1832,7 +1832,7 @@ runInEachFileSystem(os => {
i0.ɵɵstyling(_c0);
}
if (rf & 2) {
i0.ɵɵproperty("prop", ctx.bar, null, true);
i0.ɵɵhostProperty("prop", ctx.bar);
i0.ɵɵattribute("hello", ctx.foo);
i0.ɵɵclassProp(0, ctx.someClass);
i0.ɵɵstylingApply();
@ -3323,7 +3323,7 @@ runInEachFileSystem(os => {
i0.ɵɵallocHostVars(6);
}
if (rf & 2) {
i0.ɵɵproperty("href", ctx.propHref, i0.ɵɵsanitizeUrlOrResourceUrl, true)("src", ctx.propSrc, i0.ɵɵsanitizeUrlOrResourceUrl, true)("action", ctx.propAction, i0.ɵɵsanitizeUrl, true)("profile", ctx.propProfile, i0.ɵɵsanitizeResourceUrl, true)("innerHTML", ctx.propInnerHTML, i0.ɵɵsanitizeHtml, true)("title", ctx.propSafeTitle, null, true);
i0.ɵɵhostProperty("href", ctx.propHref, i0.ɵɵsanitizeUrlOrResourceUrl)("src", ctx.propSrc, i0.ɵɵsanitizeUrlOrResourceUrl)("action", ctx.propAction, i0.ɵɵsanitizeUrl)("profile", ctx.propProfile, i0.ɵɵsanitizeResourceUrl)("innerHTML", ctx.propInnerHTML, i0.ɵɵsanitizeHtml)("title", ctx.propSafeTitle);
}
}
`;
@ -3358,7 +3358,7 @@ runInEachFileSystem(os => {
i0.ɵɵallocHostVars(6);
}
if (rf & 2) {
i0.ɵɵproperty("src", ctx.srcProp, null, true)("href", ctx.hrefProp, null, true)("title", ctx.titleProp, null, true);
i0.ɵɵhostProperty("src", ctx.srcProp)("href", ctx.hrefProp)("title", ctx.titleProp);
i0.ɵɵattribute("src", ctx.srcAttr)("href", ctx.hrefAttr)("title", ctx.titleAttr);
}
}

View File

@ -168,6 +168,8 @@ export class Identifiers {
static pipeBind4: o.ExternalReference = {name: 'ɵɵpipeBind4', moduleName: CORE};
static pipeBindV: o.ExternalReference = {name: 'ɵɵpipeBindV', moduleName: CORE};
static hostProperty: o.ExternalReference = {name: 'ɵɵhostProperty', moduleName: CORE};
static property: o.ExternalReference = {name: 'ɵɵproperty', moduleName: CORE};
static propertyInterpolate:

View File

@ -644,7 +644,7 @@ function createHostBindingsFunction(
const attributeBindings: o.Expression[][] = [];
const syntheticHostBindings: o.Expression[][] = [];
(bindings || []).forEach((binding: ParsedProperty) => {
bindings && bindings.forEach((binding: ParsedProperty) => {
const name = binding.name;
const stylingInputWasSet =
styleBuilder.registerInputBasedOnName(name, binding.expression, binding.sourceSpan);
@ -677,18 +677,10 @@ function createHostBindingsFunction(
if (sanitizerFn) {
instructionParams.push(sanitizerFn);
}
if (!isAttribute) {
if (!sanitizerFn) {
// append `null` in front of `nativeOnly` flag if no sanitizer fn defined
instructionParams.push(o.literal(null));
}
// host bindings must have nativeOnly prop set to true
instructionParams.push(o.literal(true));
}
updateStatements.push(...bindingExpr.stmts);
if (instruction === R3.property) {
if (instruction === R3.hostProperty) {
propertyBindings.push(instructionParams);
} else if (instruction === R3.attribute) {
attributeBindings.push(instructionParams);
@ -701,7 +693,7 @@ function createHostBindingsFunction(
});
if (propertyBindings.length > 0) {
updateStatements.push(chainedInstruction(R3.property, propertyBindings).toStmt());
updateStatements.push(chainedInstruction(R3.hostProperty, propertyBindings).toStmt());
}
if (attributeBindings.length > 0) {
@ -802,7 +794,7 @@ function getBindingNameAndInstruction(binding: ParsedProperty):
// compatibility instruction available for this purpose.
instruction = R3.updateSyntheticHostBinding;
} else {
instruction = R3.property;
instruction = R3.hostProperty;
}
}

View File

@ -98,6 +98,7 @@ export {
ɵɵcontentQuery,
ɵɵloadContentQuery,
ɵɵelementEnd,
ɵɵhostProperty,
ɵɵproperty,
ɵɵpropertyInterpolate,
ɵɵpropertyInterpolate1,

View File

@ -82,6 +82,7 @@ export {
ɵɵprojection,
ɵɵprojectionDef,
ɵɵhostProperty,
ɵɵproperty,
ɵɵpropertyInterpolate,
ɵɵpropertyInterpolate1,

View File

@ -49,3 +49,4 @@ export * from './text';
export * from './text_interpolation';
export * from './class_map_interpolation';
export * from './style_prop_interpolation';
export * from './host_property';

View File

@ -0,0 +1,73 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* 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 {SanitizerFn} from '../interfaces/sanitization';
import {getLView, getSelectedIndex} from '../state';
import {NO_CHANGE} from '../tokens';
import {bind} from './property';
import {TsickleIssue1009, elementPropertyInternal, loadComponentRenderer} from './shared';
/**
* Update a property on a host element. Only applies to native node properties, not inputs.
*
* Operates on the element selected by index via the {@link select} instruction.
*
* @param propName Name of property. Because it is going to DOM, this is not subject to
* renaming as part of minification.
* @param value New value to write.
* @param sanitizer An optional function used to sanitize the value.
* @returns This function returns itself so that it may be chained
* (e.g. `property('name', ctx.name)('title', ctx.title)`)
*
* @codeGenApi
*/
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);
}
return ɵɵhostProperty;
}
/**
* Updates a synthetic host binding (e.g. `[@foo]`) on a component.
*
* This instruction is for compatibility purposes and is designed to ensure that a
* synthetic host binding (e.g. `@HostBinding('@foo')`) properly gets rendered in
* the component's renderer. Normally all host bindings are evaluated with the parent
* component's renderer, but, in the case of animation @triggers, they need to be
* evaluated with the sub component's renderer (because that's where the animation
* triggers are defined).
*
* Do not use this instruction as a replacement for `elementProperty`. This instruction
* only exists to ensure compatibility with the ViewEngine's host binding behavior.
*
* @param index The index of the element to update in the data array
* @param propName Name of property. Because it is going to DOM, this is not subject to
* renaming as part of minification.
* @param value New value to write.
* @param sanitizer An optional function used to sanitize the value.
*
* @codeGenApi
*/
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);
}
return ɵɵupdateSyntheticHostBinding;
}

View File

@ -12,7 +12,7 @@ import {BINDING_INDEX, LView} from '../interfaces/view';
import {getLView, getSelectedIndex} from '../state';
import {NO_CHANGE} from '../tokens';
import {TsickleIssue1009, elementPropertyInternal, loadComponentRenderer, storeBindingMetadata} from './shared';
import {TsickleIssue1009, elementPropertyInternal, storeBindingMetadata} from './shared';
/**
@ -28,22 +28,19 @@ import {TsickleIssue1009, elementPropertyInternal, loadComponentRenderer, storeB
* renaming as part of minification.
* @param value New value to write.
* @param sanitizer An optional function used to sanitize the value.
* @param nativeOnly Whether or not we should only set native properties and skip input check
* (this is necessary for host property bindings)
* @returns This function returns itself so that it may be chained
* (e.g. `property('name', ctx.name)('title', ctx.title)`)
*
* @codeGenApi
*/
export function ɵɵproperty<T>(
propName: string, value: T, sanitizer?: SanitizerFn | null,
nativeOnly?: boolean): TsickleIssue1009 {
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, nativeOnly);
elementPropertyInternal(index, propName, bindReconciledValue, sanitizer);
}
return ɵɵproperty;
}
@ -59,39 +56,3 @@ export function bind<T>(lView: LView, value: T): T|NO_CHANGE {
storeBindingMetadata(lView);
return bindingUpdated(lView, bindingIndex, value) ? value : NO_CHANGE;
}
/**
* Updates a synthetic host binding (e.g. `[@foo]`) on a component.
*
* This instruction is for compatibility purposes and is designed to ensure that a
* synthetic host binding (e.g. `@HostBinding('@foo')`) properly gets rendered in
* the component's renderer. Normally all host bindings are evaluated with the parent
* component's renderer, but, in the case of animation @triggers, they need to be
* evaluated with the sub component's renderer (because that's where the animation
* triggers are defined).
*
* Do not use this instruction as a replacement for `elementProperty`. This instruction
* only exists to ensure compatibility with the ViewEngine's host binding behavior.
*
* @param index The index of the element to update in the data array
* @param propName Name of property. Because it is going to DOM, this is not subject to
* renaming as part of minification.
* @param value New value to write.
* @param sanitizer An optional function used to sanitize the value.
* @param nativeOnly Whether or not we should only set native properties and skip input check
* (this is necessary for host property bindings)
*
* @codeGenApi
*/
export function ɵɵupdateSyntheticHostBinding<T>(
propName: string, value: T | NO_CHANGE, sanitizer?: SanitizerFn | null,
nativeOnly?: boolean): 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, nativeOnly, loadComponentRenderer);
}
return ɵɵupdateSyntheticHostBinding;
}

View File

@ -85,6 +85,7 @@ export const angularCoreEnv: {[name: string]: Function} =
'ɵɵpipeBind4': r3.ɵɵpipeBind4,
'ɵɵpipeBindV': r3.ɵɵpipeBindV,
'ɵɵprojectionDef': r3.ɵɵprojectionDef,
'ɵɵhostProperty': r3.ɵɵhostProperty,
'ɵɵproperty': r3.ɵɵproperty,
'ɵɵpropertyInterpolate': r3.ɵɵpropertyInterpolate,
'ɵɵpropertyInterpolate1': r3.ɵɵpropertyInterpolate1,

View File

@ -8,7 +8,7 @@
import {RendererType2} from '../../src/render/api';
import {getLContext} from '../../src/render3/context_discovery';
import {AttributeMarker, ɵɵattribute, ɵɵdefineComponent, ɵɵdefineDirective, ɵɵproperty} from '../../src/render3/index';
import {AttributeMarker, ɵɵattribute, ɵɵdefineComponent, ɵɵdefineDirective, ɵɵhostProperty, ɵɵproperty} from '../../src/render3/index';
import {ɵɵallocHostVars, ɵɵcontainer, ɵɵcontainerRefreshEnd, ɵɵcontainerRefreshStart, ɵɵelement, ɵɵelementEnd, ɵɵelementStart, ɵɵembeddedViewEnd, ɵɵembeddedViewStart, ɵɵprojection, ɵɵprojectionDef, ɵɵselect, ɵɵstyling, ɵɵstylingApply, ɵɵtemplate, ɵɵtext, ɵɵtextBinding} from '../../src/render3/instructions/all';
import {MONKEY_PATCH_KEY_NAME} from '../../src/render3/interfaces/context';
import {RenderFlags} from '../../src/render3/interfaces/definition';
@ -17,6 +17,7 @@ import {StylingIndex} from '../../src/render3/interfaces/styling';
import {CONTEXT, HEADER_OFFSET} from '../../src/render3/interfaces/view';
import {ɵɵsanitizeUrl} from '../../src/sanitization/sanitization';
import {Sanitizer, SecurityContext} from '../../src/sanitization/security';
import {NgIf} from './common_with_def';
import {ComponentFixture, MockRendererFactory, renderToHtml} from './render_util';
@ -1136,7 +1137,7 @@ describe('sanitization', () => {
}
if (rf & RenderFlags.Update) {
ɵɵselect(elementIndex);
ɵɵproperty('cite', ctx.cite, ɵɵsanitizeUrl, true);
ɵɵhostProperty('cite', ctx.cite, ɵɵsanitizeUrl);
}
}
});

View File

@ -877,6 +877,8 @@ export declare function ɵɵgetFactoryOf<T>(type: Type<any>): FactoryFn<T> | nul
export declare function ɵɵgetInheritedFactory<T>(type: Type<any>): (type: Type<T>) => T;
export declare function ɵɵhostProperty<T>(propName: string, value: T, sanitizer?: SanitizerFn | null): TsickleIssue1009;
export declare function ɵɵi18n(index: number, message: string, subTemplateIndex?: number): void;
export declare function ɵɵi18nApply(index: number): void;
@ -956,7 +958,7 @@ export declare function ɵɵprojection(nodeIndex: number, selectorIndex?: number
export declare function ɵɵprojectionDef(projectionSlots?: ProjectionSlots): void;
export declare function ɵɵproperty<T>(propName: string, value: T, sanitizer?: SanitizerFn | null, nativeOnly?: boolean): TsickleIssue1009;
export declare function ɵɵproperty<T>(propName: string, value: T, sanitizer?: SanitizerFn | null): TsickleIssue1009;
export declare function ɵɵpropertyInterpolate(propName: string, v0: any, sanitizer?: SanitizerFn): TsickleIssue1009;
@ -1109,7 +1111,7 @@ export declare function ɵɵtextInterpolate8(prefix: string, v0: any, i0: string
export declare function ɵɵtextInterpolateV(values: any[]): TsickleIssue1009;
export declare function ɵɵupdateSyntheticHostBinding<T>(propName: string, value: T | NO_CHANGE, sanitizer?: SanitizerFn | null, nativeOnly?: boolean): TsickleIssue1009;
export declare function ɵɵupdateSyntheticHostBinding<T>(propName: string, value: T | NO_CHANGE, sanitizer?: SanitizerFn | null): TsickleIssue1009;
export declare function ɵɵviewQuery<T>(predicate: Type<any> | string[], descend: boolean, read: any): QueryList<T>;