Pawel Kozlowski c34c223122 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
2019-03-25 09:34:13 -07:00

85 lines
2.2 KiB
TypeScript

/**
* @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
*/
// The functions in this file verify that the assumptions we are making
// about state in an instruction are correct before implementing any logic.
// They are meant only to be called in dev mode as sanity checks.
export function assertNumber(actual: any, msg: string) {
if (typeof actual != 'number') {
throwError(msg);
}
}
export function assertEqual<T>(actual: T, expected: T, msg: string) {
if (actual != expected) {
throwError(msg);
}
}
export function assertNotEqual<T>(actual: T, expected: T, msg: string) {
if (actual == expected) {
throwError(msg);
}
}
export function assertSame<T>(actual: T, expected: T, msg: string) {
if (actual !== expected) {
throwError(msg);
}
}
export function assertNotSame<T>(actual: T, expected: T, msg: string) {
if (actual === expected) {
throwError(msg);
}
}
export function assertLessThan<T>(actual: T, expected: T, msg: string) {
if (actual >= expected) {
throwError(msg);
}
}
export function assertGreaterThan<T>(actual: T, expected: T, msg: string) {
if (actual <= expected) {
throwError(msg);
}
}
export function assertNotDefined<T>(actual: T, msg: string) {
if (actual != null) {
throwError(msg);
}
}
export function assertDefined<T>(actual: T, msg: string) {
if (actual == null) {
throwError(msg);
}
}
export function throwError(msg: string): never {
// tslint:disable-next-line
debugger; // Left intentionally for better debugger experience.
throw new Error(`ASSERTION ERROR: ${msg}`);
}
export function assertDomNode(node: any) {
// If we're in a worker, `Node` will not be defined.
assertEqual(
(typeof Node !== 'undefined' && node instanceof Node) ||
(typeof node === 'object' && node.constructor.name === 'WebWorkerRenderNode'),
true, 'The provided value must be an instance of a DOM Node');
}
export function assertDataInRange(arr: any[], index: number) {
assertLessThan(index, arr ? arr.length : 0, 'index expected to be a valid data index');
}