refactor(compiler): generate less code for bindings to DOM elements

Detailed changes:
- remove `UNINITIALIZED`, initialize change detection fields with `undefined`.
  * we use `view.numberOfChecks === 0` now everywhere
    as indicator whether we are in the first change detection cycle
    (previously we used this only in a couple of places).
  * we keep the initialization itself as change detection get slower without it.
- remove passing around `throwOnChange` in various generated calls,
  and store it on the view as property instead.
- change generated code for bindings to DOM elements as follows:
  Before:
  ```
  var currVal_10 = self.context.bgColor;
  if (jit_checkBinding15(self.throwOnChange,self._expr_10,currVal_10)) {
    self.renderer.setElementStyle(self._el_0,'backgroundColor',((self.viewUtils.sanitizer.sanitize(jit_21,currVal_10) == null)? null: self.viewUtils.sanitizer.sanitize(jit_21,currVal_10).toString()));
    self._expr_10 = currVal_10;
  }
  var currVal_11 = jit_inlineInterpolate16(1,' ',self.context.data.value,' ');
  if (jit_checkBinding15(self.throwOnChange,self._expr_11,currVal_11)) {
    self.renderer.setText(self._text_1,currVal_11);
    self._expr_11 = currVal_11;
  }
  ```,
  After:
  ```
  var currVal_10 = self.context.bgColor;
  jit_checkRenderStyle14(self,self._el_0,'backgroundColor',null,self._expr_10,self._expr_10=currVal_10,false,jit_21);
  var currVal_11 = jit_inlineInterpolate15(1,' ',self.context.data.value,' ');
  jit_checkRenderText16(self,self._text_1,self._expr_11,self._expr_11=currVal_11,false);
  ```

Performance impact:
- None seen (checked against internal latency lab)

Part of #13651
This commit is contained in:
Tobias Bosch
2016-12-29 15:03:55 -08:00
committed by Igor Minar
parent 8ed92d75b0
commit db49d422f2
23 changed files with 346 additions and 286 deletions

View File

@ -12,7 +12,7 @@ import {IterableDifferFactory, IterableDiffers} from './differs/iterable_differs
import {KeyValueDifferFactory, KeyValueDiffers} from './differs/keyvalue_differs';
export {SimpleChanges} from '../metadata/lifecycle_hooks';
export {SimpleChange, UNINITIALIZED, ValueUnwrapper, WrappedValue, devModeEqual, looseIdentical} from './change_detection_util';
export {SimpleChange, ValueUnwrapper, WrappedValue, devModeEqual, looseIdentical} from './change_detection_util';
export {ChangeDetectorRef} from './change_detector_ref';
export {ChangeDetectionStrategy, ChangeDetectorStatus, isDefaultChangeDetectionStrategy} from './constants';
export {CollectionChangeRecord, DefaultIterableDifferFactory} from './differs/default_iterable_differ';

View File

