Skip to content

Commit

Permalink
feat(ivy): memoize array literals in render3 (#21973)
Browse files Browse the repository at this point in the history
PR Close #21973
  • Loading branch information
kara authored and alxhub committed Feb 6, 2018
1 parent 7e51e52 commit 4d62be6
Show file tree
Hide file tree
Showing 6 changed files with 618 additions and 1 deletion.
11 changes: 11 additions & 0 deletions packages/core/src/render3/index.ts
Expand Up @@ -78,6 +78,17 @@ export {
query as Q,
queryRefresh as qR,
} from './query';
export {
objectLiteral1 as o1,
objectLiteral2 as o2,
objectLiteral3 as o3,
objectLiteral4 as o4,
objectLiteral5 as o5,
objectLiteral6 as o6,
objectLiteral7 as o7,
objectLiteral8 as o8,
} from './object_literal';


// clang-format on

Expand Down
7 changes: 6 additions & 1 deletion packages/core/src/render3/instructions.ts
Expand Up @@ -494,7 +494,8 @@ export function createTView(): TView {
contentCheckHooks: null,
viewHooks: null,
viewCheckHooks: null,
destroyHooks: null
destroyHooks: null,
objectLiterals: null
};
}

Expand Down Expand Up @@ -1754,6 +1755,10 @@ export function getRenderer(): Renderer3 {
return renderer;
}

export function getTView(): TView {
return currentView.tView;
}

