From 2c75ba989d72b7c04e336bcb4ccfc08fb8981c6b Mon Sep 17 00:00:00 2001 From: vakrilov Date: Thu, 21 May 2015 18:00:53 +0300 Subject: [PATCH 1/4] Caching class names --- utils/types.d.ts | 22 ++++++++++++++++ utils/types.ts | 66 +++++++++++++++++++++++++++++++++++++----------- 2 files changed, 73 insertions(+), 15 deletions(-) diff --git a/utils/types.d.ts b/utils/types.d.ts index 49e7b9206..bf84d102b 100644 --- a/utils/types.d.ts +++ b/utils/types.d.ts @@ -61,4 +61,26 @@ * Return an array of strings with the name of all classes. */ export function getBaseClasses(object): Array; + + /** + * A function that gets the ClassInfo for an object. + * @param object The object for which the ClassInfo will be get. + * Returns a ClassInfo for the object. + */ + export function getClassInfo(object: Object): ClassInfo; + + /** + * A Class holding information about a class + */ + export class ClassInfo { + /** + * Gets the name of the class. + */ + name: string; + + /** + * Gets the ClassInfo for the base class of the current info. + */ + baseClassInfo: ClassInfo; + } } \ No newline at end of file diff --git a/utils/types.ts b/utils/types.ts index 16a1d0c06..94bd41367 100644 --- a/utils/types.ts +++ b/utils/types.ts @@ -31,27 +31,63 @@ export function verifyCallback(value: any) { } } +var classInfosMap = new Map(); var funcNameRegex = /function (.{1,})\(/; -export function getClass(object): string { - var results = (funcNameRegex).exec((object).constructor.toString()); - return (results && results.length > 1) ? results[1] : ""; +export function getClass(object: Object): string { + return getClassInfo(object).name; +} + +export function getClassInfo(object: Object): ClassInfo { + var constructor = object.constructor; + + var result = classInfosMap.get(constructor); + if (!result) { + result = new ClassInfo(constructor); + classInfosMap.set(constructor, result); + } + + return result; } export function getBaseClasses(object): Array { - var baseProto = object.__proto__; var result = []; - result.push(getClass(object)); - - while (baseProto !== Object.prototype) { - var baseProtoString = baseProto.toString(); - // while extending some classes for platform specific versions results in duplicate class types in hierarchy - if (result.indexOf(baseProtoString) === -1) { - result.push(baseProtoString); - } - baseProto = baseProto.__proto__; + var info = getClassInfo(object); + while (info) { + result.push(info.name); + info = info.baseClassInfo; + } + return result; +} + +export class ClassInfo { + private _typeCosntructor: Function; + private _name: string; + private _baseClassInfo: ClassInfo; + + constructor(typeCosntructor: Function) { + this._typeCosntructor = typeCosntructor; } - result.push("Object"); + get name(): string { + if (!this._name) { + var results = (funcNameRegex).exec(this._typeCosntructor.toString()); + this._name = (results && results.length > 1) ? results[1] : ""; + } - return result; + return this._name; + } + + get baseClassInfo(): ClassInfo { + if (isUndefined(this._baseClassInfo)) { + var constructorProto = this._typeCosntructor.prototype; + if (constructorProto.__proto__) { + this._baseClassInfo = getClassInfo(constructorProto.__proto__); + } + else { + this._baseClassInfo = null; + } + } + + return this._baseClassInfo; + } } \ No newline at end of file From aeebe2df7f92dbc4137198ebe3c7c7ab9916d617 Mon Sep 17 00:00:00 2001 From: vakrilov Date: Fri, 22 May 2015 14:51:40 +0300 Subject: [PATCH 2/4] Use ClassInfo in dependency observable --- ui/core/dependency-observable.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/core/dependency-observable.ts b/ui/core/dependency-observable.ts index 71eac3778..22e50c72c 100644 --- a/ui/core/dependency-observable.ts +++ b/ui/core/dependency-observable.ts @@ -24,16 +24,16 @@ function validateRegisterParameters(name: string, ownerType: string) { } function getPropertyByNameAndType(name: string, owner: any): Property { - var baseClasses = types.getBaseClasses(owner); - var i; var result; var key; - for (i = 0; i < baseClasses.length; i++) { - key = generatePropertyKey(name, baseClasses[i]); + var classInfo = types.getClassInfo(owner); + while (classInfo) { + key = generatePropertyKey(name, classInfo.name); result = propertyFromKey[key]; if (result) { break; } + classInfo = classInfo.baseClassInfo; } return result; } From 27b38da248c6f0b3dde5e560a93c2e846f43424f Mon Sep 17 00:00:00 2001 From: vakrilov Date: Fri, 22 May 2015 18:07:36 +0300 Subject: [PATCH 3/4] Style handlers caching revised --- ui/styling/style.ts | 69 +++++++++++++++++++++++++-------------------- utils/types.ts | 20 +++++++++---- 2 files changed, 52 insertions(+), 37 deletions(-) diff --git a/ui/styling/style.ts b/ui/styling/style.ts index 13d6b4b9a..466d63b19 100644 --- a/ui/styling/style.ts +++ b/ui/styling/style.ts @@ -13,9 +13,11 @@ import imageSource = require("image-source"); import utils = require("utils/utils"); // key is the property id and value is Dictionary; -var _registeredHandlers = {}; +var _registeredHandlers = Array(); + // key is a className + property id and value is StylePropertyChangedHandler; var _handlersCache = {}; + // classes like Frame that does not need to handle styling properties. var noStylingClasses = {}; @@ -292,14 +294,14 @@ export function registerHandler(property: dependencyObservable.Property, handler: styling.stylers.StylePropertyChangedHandler, className?: string) { var realClassName = className ? className : "default"; - if (_registeredHandlers.hasOwnProperty(property.id + "")) { - _registeredHandlers[property.id][realClassName] = handler; - } - else { - var handlerRecord = {}; - handlerRecord[realClassName] = handler; + + var handlerRecord = _registeredHandlers[property.id]; + if (!handlerRecord) { + handlerRecord = {}; _registeredHandlers[property.id] = handlerRecord; } + + handlerRecord[realClassName] = handler; } export function registerNoStylingClass(className) { @@ -307,33 +309,38 @@ export function registerNoStylingClass(className) { } export function getHandler(property: dependencyObservable.Property, view: view.View): styling.stylers.StylePropertyChangedHandler { - var classNames = types.getBaseClasses(view); - // adding default as last class name if no other class is found default handler will be used - classNames.push("default"); - if (_handlersCache.hasOwnProperty(classNames[0] + property.id)) { - return _handlersCache[classNames[0] + property.id]; + return getHandlerInternal(property.id, types.getClassInfo(view)); +} + +function getHandlerInternal(propertyId: number, classInfo: types.ClassInfo): styling.stylers.StylePropertyChangedHandler { + var className = classInfo ? classInfo.name : "default"; + var handlerKey = className + propertyId; + + // try the cache first + var result = _handlersCache[handlerKey]; + if (types.isDefined(result)) { + return result; + } + + var propertyHandlers = _registeredHandlers[propertyId]; + if (noStylingClasses.hasOwnProperty(className) || !propertyHandlers) { + // Reached 'no-styling' class or no property handlers are registered for this proeprtyID + result = null; + } + else if (propertyHandlers.hasOwnProperty(className)) { + // Found handler for this class + result = propertyHandlers[className]; + } + else if (classInfo) { + // Check the base class + result = getHandlerInternal(propertyId, classInfo.baseClassInfo); } else { - var i; - var propertyHandlers; - var handler; - propertyHandlers = _registeredHandlers[property.id]; - for (i = 0; i < classNames.length; i++) { - if (propertyHandlers) { - var loopClassName = classNames[i]; - if (noStylingClasses.hasOwnProperty(loopClassName)) { - _handlersCache[loopClassName + property.id] = null; - return null; - } - if (propertyHandlers.hasOwnProperty(loopClassName)) { - handler = propertyHandlers[loopClassName]; - _handlersCache[loopClassName + property.id] = handler; - return handler; - } - } - } + result = null; } - return null; + + _handlersCache[handlerKey] = result; + return result; } // Property registration diff --git a/utils/types.ts b/utils/types.ts index 94bd41367..f37dbf91a 100644 --- a/utils/types.ts +++ b/utils/types.ts @@ -79,15 +79,23 @@ export class ClassInfo { get baseClassInfo(): ClassInfo { if (isUndefined(this._baseClassInfo)) { - var constructorProto = this._typeCosntructor.prototype; - if (constructorProto.__proto__) { - this._baseClassInfo = getClassInfo(constructorProto.__proto__); - } - else { - this._baseClassInfo = null; + this._baseClassInfo = ClassInfo._getBase(this); + + // While extending some classes for platform specific versions results in duplicate class types in hierarchy. + if (this._baseClassInfo && this._baseClassInfo.name === this.name) { + this._baseClassInfo = ClassInfo._getBase(this._baseClassInfo); } } return this._baseClassInfo; } + + private static _getBase(info: ClassInfo): ClassInfo { + var result = null; + var constructorProto = info._typeCosntructor.prototype; + if (constructorProto.__proto__) { + result = getClassInfo(constructorProto.__proto__); + } + return result; + } } \ No newline at end of file From 3b8e27ef35066aa2734304237a6d6a99dadc8233 Mon Sep 17 00:00:00 2001 From: vakrilov Date: Fri, 22 May 2015 18:07:47 +0300 Subject: [PATCH 4/4] Performance test pages --- CrossPlatformModules.csproj | 10 ++++ apps/tests/pages/performance-test/start.ts | 12 +++++ apps/tests/pages/performance-test/start.xml | 7 +++ apps/tests/pages/performance-test/test-big.ts | 7 +++ .../tests/pages/performance-test/test-big.xml | 54 +++++++++++++++++++ .../pages/performance-test/test-small.ts | 7 +++ .../pages/performance-test/test-small.xml | 12 +++++ 7 files changed, 109 insertions(+) create mode 100644 apps/tests/pages/performance-test/start.ts create mode 100644 apps/tests/pages/performance-test/start.xml create mode 100644 apps/tests/pages/performance-test/test-big.ts create mode 100644 apps/tests/pages/performance-test/test-big.xml create mode 100644 apps/tests/pages/performance-test/test-small.ts create mode 100644 apps/tests/pages/performance-test/test-small.xml diff --git a/CrossPlatformModules.csproj b/CrossPlatformModules.csproj index 8bafda30a..6a70101c7 100644 --- a/CrossPlatformModules.csproj +++ b/CrossPlatformModules.csproj @@ -128,6 +128,9 @@ + + + @@ -609,6 +612,13 @@ + + Designer + + + Designer + + diff --git a/apps/tests/pages/performance-test/start.ts b/apps/tests/pages/performance-test/start.ts new file mode 100644 index 000000000..1dcbec2cb --- /dev/null +++ b/apps/tests/pages/performance-test/start.ts @@ -0,0 +1,12 @@ +import frame = require("ui/frame"); +import observable = require("data/observable"); + +declare function __startCPUProfiler(name: string); + +export function navigate(args: observable.EventData) { + var tag = "" + args.object.get("tag"); + __startCPUProfiler("xml-performance-" + tag); + frame.topmost().navigate({ + moduleName: tag, + }); +} \ No newline at end of file diff --git a/apps/tests/pages/performance-test/start.xml b/apps/tests/pages/performance-test/start.xml new file mode 100644 index 000000000..c2facd58a --- /dev/null +++ b/apps/tests/pages/performance-test/start.xml @@ -0,0 +1,7 @@ + + +