@ -11,10 +11,6 @@ import {isPrimitive, looseIdentical} from '../facade/lang';
export {looseIdentical} from '../facade/lang';
export const UNINITIALIZED = {
toString: () => 'CD_INIT_VALUE'
};
export function devModeEqual(a: any, b: any): boolean {
if (isListLikeIterable(a) && isListLikeIterable(b)) {
return areIterablesEqual(a, b, devModeEqual);
@ -75,10 +71,15 @@ export class ValueUnwrapper {
* @stable
*/
export class SimpleChange {
constructor(public previousValue: any, public currentValue: any) {}
constructor(
public previousValue: any, public currentValue: any, _isFirstChange: boolean = false) {
// Store this in a non declared field
// to prevent a breaking change (users might have `implement`ed SimpleChange before)
(<any>this)._firstChange = _isFirstChange;
}
/**
* Check whether the new value is the first value assigned.
*/
isFirstChange(): boolean { return this.previousValue === UNINITIALIZED; }
isFirstChange(): boolean { return (<any>this)._firstChange; }
}

View File

@ -72,7 +72,6 @@ export const __core_private__: {
StaticNodeDebugInfo: typeof debug_context.StaticNodeDebugInfo,
_StaticNodeDebugInfo?: debug_context.StaticNodeDebugInfo,
devModeEqual: typeof change_detection_util.devModeEqual,
UNINITIALIZED: typeof change_detection_util.UNINITIALIZED,
ValueUnwrapper: typeof change_detection_util.ValueUnwrapper,
_ValueUnwrapper?: change_detection_util.ValueUnwrapper,
RenderDebugInfo: typeof api.RenderDebugInfo,
@ -130,7 +129,6 @@ export const __core_private__: {
DebugContext: debug_context.DebugContext,
StaticNodeDebugInfo: debug_context.StaticNodeDebugInfo,
devModeEqual: change_detection_util.devModeEqual,
UNINITIALIZED: change_detection_util.UNINITIALIZED,
ValueUnwrapper: change_detection_util.ValueUnwrapper,
RenderDebugInfo: api.RenderDebugInfo,
TemplateRef_: template_ref.TemplateRef_,

View File

@ -6,7 +6,6 @@
* found in the LICENSE file at https://angular.io/license
*/
import {UNINITIALIZED} from '../change_detection/change_detection_util';
import {BaseError, WrappedError} from '../facade/errors';
import {DebugContext} from './debug_context';
@ -45,10 +44,10 @@ import {DebugContext} from './debug_context';
* @stable
*/
export class ExpressionChangedAfterItHasBeenCheckedError extends BaseError {
constructor(oldValue: any, currValue: any) {
constructor(oldValue: any, currValue: any, isFirstCheck: boolean) {
let msg =
`Expression has changed after it was checked. Previous value: '${oldValue}'. Current value: '${currValue}'.`;
if (oldValue === UNINITIALIZED) {
if (isFirstCheck) {
msg +=
` It seems like the view has been created after its parent and its children have been dirty checked.` +
` Has it been created in a change detection hook ?`;

View File

@ -47,6 +47,7 @@ export abstract class AppView<T> {
appRef: ApplicationRef;
numberOfChecks: number = 0;
throwOnChange: boolean = false;
renderer: Renderer;
@ -326,7 +327,8 @@ export abstract class AppView<T> {
if (this.cdMode === ChangeDetectorStatus.Destroyed) {
this.throwDestroyedError('detectChanges');
}
this.detectChangesInternal(throwOnChange);
this.throwOnChange = throwOnChange;
this.detectChangesInternal();
if (this.cdMode === ChangeDetectorStatus.CheckOnce) this.cdMode = ChangeDetectorStatus.Checked;
this.numberOfChecks++;
@ -336,7 +338,7 @@ export abstract class AppView<T> {
/**
* Overwritten by implementations
*/
detectChangesInternal(throwOnChange: boolean): void {}
detectChangesInternal(): void {}
markAsCheckOnce(): void { this.cdMode = ChangeDetectorStatus.CheckOnce; }

View File

@ -8,12 +8,11 @@
import {AnimationQueue} from '../animation/animation_queue';
import {SimpleChange, devModeEqual} from '../change_detection/change_detection';
import {UNINITIALIZED} from '../change_detection/change_detection_util';
import {Inject, Injectable} from '../di';
import {isPresent, looseIdentical} from '../facade/lang';
import {ViewEncapsulation} from '../metadata/view';
import {RenderComponentType, RenderDebugInfo, Renderer, RootRenderer} from '../render/api';
import {Sanitizer} from '../security';
import {Sanitizer, SecurityContext} from '../security';
import {Type} from '../type';
import {VERSION} from '../version';
import {NgZone} from '../zone/ng_zone';
@ -102,14 +101,77 @@ function _toStringWithNull(v: any): string {
return v != null ? v.toString() : '';
}
export function checkBinding(throwOnChange: boolean, oldValue: any, newValue: any): boolean {
if (throwOnChange) {
if (!devModeEqual(oldValue, newValue)) {
throw new ExpressionChangedAfterItHasBeenCheckedError(oldValue, newValue);
export function checkBinding(
view: AppView<any>, oldValue: any, newValue: any, forceUpdate: boolean): boolean {
const isFirstCheck = view.numberOfChecks === 0;
if (view.throwOnChange) {
if (isFirstCheck || !devModeEqual(oldValue, newValue)) {
throw new ExpressionChangedAfterItHasBeenCheckedError(oldValue, newValue, isFirstCheck);
}
return false;
} else {
return !looseIdentical(oldValue, newValue);
return isFirstCheck || forceUpdate || !looseIdentical(oldValue, newValue);
}
}
export function checkBindingChange(
view: AppView<any>, oldValue: any, newValue: any, forceUpdate: boolean): SimpleChange {
if (checkBinding(view, oldValue, newValue, forceUpdate)) {
return new SimpleChange(oldValue, newValue, view.numberOfChecks === 0);
}
}
export function checkRenderText(
view: AppView<any>, renderElement: any, oldValue: any, newValue: any, forceUpdate: boolean) {
if (checkBinding(view, oldValue, newValue, forceUpdate)) {
view.renderer.setText(renderElement, newValue);
}
}
export function checkRenderProperty(
view: AppView<any>, renderElement: any, propName: string, oldValue: any, newValue: any,
forceUpdate: boolean, securityContext: SecurityContext) {
if (checkBinding(view, oldValue, newValue, forceUpdate)) {
let renderValue =
securityContext ? view.viewUtils.sanitizer.sanitize(securityContext, newValue) : newValue;
view.renderer.setElementProperty(renderElement, propName, renderValue);
}
}
export function checkRenderAttribute(
view: AppView<any>, renderElement: any, attrName: string, oldValue: any, newValue: any,
forceUpdate: boolean, securityContext: SecurityContext) {
if (checkBinding(view, oldValue, newValue, forceUpdate)) {
let renderValue =
securityContext ? view.viewUtils.sanitizer.sanitize(securityContext, newValue) : newValue;
renderValue = renderValue != null ? renderValue.toString() : null;
view.renderer.setElementAttribute(renderElement, attrName, renderValue);
}
}
export function checkRenderClass(
view: AppView<any>, renderElement: any, className: string, oldValue: any, newValue: any,
forceUpdate: boolean) {
if (checkBinding(view, oldValue, newValue, forceUpdate)) {
view.renderer.setElementClass(renderElement, className, newValue);
}
}
export function checkRenderStyle(
view: AppView<any>, renderElement: any, styleName: string, unit: string, oldValue: any,
newValue: any, forceUpdate: boolean, securityContext: SecurityContext) {
if (checkBinding(view, oldValue, newValue, forceUpdate)) {
let renderValue =
securityContext ? view.viewUtils.sanitizer.sanitize(securityContext, newValue) : newValue;
if (renderValue != null) {
renderValue = renderValue.toString();
if (unit != null) {
renderValue = renderValue + unit;
}
} else {
renderValue = null;
}
view.renderer.setElementStyle(renderElement, styleName, renderValue);
}
}
@ -121,11 +183,12 @@ export const EMPTY_ARRAY: any[] = [];
export const EMPTY_MAP = {};
export function pureProxy1<P0, R>(fn: (p0: P0) => R): (p0: P0) => R {
let numberOfChecks = 0;
let result: R;
let v0: any = UNINITIALIZED;
let v0: any;
return (p0) => {
if (!looseIdentical(v0, p0)) {
if (!numberOfChecks++ || !looseIdentical(v0, p0)) {
v0 = p0;
result = fn(p0);
}
@ -134,12 +197,13 @@ export function pureProxy1<P0, R>(fn: (p0: P0) => R): (p0: P0) => R {
}
export function pureProxy2<P0, P1, R>(fn: (p0: P0, p1: P1) => R): (p0: P0, p1: P1) => R {
let numberOfChecks = 0;
let result: R;
let v0: any = UNINITIALIZED;
let v1: any = UNINITIALIZED;
let v0: any;
let v1: any;
return (p0, p1) => {
if (!looseIdentical(v0, p0) || !looseIdentical(v1, p1)) {
if (!numberOfChecks++ || !looseIdentical(v0, p0) || !looseIdentical(v1, p1)) {
v0 = p0;
v1 = p1;
result = fn(p0, p1);
@ -150,13 +214,15 @@ export function pureProxy2<P0, P1, R>(fn: (p0: P0, p1: P1) => R): (p0: P0, p1: P
export function pureProxy3<P0, P1, P2, R>(fn: (p0: P0, p1: P1, p2: P2) => R): (
p0: P0, p1: P1, p2: P2) => R {
let numberOfChecks = 0;
let result: R;
let v0: any = UNINITIALIZED;
let v1: any = UNINITIALIZED;
let v2: any = UNINITIALIZED;
let v0: any;
let v1: any;
let v2: any;
return (p0, p1, p2) => {
if (!looseIdentical(v0, p0) || !looseIdentical(v1, p1) || !looseIdentical(v2, p2)) {
if (!numberOfChecks++ || !looseIdentical(v0, p0) || !looseIdentical(v1, p1) ||
!looseIdentical(v2, p2)) {
v0 = p0;
v1 = p1;
v2 = p2;
@ -168,12 +234,13 @@ export function pureProxy3<P0, P1, P2, R>(fn: (p0: P0, p1: P1, p2: P2) => R): (
export function pureProxy4<P0, P1, P2, P3, R>(fn: (p0: P0, p1: P1, p2: P2, p3: P3) => R): (
p0: P0, p1: P1, p2: P2, p3: P3) => R {
let numberOfChecks = 0;
let result: R;
let v0: any, v1: any, v2: any, v3: any;
v0 = v1 = v2 = v3 = UNINITIALIZED;
v0 = v1 = v2 = v3;
return (p0, p1, p2, p3) => {
if (!looseIdentical(v0, p0) || !looseIdentical(v1, p1) || !looseIdentical(v2, p2) ||
!looseIdentical(v3, p3)) {
if (!numberOfChecks++ || !looseIdentical(v0, p0) || !looseIdentical(v1, p1) ||
!looseIdentical(v2, p2) || !looseIdentical(v3, p3)) {
v0 = p0;
v1 = p1;
v2 = p2;
@ -187,12 +254,13 @@ export function pureProxy4<P0, P1, P2, P3, R>(fn: (p0: P0, p1: P1, p2: P2, p3: P
export function pureProxy5<P0, P1, P2, P3, P4, R>(
fn: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4) => R): (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4) =>
R {
let numberOfChecks = 0;
let result: R;
let v0: any, v1: any, v2: any, v3: any, v4: any;
v0 = v1 = v2 = v3 = v4 = UNINITIALIZED;
v0 = v1 = v2 = v3 = v4;
return (p0, p1, p2, p3, p4) => {
if (!looseIdentical(v0, p0) || !looseIdentical(v1, p1) || !looseIdentical(v2, p2) ||
!looseIdentical(v3, p3) || !looseIdentical(v4, p4)) {
if (!numberOfChecks++ || !looseIdentical(v0, p0) || !looseIdentical(v1, p1) ||
!looseIdentical(v2, p2) || !looseIdentical(v3, p3) || !looseIdentical(v4, p4)) {
v0 = p0;
v1 = p1;
v2 = p2;
@ -208,12 +276,14 @@ export function pureProxy5<P0, P1, P2, P3, P4, R>(
export function pureProxy6<P0, P1, P2, P3, P4, P5, R>(
fn: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) =>
R): (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) => R {
let numberOfChecks = 0;
let result: R;
let v0: any, v1: any, v2: any, v3: any, v4: any, v5: any;
v0 = v1 = v2 = v3 = v4 = v5 = UNINITIALIZED;
v0 = v1 = v2 = v3 = v4 = v5;
return (p0, p1, p2, p3, p4, p5) => {
if (!looseIdentical(v0, p0) || !looseIdentical(v1, p1) || !looseIdentical(v2, p2) ||
!looseIdentical(v3, p3) || !looseIdentical(v4, p4) || !looseIdentical(v5, p5)) {
if (!numberOfChecks++ || !looseIdentical(v0, p0) || !looseIdentical(v1, p1) ||
!looseIdentical(v2, p2) || !looseIdentical(v3, p3) || !looseIdentical(v4, p4) ||
!looseIdentical(v5, p5)) {
v0 = p0;
v1 = p1;
v2 = p2;
@ -229,13 +299,14 @@ export function pureProxy6<P0, P1, P2, P3, P4, P5, R>(
export function pureProxy7<P0, P1, P2, P3, P4, P5, P6, R>(
fn: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6) =>
R): (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6) => R {
let numberOfChecks = 0;
let result: R;
let v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, v6: any;
v0 = v1 = v2 = v3 = v4 = v5 = v6 = UNINITIALIZED;
v0 = v1 = v2 = v3 = v4 = v5 = v6;
return (p0, p1, p2, p3, p4, p5, p6) => {
if (!looseIdentical(v0, p0) || !looseIdentical(v1, p1) || !looseIdentical(v2, p2) ||
!looseIdentical(v3, p3) || !looseIdentical(v4, p4) || !looseIdentical(v5, p5) ||
!looseIdentical(v6, p6)) {
if (!numberOfChecks++ || !looseIdentical(v0, p0) || !looseIdentical(v1, p1) ||
!looseIdentical(v2, p2) || !looseIdentical(v3, p3) || !looseIdentical(v4, p4) ||
!looseIdentical(v5, p5) || !looseIdentical(v6, p6)) {
v0 = p0;
v1 = p1;
v2 = p2;
@ -252,13 +323,14 @@ export function pureProxy7<P0, P1, P2, P3, P4, P5, P6, R>(
export function pureProxy8<P0, P1, P2, P3, P4, P5, P6, P7, R>(
fn: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7) =>
R): (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7) => R {
let numberOfChecks = 0;
let result: R;
let v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, v6: any, v7: any;
v0 = v1 = v2 = v3 = v4 = v5 = v6 = v7 = UNINITIALIZED;
v0 = v1 = v2 = v3 = v4 = v5 = v6 = v7;
return (p0, p1, p2, p3, p4, p5, p6, p7) => {
if (!looseIdentical(v0, p0) || !looseIdentical(v1, p1) || !looseIdentical(v2, p2) ||
!looseIdentical(v3, p3) || !looseIdentical(v4, p4) || !looseIdentical(v5, p5) ||
!looseIdentical(v6, p6) || !looseIdentical(v7, p7)) {
if (!numberOfChecks++ || !looseIdentical(v0, p0) || !looseIdentical(v1, p1) ||
!looseIdentical(v2, p2) || !looseIdentical(v3, p3) || !looseIdentical(v4, p4) ||
!looseIdentical(v5, p5) || !looseIdentical(v6, p6) || !looseIdentical(v7, p7)) {
v0 = p0;
v1 = p1;
v2 = p2;
@ -276,13 +348,15 @@ export function pureProxy8<P0, P1, P2, P3, P4, P5, P6, P7, R>(
export function pureProxy9<P0, P1, P2, P3, P4, P5, P6, P7, P8, R>(
fn: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8) =>
R): (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8) => R {
let numberOfChecks = 0;
let result: R;
let v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, v6: any, v7: any, v8: any;
v0 = v1 = v2 = v3 = v4 = v5 = v6 = v7 = v8 = UNINITIALIZED;
v0 = v1 = v2 = v3 = v4 = v5 = v6 = v7 = v8;
return (p0, p1, p2, p3, p4, p5, p6, p7, p8) => {
if (!looseIdentical(v0, p0) || !looseIdentical(v1, p1) || !looseIdentical(v2, p2) ||
!looseIdentical(v3, p3) || !looseIdentical(v4, p4) || !looseIdentical(v5, p5) ||
!looseIdentical(v6, p6) || !looseIdentical(v7, p7) || !looseIdentical(v8, p8)) {
if (!numberOfChecks++ || !looseIdentical(v0, p0) || !looseIdentical(v1, p1) ||
!looseIdentical(v2, p2) || !looseIdentical(v3, p3) || !looseIdentical(v4, p4) ||
!looseIdentical(v5, p5) || !looseIdentical(v6, p6) || !looseIdentical(v7, p7) ||
!looseIdentical(v8, p8)) {
v0 = p0;
v1 = p1;
v2 = p2;
@ -301,14 +375,15 @@ export function pureProxy9<P0, P1, P2, P3, P4, P5, P6, P7, P8, R>(
export function pureProxy10<P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, R>(
fn: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9) =>
R): (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9) => R {
let numberOfChecks = 0;
let result: R;
let v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, v6: any, v7: any, v8: any, v9: any;
v0 = v1 = v2 = v3 = v4 = v5 = v6 = v7 = v8 = v9 = UNINITIALIZED;
v0 = v1 = v2 = v3 = v4 = v5 = v6 = v7 = v8 = v9;
return (p0, p1, p2, p3, p4, p5, p6, p7, p8, p9) => {
if (!looseIdentical(v0, p0) || !looseIdentical(v1, p1) || !looseIdentical(v2, p2) ||
!looseIdentical(v3, p3) || !looseIdentical(v4, p4) || !looseIdentical(v5, p5) ||
!looseIdentical(v6, p6) || !looseIdentical(v7, p7) || !looseIdentical(v8, p8) ||
!looseIdentical(v9, p9)) {
if (!numberOfChecks++ || !looseIdentical(v0, p0) || !looseIdentical(v1, p1) ||
!looseIdentical(v2, p2) || !looseIdentical(v3, p3) || !looseIdentical(v4, p4) ||
!looseIdentical(v5, p5) || !looseIdentical(v6, p6) || !looseIdentical(v7, p7) ||
!looseIdentical(v8, p8) || !looseIdentical(v9, p9)) {
v0 = p0;
v1 = p1;
v2 = p2;