export function getDirectiveInstance<T>(instanceOrArray: T | [T]): T {
// Directives with content queries store an array in data[directiveIndex]
// with the instance as the first index
Expand Down
3 changes: 3 additions & 0 deletions packages/core/src/render3/interfaces/view.ts
Expand Up @@ -259,6 +259,9 @@ export interface TView {
* Odd indices: Hook function
*/
destroyHooks: HookData|null;

/** Contains copies of object literals that were passed as bindings in this view. */
objectLiterals: any[]|null;
}

/**
Expand Down
275 changes: 275 additions & 0 deletions packages/core/src/render3/object_literal.ts
@@ -0,0 +1,275 @@
/**
* @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 {assertEqual} from './assert';
import {NO_CHANGE, bind, getTView} from './instructions';


/**
* Updates an expression in an object literal if the expression has changed.
* Used in objectLiteral instructions.
*
* @param obj Object to update
* @param key Key to set in object
* @param exp Expression to set at key
* @returns Whether or not there has been a change
*/
function updateBinding(obj: any, key: string | number, exp: any): boolean {
if (bind(exp) !== NO_CHANGE) {
obj[key] = exp;
return true;
}
return false;
}

/** Updates two expressions in an object literal if they have changed. */
function updateBinding2(obj: any, key1: number, exp1: any, key2: number, exp2: any): boolean {
let different = updateBinding(obj, key1, exp1);
return updateBinding(obj, key2, exp2) || different;
}

/** Updates four expressions in an object literal if they have changed. */
function updateBinding4(
obj: any, key1: number, exp1: any, key2: number, exp2: any, key3: number, exp3: any,
key4: number, exp4: any): boolean {
let different = updateBinding2(obj, key1, exp1, key2, exp2);
return updateBinding2(obj, key3, exp3, key4, exp4) || different;
}

/**
* Gets a blueprint of an object or array if one has already been saved, or copies the
* object and saves it for the next change detection run if it hasn't.
*/
function getMutableBlueprint(index: number, obj: any): any {
const tView = getTView();
const objectLiterals = tView.objectLiterals;
if (objectLiterals && index < objectLiterals.length) {
return objectLiterals[index];
} else {
ngDevMode && objectLiterals && assertEqual(index, objectLiterals.length, 'index');
return (objectLiterals || (tView.objectLiterals = []))[index] = copyObject(obj);
}
}

/** Copies an object or array */
function copyObject(obj: any): any {
return Array.isArray(obj) ? obj.slice() : {...obj};
}

/**
* Updates the expression in the given object if it has changed and returns a copy of the object.
* Or if the expression hasn't changed, returns NO_CHANGE.
*
* @param objIndex Index of object blueprint in objectLiterals
* @param obj Object to update
* @param key Key to set in object
* @param exp Expression to set at key
* @returns A copy of the object or NO_CHANGE
*/
export function objectLiteral1(objIndex: number, obj: any, key: string | number, exp: any): any {
obj = getMutableBlueprint(objIndex, obj);
if (bind(exp) === NO_CHANGE) {
return NO_CHANGE;
} else {
obj[key] = exp;
// Must copy to change identity when binding changes
return copyObject(obj);
}
}

/**
* Updates the expressions in the given object if they have changed and returns a copy of the
* object.
* Or if no expressions have changed, returns NO_CHANGE.
*
* @param objIndex
* @param obj
* @param key1
* @param exp1
* @param key2
* @param exp2
* @returns A copy of the array or NO_CHANGE
*/
export function objectLiteral2(
objIndex: number, obj: any, key1: number, exp1: any, key2: number, exp2: any): any {
obj = getMutableBlueprint(objIndex, obj);
return updateBinding2(obj, key1, exp1, key2, exp2) ? copyObject(obj) : NO_CHANGE;
}

/**
* Updates the expressions in the given object if they have changed and returns a copy of the
* object.
* Or if no expressions have changed, returns NO_CHANGE.
*
* @param objIndex
* @param obj
* @param key1
* @param exp1
* @param key2
* @param exp2
* @param key3
* @param exp3
* @returns A copy of the object or NO_CHANGE
*/
export function objectLiteral3(
objIndex: number, obj: any, key1: number, exp1: any, key2: number, exp2: any, key3: number,
exp3: any): any {
obj = getMutableBlueprint(objIndex, obj);
let different = updateBinding2(obj, key1, exp1, key2, exp2);
return updateBinding(obj, key3, exp3) || different ? copyObject(obj) : NO_CHANGE;
}

/**
* Updates the expressions in the given object if they have changed and returns a copy of the
* object.
* Or if no expressions have changed, returns NO_CHANGE.
*
* @param objIndex
* @param obj
* @param key1
* @param exp1
* @param key2
* @param exp2
* @param key3
* @param exp3
* @param key4
* @param exp4
* @returns A copy of the object or NO_CHANGE
*/
export function objectLiteral4(
objIndex: number, obj: any, key1: number, exp1: any, key2: number, exp2: any, key3: number,
exp3: any, key4: number, exp4: any): any {
obj = getMutableBlueprint(objIndex, obj);
return updateBinding4(obj, key1, exp1, key2, exp2, key3, exp3, key4, exp4) ? copyObject(obj) :
NO_CHANGE;
}

/**
* Updates the expressions in the given object if they have changed and returns a copy of the
* object.
* Or if no expressions have changed, returns NO_CHANGE.
*
* @param objIndex
* @param obj
* @param key1
* @param exp1
* @param key2
* @param exp2
* @param key3
* @param exp3
* @param key4
* @param exp4
* @param key5
* @param exp5
* @returns A copy of the object or NO_CHANGE
*/
export function objectLiteral5(
objIndex: number, obj: any, key1: number, exp1: any, key2: number, exp2: any, key3: number,
exp3: any, key4: number, exp4: any, key5: number, exp5: any): any {
obj = getMutableBlueprint(objIndex, obj);
let different = updateBinding4(obj, key1, exp1, key2, exp2, key3, exp3, key4, exp4);
return updateBinding(obj, key5, exp5) || different ? copyObject(obj) : NO_CHANGE;
}

/**
* Updates the expressions in the given object if they have changed and returns a copy of the
* object.
* Or if no expressions have changed, returns NO_CHANGE.
*
* @param objIndex
* @param obj
* @param key1
* @param exp1
* @param key2
* @param exp2
* @param key3
* @param exp3
* @param key4
* @param exp4
* @param key5
* @param exp5
* @param key6
* @param exp6
* @returns A copy of the object or NO_CHANGE
*/
export function objectLiteral6(
objIndex: number, obj: any, key1: number, exp1: any, key2: number, exp2: any, key3: number,
exp3: any, key4: number, exp4: any, key5: number, exp5: any, key6: number, exp6: any): any {
obj = getMutableBlueprint(objIndex, obj);
let different = updateBinding4(obj, key1, exp1, key2, exp2, key3, exp3, key4, exp4);
return updateBinding2(obj, key5, exp5, key6, exp6) || different ? copyObject(obj) : NO_CHANGE;
}

/**
* Updates the expressions in the given object if they have changed and returns a copy of the
* object.
* Or if no expressions have changed, returns NO_CHANGE.
*
* @param objIndex
* @param obj
* @param key1
* @param exp1
* @param key2
* @param exp2
* @param key3
* @param exp3
* @param key4
* @param exp4
* @param key5
* @param exp5
* @param key6
* @param exp6
* @param key7
* @param exp7
* @returns A copy of the object or NO_CHANGE
*/
export function objectLiteral7(
objIndex: number, obj: any, key1: number, exp1: any, key2: number, exp2: any, key3: number,
exp3: any, key4: number, exp4: any, key5: number, exp5: any, key6: number, exp6: any,
key7: number, exp7: any): any {
obj = getMutableBlueprint(objIndex, obj);
let different = updateBinding4(obj, key1, exp1, key2, exp2, key3, exp3, key4, exp4);
different = updateBinding2(obj, key5, exp5, key6, exp6) || different;
return updateBinding(obj, key7, exp7) || different ? copyObject(obj) : NO_CHANGE;
}

/**
* Updates the expressions in the given object if they have changed and returns a copy of the
* object.
* Or if no expressions have changed, returns NO_CHANGE.
*
* @param objIndex
* @param obj
* @param key1
* @param exp1
* @param key2
* @param exp2
* @param key3
* @param exp3
* @param key4
* @param exp4
* @param key5
* @param exp5
* @param key6
* @param exp6
* @param key7
* @param exp7
* @param key8
* @param exp8
* @returns A copy of the object or NO_CHANGE
*/
export function objectLiteral8(
objIndex: number, obj: any, key1: number, exp1: any, key2: number, exp2: any, key3: number,
exp3: any, key4: number, exp4: any, key5: number, exp5: any, key6: number, exp6: any,
key7: number, exp7: any, key8: number, exp8: any): any {
obj = getMutableBlueprint(objIndex, obj);
let different = updateBinding4(obj, key1, exp1, key2, exp2, key3, exp3, key4, exp4);
return updateBinding4(obj, key5, exp5, key6, exp6, key7, exp7, key8, exp8) || different ?
copyObject(obj) :
NO_CHANGE;
}

0 comments on commit 4d62be6

Please sign in to comment.