From c34c223122528f3d8073e6c770e130191db25aef Mon Sep 17 00:00:00 2001 From: Pawel Kozlowski Date: Fri, 22 Mar 2019 14:29:45 +0100 Subject: [PATCH] fix(ivy): avoid type coercion in binding-related instruction asserts (#29470) ivy's bindingUpdated instruction is using the assertNotEqual check to make sure that NO_CHANGE value (of type Object) is not passed as a value to be dirty-checked. In practice it means that any value passed as a binding value would be compared to the NO_CHANGE object. It turns out that the assertNotEqual is using == and given that binding values are of different type and we always compare it to the NO_CHANGE object we were doing lots of type coercion. It resulted in calls to expensive types conversions and calls to Object.toString(). A profiler reported ~15% of the self time spent in the assertNotEqual but it turns out that removing type coercion speeds up Material Chips with input scenario much more (~40ms down to ~20ms). This PR introduces new assert method `assertNotSame` that uses strict equality check. The new assertion is used in binding instructions to compare to NO_CHANGE object reference. PR Close #29470 --- packages/core/src/render3/bindings.ts | 6 +++--- packages/core/src/util/assert.ts | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/core/src/render3/bindings.ts b/packages/core/src/render3/bindings.ts index 400aec275e..0fe18bc2ee 100644 --- a/packages/core/src/render3/bindings.ts +++ b/packages/core/src/render3/bindings.ts @@ -7,7 +7,7 @@ */ import {devModeEqual} from '../change_detection/change_detection_util'; -import {assertDataInRange, assertLessThan, assertNotEqual} from '../util/assert'; +import {assertDataInRange, assertLessThan, assertNotSame} from '../util/assert'; import {throwErrorIfNoChangesMode} from './errors'; import {LView} from './interfaces/view'; import {getCheckNoChangesMode} from './state'; @@ -27,13 +27,13 @@ export function updateBinding(lView: LView, bindingIndex: number, value: any): a export function getBinding(lView: LView, bindingIndex: number): any { ngDevMode && assertDataInRange(lView, bindingIndex); ngDevMode && - assertNotEqual(lView[bindingIndex], NO_CHANGE, 'Stored value should never be NO_CHANGE.'); + assertNotSame(lView[bindingIndex], NO_CHANGE, 'Stored value should never be NO_CHANGE.'); return lView[bindingIndex]; } /** Updates binding if changed, then returns whether it was updated. */ export function bindingUpdated(lView: LView, bindingIndex: number, value: any): boolean { - ngDevMode && assertNotEqual(value, NO_CHANGE, 'Incoming value should never be NO_CHANGE.'); + ngDevMode && assertNotSame(value, NO_CHANGE, 'Incoming value should never be NO_CHANGE.'); ngDevMode && assertLessThan(bindingIndex, lView.length, `Slot should have been initialized to NO_CHANGE`); diff --git a/packages/core/src/util/assert.ts b/packages/core/src/util/assert.ts index ea9ed55542..58b93ffb2f 100644 --- a/packages/core/src/util/assert.ts +++ b/packages/core/src/util/assert.ts @@ -34,6 +34,12 @@ export function assertSame(actual: T, expected: T, msg: string) { } } +export function assertNotSame(actual: T, expected: T, msg: string) { + if (actual === expected) { + throwError(msg); + } +} + export function assertLessThan(actual: T, expected: T, msg: string) { if (actual >= expected) { throwError(msg);