Add timeline traces in the console, let enabling them through the package.json

This commit is contained in:
Panayot Cankov
2017-06-02 16:34:04 +03:00
parent 63633533bc
commit 51b351f5b6
24 changed files with 282 additions and 128 deletions

View File

@ -1,5 +1,5 @@
import { assert, assertEqual, assertFalse, assertTrue, assertThrows } from "../TKUnit";
import { enable, disable, profile, time, start, stop, pause, isRunning } from "tns-core-modules/profiling";
import { enable, disable, profile, time, start, stop, timer, isRunning, resetProfiles } from "tns-core-modules/profiling";
enable();
class TestClass {
@ -22,6 +22,20 @@ class TestClass {
unnamed2() {
// noop
}
private isInReentrant = false;
@profile
reentrant() {
try {
if (!this.isInReentrant) {
this.isInReentrant = true;
this.reentrant();
}
} finally {
this.isInReentrant = false;
}
}
}
const testFunction1 = profile(function testFunction1() {
@ -45,41 +59,54 @@ function retry(count: number, action: () => void) {
}
}
export function setUp() {
enable();
}
export function tearDown() {
disable();
}
export function test_time_returns_number() {
assertEqual(typeof time(), "number");
};
export function test_isRunning() {
resetProfiles();
const name = "test_isRunning";
assertFalse(isRunning(name), "isRunning should be false before start");
start(name);
assertTrue(isRunning(name), "isRunning should be true after start");
pause(name);
assertFalse(isRunning(name), "isRunning should be false after pause");
stop(name);
assertFalse(isRunning(name), "isRunning should be false after stop");
start(name);
assertTrue(isRunning(name), "isRunning should be true after second start");
stop(name);
assertFalse(isRunning(name), "isRunning should be false after stop");
assertFalse(isRunning(name), "isRunning should be false after second stop");
}
export function test_isRunning_withReentrancy() {
resetProfiles();
const name = "test_isRunning";
assertFalse(isRunning(name), "isRunning should be false before start");
start(name);
assertTrue(isRunning(name), "isRunning should be true after start");
start(name);
assertTrue(isRunning(name), "isRunning should be true after second start");
stop(name);
assertTrue(isRunning(name), "isRunning should be true after first stop");
stop(name);
assertFalse(isRunning(name), "isRunning should be false after second stop");
}
export function test_start_stop() {
resetProfiles();
retry(5, () => {
const name = "test_start_stop";
start(name);
const res = stop(name);
stop(name);
const res = timer(name);
assertEqual(res.count, 1);
assert(res.totalTime <= 1);
@ -87,18 +114,20 @@ export function test_start_stop() {
};
export function test_start_pause_count() {
resetProfiles();
const name = "test_start_pause_count";
for (var i = 0; i < 10; i++) {
start(name);
pause(name);
stop(name);
}
const res = stop(name);
const res = timer(name);
assertEqual(res.count, 10);
};
export function test_profile_decorator_count() {
resetProfiles();
const test = new TestClass();
for (var i = 0; i < 10; i++) {
test.doNothing();
@ -109,36 +138,39 @@ export function test_profile_decorator_count() {
}
["__func_decorator__", "TestClass.unnamed1", "TestClass.unnamed2", "testFunction1", "testFunction2"].forEach(key => {
const res = stop(key);
const res = timer(key);
assertEqual(res.count, 10, "Expected profile with name ${key} to have traced 10 calls.");
});
}
export function test_profile_decorator_handles_exceptions() {
resetProfiles();
const test = new TestClass();
assertThrows(() => test.throwError());
assertFalse(isRunning("__func_decorator_error__"), "Timer should be stopped on exception.");
assertEqual(stop("__func_decorator_error__").count, 1, "Timer should be called once");
assertEqual(timer("__func_decorator_error__").count, 1, "Timer should be called once");
}
export function test_start_pause_performance() {
resetProfiles();
retry(5, () => {
const count = 10000;
const name = "test_start_pause_performance";
for (var i = 0; i < count; i++) {
start(name);
pause(name);
stop(name);
}
const res = stop(name);
const res = timer(name);
assertEqual(res.count, count);
assert(res.totalTime <= 500, `Total time for ${count} timer operations is too much: ${res.totalTime}`);
});
};
export function test_profile_decorator_performance() {
resetProfiles();
retry(5, () => {
var start = Date.now();
const count = 10000;
@ -147,7 +179,7 @@ export function test_profile_decorator_performance() {
test.doNothing();
}
const res = stop("__func_decorator__");
const res = timer("__func_decorator__");
assertEqual(res.count, count);
assert(res.totalTime <= 500, `Total time for ${count} timer operations is too much: ${res.totalTime}`);
@ -155,3 +187,12 @@ export function test_profile_decorator_performance() {
assert(end - start <= 1000, `Total time for test execution is too much: ${end - start}ms`);
});
}
export function test_reentrancy() {
resetProfiles();
// reentrant
retry(5, () => {
const test = new TestClass();
test.reentrant();
});
}

View File

@ -7,6 +7,7 @@ import {
notify, hasListeners, lowMemoryEvent, orientationChangedEvent, suspendEvent, resumeEvent, displayedEvent,
setApplication, livesync, Observable
} from "./application-common";
import { profile } from "../profiling";
// First reexport so that app module is initialized.
export * from "./application-common";
@ -167,7 +168,7 @@ global.__onLiveSync = function () {
function initLifecycleCallbacks() {
// TODO: Verify whether the logic for triggerring application-wide events based on Activity callbacks is working properly
const lifecycleCallbacks = new android.app.Application.ActivityLifecycleCallbacks({
onActivityCreated: function (activity: android.app.Activity, savedInstanceState: android.os.Bundle) {
onActivityCreated: profile("onActivityCreated", function (activity: android.app.Activity, savedInstanceState: android.os.Bundle) {
// Set app theme after launch screen was used during startup
const activityInfo = activity.getPackageManager().getActivityInfo(activity.getComponentName(), android.content.pm.PackageManager.GET_META_DATA);
if (activityInfo.metaData) {
@ -194,9 +195,9 @@ function initLifecycleCallbacks() {
});
rootView.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener);
}
},
}),
onActivityDestroyed: function (activity: android.app.Activity) {
onActivityDestroyed: profile("onActivityDestroyed", function (activity: android.app.Activity) {
if (activity === androidApp.foregroundActivity) {
androidApp.foregroundActivity = undefined;
}
@ -208,18 +209,18 @@ function initLifecycleCallbacks() {
androidApp.notify(<AndroidActivityEventData>{ eventName: ActivityDestroyed, object: androidApp, activity: activity });
// TODO: This is a temporary workaround to force the V8's Garbage Collector, which will force the related Java Object to be collected.
gc();
},
}),
onActivityPaused: function (activity: android.app.Activity) {
onActivityPaused: profile("onActivityPaused", function (activity: android.app.Activity) {
if ((<any>activity).isNativeScriptActivity) {
androidApp.paused = true;
notify(<ApplicationEventData>{ eventName: suspendEvent, object: androidApp, android: activity });
}
androidApp.notify(<AndroidActivityEventData>{ eventName: ActivityPaused, object: androidApp, activity: activity });
},
}),
onActivityResumed: function (activity: android.app.Activity) {
onActivityResumed: profile("onActivityResumed", function (activity: android.app.Activity) {
androidApp.foregroundActivity = activity;
if ((<any>activity).isNativeScriptActivity) {
@ -228,19 +229,19 @@ function initLifecycleCallbacks() {
}
androidApp.notify(<AndroidActivityEventData>{ eventName: ActivityResumed, object: androidApp, activity: activity });
},
}),
onActivitySaveInstanceState: function (activity: android.app.Activity, outState: android.os.Bundle) {
onActivitySaveInstanceState: profile("onActivityResumed", function (activity: android.app.Activity, outState: android.os.Bundle) {
androidApp.notify(<AndroidActivityBundleEventData>{ eventName: SaveActivityState, object: androidApp, activity: activity, bundle: outState });
},
}),
onActivityStarted: function (activity: android.app.Activity) {
onActivityStarted: profile("onActivityStarted", function (activity: android.app.Activity) {
androidApp.notify(<AndroidActivityEventData>{ eventName: ActivityStarted, object: androidApp, activity: activity });
},
}),
onActivityStopped: function (activity: android.app.Activity) {
onActivityStopped: profile("onActivityStopped", function (activity: android.app.Activity) {
androidApp.notify(<AndroidActivityEventData>{ eventName: ActivityStopped, object: androidApp, activity: activity });
}
})
});
return lifecycleCallbacks;
@ -249,17 +250,17 @@ function initLifecycleCallbacks() {
let currentOrientation: number;
function initComponentCallbacks() {
let componentCallbacks = new android.content.ComponentCallbacks2({
onLowMemory: function () {
onLowMemory: profile("onLowMemory", function () {
gc();
java.lang.System.gc();
notify(<ApplicationEventData>{ eventName: lowMemoryEvent, object: this, android: this });
},
}),
onTrimMemory: function (level: number) {
onTrimMemory: profile("onTrimMemory", function (level: number) {
// TODO: This is skipped for now, test carefully for OutOfMemory exceptions
},
}),
onConfigurationChanged: function (newConfig: android.content.res.Configuration) {
onConfigurationChanged: profile("onConfigurationChanged", function (newConfig: android.content.res.Configuration) {
const newOrientation = newConfig.orientation;
if (newOrientation === currentOrientation) {
return;
@ -286,7 +287,7 @@ function initComponentCallbacks() {
newValue: newValue,
object: androidApp
});
}
})
});
return componentCallbacks;

View File

@ -10,9 +10,32 @@ interface TimerInfo {
}
/**
* Enables profiling.
* Profiling mode to use.
* - `counters` Accumulates method call counts and times until dumpProfiles is called and then prints agregated statistic in the console. This is the default.
* - `timeline` Outputs method names along start/end timestamps in the console on the go.
*/
export declare function enable(): void;
type InstrumentationMode = "counters" | "timeline";
/**
* Enables profiling.
*
* Upon loading of the module it will cache the package.json of the app and check if there is a "profiling" key set,
* its value can be one of the options available for InstrumentationMode, and if set,
* enable() will be called in pre app start with the value in the package.json.
*
* An example for an `app/package.json` enabling the manual instrumentation profiling is:
* ```
* {
* "main": "main.js",
* "profiling": "timeline"
* }
* ```
*
* @param type Profiling mode to use.
* - "counters" - Accumulates method call counts and times until dumpProfiles is called and then prints agregated statistic in the console. This is the default.
* - "timeline" - Outputs method names along start/end timestamps in the console on the go.
*/
export declare function enable(type?: InstrumentationMode): void;
/**
* Disables profiling.
@ -37,15 +60,13 @@ export declare function start(name: string): void;
* @param name Name of the timer.
* @returns TimerInfo for the paused timer.
*/
export declare function pause(name: string): TimerInfo;
export declare function stop(name: string): TimerInfo;
/**
* Stops a timer with a specific name. This will print the count and the total time and will also reset the timer.
* Works only if profiling is enabled.
* @param name Name of the timer.
* @returns TimerInfo for the stopped timer.
* Read a timer info.
* @param name The name of the timer to obtain information about.
*/
export declare function stop(name: string): TimerInfo;
export function timer(name: string): TimerInfo;
/**
* Returns true if a timer is currently running.
@ -60,18 +81,21 @@ export declare function isRunning(name: string): boolean;
* @param name Name of the timer which will be used for method calls. If not provided - the name of the method will be used.
*/
export declare function profile(name?: string): MethodDecorator;
/**
* Function factory. It will intercept the function call and start and pause a timer before and after the function call. Works only if profiling is enabled.
* Works only if profiling is enabled.
* @param fn The function to wrap. Uses the function name to track the times.
*/
export declare function profile<F extends Function>(fn: F): F;
/**
* Function factory. It will intercept the function call and start and pause a timer before and after the function call. Works only if profiling is enabled.
* @param name The name used to track calls and times.
* @param fn The function to wrap.
*/
export declare function profile<F extends Function>(name: string, fn: F): F;
/**
* Method decorator. It will intercept the method calls and start and pause a timer before and after the method call. Works only if profiling is enabled.
*/
@ -103,4 +127,4 @@ export declare function stopCPUProfile(name: string): void;
/**
* Gets the uptime of the current process in miliseconds.
*/
export function uptime(): number;
export function uptime(): number;

View File

@ -1,7 +1,7 @@
declare var __startCPUProfiler: any;
declare var __stopCPUProfiler: any;
import { TimerInfo as TimerInfoDefinition } from ".";
import { TimerInfo as TimerInfoDefinition, InstrumentationMode } from ".";
export const uptime = global.android ? (<any>org).nativescript.Process.getUpTime : (<any>global).__tns_uptime;
@ -10,7 +10,10 @@ interface TimerInfo extends TimerInfoDefinition {
lastTime?: number;
count: number;
currentStart: number;
isRunning: boolean;
/**
* Counts the number of entry and exits a function had.
*/
runCount: number;
}
// Use object instead of map as it is a bit faster
@ -18,87 +21,119 @@ const timers: { [index: string]: TimerInfo } = {};
const anyGlobal = <any>global;
const profileNames: string[] = [];
let instrumentationEnabled = false;
export function enable() {
instrumentationEnabled = true;
}
export function disable() {
instrumentationEnabled = false;
}
export const time = (<any>global).__time || Date.now;
export function start(name: string): void {
let info = timers[name];
if (info) {
if (info.isRunning) {
throw new Error(`Timer already running: ${name}`);
}
info.currentStart = time();
info.isRunning = true;
info.runCount++;
} else {
info = {
totalTime: 0,
count: 0,
currentStart: time(),
isRunning: true
runCount: 1
};
timers[name] = info;
}
}
export function pause(name: string): TimerInfo {
let info = pauseInternal(name);
return info;
}
export function stop(name: string): TimerInfo {
let info = pauseInternal(name);
console.log(`---- [${name}] STOP total: ${info.totalTime} count:${info.count}`);
timers[name] = undefined;
return info;
}
export function isRunning(name: string): boolean {
const info = timers[name];
return !!(info && info.isRunning);
}
function pauseInternal(name: string): TimerInfo {
const info = timers[name];
if (!info) {
throw new Error(`No timer started: ${name}`);
}
if (info.isRunning) {
info.lastTime = time() - info.currentStart;
info.totalTime += info.lastTime;
info.count++;
info.currentStart = 0;
info.isRunning = false;
if (info.runCount) {
info.runCount--;
if (info.runCount) {
info.count++;
} else {
info.lastTime = time() - info.currentStart;
info.totalTime += info.lastTime;
info.count++;
info.currentStart = 0;
}
} else {
throw new Error(`Timer ${name} paused more times than started.`);
}
return info;
}
function profileFunction<F extends Function>(fn: F, customName?: string): F {
const name = customName || fn.name;
export function timer(name: string): TimerInfo {
return timers[name];
}
export function print(name: string): TimerInfo {
const info = timers[name];
if (!info) {
throw new Error(`No timer started: ${name}`);
}
console.log(`---- [${name}] STOP total: ${info.totalTime} count:${info.count}`);
return info;
}
export function isRunning(name: string): boolean {
const info = timers[name];
return !!(info && info.runCount);
}
function countersProfileFunctionFactory<F extends Function>(fn: F, name: string): F {
profileNames.push(name);
return <any>function() {
start(name);
try {
return fn.apply(this, arguments);
} finally {
pause(name);
stop(name);
}
}
}
function timelineProfileFunctionFactory<F extends Function>(fn: F, name: string): F {
return <any>function() {
const start = time();
try {
return fn.apply(this, arguments);
} finally {
const end = time();
console.log(`Timeline: Modules: ${name} (${start}ms. - ${end}ms.)`);
}
}
}
let profileFunctionFactory: <F extends Function>(fn: F, name: string) => F;
export function enable(mode: InstrumentationMode = "counters") {
profileFunctionFactory = mode && {
counters: countersProfileFunctionFactory,
timeline: timelineProfileFunctionFactory
}[mode];
}
try {
const appConfig = global.require("~/package.json");
if (appConfig && appConfig.profiling) {
if (appConfig && appConfig.profiling) {
enable(appConfig.profiling);
}
}
} catch(e) {
console.log("Profiling startup failed to figure out defaults from package.json, error: " + e);
}
export function disable() {
profileFunctionFactory = undefined;
}
function profileFunction<F extends Function>(fn: F, customName?: string): F {
return profileFunctionFactory(fn, customName || fn.name);
}
const profileMethodUnnamed = (target, key, descriptor) => {
// save a reference to the original method this way we keep the values currently in the
// descriptor and don't overwrite what another decorator might have done to the descriptor.
@ -114,17 +149,8 @@ const profileMethodUnnamed = (target, key, descriptor) => {
let name = className + key;
profileNames.push(name);
//editing the descriptor/value parameter
descriptor.value = function () {
start(name);
try {
return originalMethod.apply(this, arguments);
} finally {
pause(name);
}
};
descriptor.value = profileFunctionFactory(originalMethod, name);
// return edited descriptor as opposed to overwriting the descriptor
return descriptor;
@ -140,17 +166,8 @@ function profileMethodNamed(name: string): MethodDecorator {
}
var originalMethod = descriptor.value;
profileNames.push(name);
//editing the descriptor/value parameter
descriptor.value = function () {
start(name);
try {
return originalMethod.apply(this, arguments);
} finally {
pause(name);
}
};
descriptor.value = profileFunctionFactory(originalMethod, name);
// return edited descriptor as opposed to overwriting the descriptor
return descriptor;
@ -163,27 +180,27 @@ const voidMethodDecorator = () => {
export function profile(nameFnOrTarget?: string | Function | Object, fnOrKey?: Function | string | symbol, descriptor?: PropertyDescriptor): Function | MethodDecorator {
if (typeof nameFnOrTarget === "object" && (typeof fnOrKey === "string" || typeof fnOrKey === "symbol")) {
if (!instrumentationEnabled) {
if (!profileFunctionFactory) {
return;
}
return profileMethodUnnamed(nameFnOrTarget, fnOrKey, descriptor);
} else if (typeof nameFnOrTarget === "string" && typeof fnOrKey === "function") {
if (!instrumentationEnabled) {
if (!profileFunctionFactory) {
return fnOrKey;
}
return profileFunction(fnOrKey, nameFnOrTarget);
} else if (typeof nameFnOrTarget === "function") {
if (!instrumentationEnabled) {
if (!profileFunctionFactory) {
return nameFnOrTarget;
}
return profileFunction(nameFnOrTarget);
} else if (typeof nameFnOrTarget === "string") {
if (!instrumentationEnabled) {
if (!profileFunctionFactory) {
return voidMethodDecorator;
}
return profileMethodNamed(nameFnOrTarget);
} else {
if (!instrumentationEnabled) {
if (!profileFunctionFactory) {
return voidMethodDecorator;
}
return profileMethodUnnamed;
@ -193,7 +210,6 @@ export function profile(nameFnOrTarget?: string | Function | Object, fnOrKey?: F
export function dumpProfiles(): void {
profileNames.forEach(function (name) {
const info = timers[name];
if (info) {
console.log("---- [" + name + "] STOP total: " + info.totalTime + " count:" + info.count);
}
@ -206,12 +222,11 @@ export function dumpProfiles(): void {
export function resetProfiles(): void {
profileNames.forEach(function (name) {
const info = timers[name];
if (info) {
if (!info.isRunning) {
timers[name] = undefined;
} else {
if (info.runCount) {
console.log("---- timer with name [" + name + "] is currently running and won't be reset");
} else {
timers[name] = undefined;
}
}
});

View File

@ -3,7 +3,7 @@
paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty,
Length, zIndexProperty, textAlignmentProperty, TextAlignment
} from "./button-common";
import { profile } from "../../profiling";
import { TouchGestureEventData, GestureTypes, TouchAction } from "../gestures";
export * from "./button-common";
@ -41,6 +41,7 @@ export class Button extends ButtonBase {
private _highlightedHandler: (args: TouchGestureEventData) => void;
@profile
public createNativeView() {
initializeClickListener();
const button = new android.widget.Button(this._context);

View File

@ -6,6 +6,8 @@ import { ViewBase } from "../view-base";
import { WrappedValue, PropertyChangeData } from "../../../data/observable";
import { Style } from "../../styling/style";
import { profile } from "../../../profiling";
export { Style };
export const unsetValue: any = new Object();
@ -964,7 +966,7 @@ function inheritableCssPropertyValuesOn(style: Style): Array<{ property: Inherit
return array;
}
export function initNativeView(view: ViewBase): void {
export const initNativeView = profile('"properties".initNativeView', function initNativeView(view: ViewBase): void {
let symbols = Object.getOwnPropertySymbols(view);
for (let symbol of symbols) {
const property: Property<any, any> = symbolPropertyMap[symbol];
@ -1005,7 +1007,7 @@ export function initNativeView(view: ViewBase): void {
view[property.setNative](value);
}
}
}
});
export function resetNativeView(view: ViewBase): void {
let symbols = Object.getOwnPropertySymbols(view);

View File

@ -17,6 +17,8 @@ import * as types from "../../../utils/types";
import { Color } from "../../../color";
import { profile } from "../../../profiling";
export { isIOS, isAndroid, layout, Color };
export * from "../bindable";
export * from "../properties";
@ -260,6 +262,7 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
this[name] = WrappedValue.unwrap(value);
}
@profile
public onLoaded() {
this._isLoaded = true;
this._loadEachChild();
@ -273,6 +276,7 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
});
}
@profile
public onUnloaded() {
this._unloadEachChild();
this._isLoaded = false;
@ -299,6 +303,7 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
});
}
@profile
public _applyStyleFromScope() {
const scope = this._styleScope;
if (scope) {
@ -309,6 +314,7 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
}
// TODO: Make sure the state is set to null and this is called on unloaded to clean up change listeners...
@profile
_setCssState(next: ssm.CssState): void {
const previous = this._cssState;
this._cssState = next;
@ -380,6 +386,7 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
private _invalidateCssHandler;
private _invalidateCssHandlerSuspended: boolean;
@profile
private applyCssState(): void {
this._batchUpdate(() => {
if (!this._cssState) {
@ -412,6 +419,7 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
return allStates;
}
@profile
public addPseudoClass(name: string): void {
let allStates = this.getAllAliasedStates(name);
for (let i = 0; i < allStates.length; i++) {
@ -422,6 +430,7 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
}
}
@profile
public deletePseudoClass(name: string): void {
let allStates = this.getAllAliasedStates(name);
for (let i = 0; i < allStates.length; i++) {
@ -432,6 +441,7 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
}
}
@profile
private _applyInlineStyle(inlineStyle) {
if (typeof inlineStyle === "string") {
try {
@ -504,6 +514,7 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
}
}
@profile
public requestLayout(): void {
let parent = this.parent;
if (parent) {
@ -515,6 +526,7 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
//
}
@profile
public _addView(view: ViewBase, atIndex?: number) {
if (traceEnabled()) {
traceWrite(`${this}._addView(${view}, ${atIndex})`, traceCategories.ViewHierarchy);
@ -535,6 +547,7 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
view._parentChanged(null);
}
@profile
private _setStyleScope(scope: ssm.StyleScope): void {
this._styleScope = scope;
this._applyStyleFromScope();
@ -623,6 +636,7 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
}
}
@profile
public _setupUI(context: android.content.Context, atIndex?: number, parentIsLoaded?: boolean) {
traceNotifyEvent(this, "_setupUI");
if (traceEnabled()) {
@ -701,6 +715,7 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
});
}
@profile
public _tearDownUI(force?: boolean) {
// No context means we are already teared down.
if (!this._context) {

View File

@ -17,6 +17,7 @@ import {
rotateProperty, scaleXProperty, scaleYProperty, translateXProperty, translateYProperty,
zIndexProperty, backgroundInternalProperty
} from "../../styling/style-properties";
import { profile } from "../../../profiling";
export * from "./view-common";
@ -82,11 +83,13 @@ export class View extends ViewCommon {
}
}
@profile
public onLoaded() {
super.onLoaded();
this.setOnTouchListener();
}
@profile
public onUnloaded() {
if (this.touchListenerIsSet) {
this.nativeView.setOnTouchListener(null);
@ -132,6 +135,7 @@ export class View extends ViewCommon {
}
}
@profile
public requestLayout(): void {
super.requestLayout();
if (this.nativeView) {

View File

@ -6,7 +6,6 @@ import {
ViewCommon, layout, isEnabledProperty, originXProperty, originYProperty, automationTextProperty, isUserInteractionEnabledProperty,
traceEnabled, traceWrite, traceCategories
} from "./view-common";
import {
Visibility,
visibilityProperty, opacityProperty,
@ -14,6 +13,7 @@ import {
translateXProperty, translateYProperty, zIndexProperty,
backgroundInternalProperty, clipPathProperty
} from "../../styling/style-properties";
import { profile } from "../../../profiling";
export * from "./view-common";
@ -80,6 +80,7 @@ export class View extends ViewCommon {
}
}
@profile
public layout(left: number, top: number, right: number, bottom: number): void {
let { boundsChanged, sizeChanged } = this._setCurrentLayoutBounds(left, top, right, bottom);
this.layoutNativeView(left, top, right, bottom);
@ -100,6 +101,7 @@ export class View extends ViewCommon {
this._privateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}
@profile
public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
const view = this.nativeView;
const width = layout.getMeasureSpecSize(widthMeasureSpec);

View File

@ -10,6 +10,8 @@ import { FrameBase, application, NavigationContext, stack, goBack, View, Observa
import { DIALOG_FRAGMENT_TAG } from "../page/constants";
import * as transitionModule from "../transition";
import { profile } from "../../profiling";
export * from "./frame-common";
const HIDDEN = "_hidden";
@ -117,6 +119,7 @@ export class Frame extends FrameBase {
return this._android;
}
@profile
public _navigateCore(backstackEntry: BackstackEntry) {
super._navigateCore(backstackEntry);
@ -631,6 +634,7 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks {
public entry: BackstackEntry;
public clearHistory: boolean;
@profile
public onHiddenChanged(fragment: android.app.Fragment, hidden: boolean, superFunc: Function): void {
if (traceEnabled()) {
traceWrite(`${fragment}.onHiddenChanged(${hidden})`, traceCategories.NativeLifecycle);
@ -644,6 +648,7 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks {
}
}
@profile
public onCreateAnimator(fragment: android.app.Fragment, transit: number, enter: boolean, nextAnim: number, superFunc: Function): android.animation.Animator {
let nextAnimString: string;
switch (nextAnim) {
@ -665,6 +670,7 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks {
return animator;
}
@profile
public onCreate(fragment: android.app.Fragment, savedInstanceState: android.os.Bundle, superFunc: Function): void {
if (traceEnabled()) {
traceWrite(`${fragment}.onCreate(${savedInstanceState})`, traceCategories.NativeLifecycle);
@ -687,6 +693,7 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks {
}
}
@profile
public onCreateView(fragment: android.app.Fragment, inflater: android.view.LayoutInflater, container: android.view.ViewGroup, savedInstanceState: android.os.Bundle, superFunc: Function): android.view.View {
if (traceEnabled()) {
traceWrite(`${fragment}.onCreateView(inflater, container, ${savedInstanceState})`, traceCategories.NativeLifecycle);
@ -709,6 +716,7 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks {
return page.nativeView;
}
@profile
public onSaveInstanceState(fragment: android.app.Fragment, outState: android.os.Bundle, superFunc: Function): void {
if (traceEnabled()) {
traceWrite(`${fragment}.onSaveInstanceState(${outState})`, traceCategories.NativeLifecycle);
@ -719,6 +727,7 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks {
}
}
@profile
public onDestroyView(fragment: android.app.Fragment, superFunc: Function): void {
if (traceEnabled()) {
traceWrite(`${fragment}.onDestroyView()`, traceCategories.NativeLifecycle);
@ -728,6 +737,7 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks {
onFragmentHidden(fragment, true);
}
@profile
public onDestroy(fragment: android.app.Fragment, superFunc: Function): void {
if (traceEnabled()) {
traceWrite(`${fragment}.onDestroy()`, traceCategories.NativeLifecycle);
@ -735,6 +745,7 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks {
superFunc.call(fragment);
}
@profile
public toStringOverride(fragment: android.app.Fragment, superFunc: Function): string {
return `${fragment.getTag()}<${this.entry ? this.entry.resolvedPage : ""}>`;
}
@ -743,6 +754,7 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks {
class ActivityCallbacksImplementation implements AndroidActivityCallbacks {
private _rootView: View;
@profile
public onCreate(activity: android.app.Activity, savedInstanceState: android.os.Bundle, superFunc: Function): void {
if (traceEnabled()) {
traceWrite(`Activity.onCreate(${savedInstanceState})`, traceCategories.NativeLifecycle);
@ -812,6 +824,7 @@ class ActivityCallbacksImplementation implements AndroidActivityCallbacks {
activityInitialized = true;
}
@profile
public onSaveInstanceState(activity: android.app.Activity, outState: android.os.Bundle, superFunc: Function): void {
superFunc.call(activity, outState);
let view = this._rootView;
@ -820,6 +833,7 @@ class ActivityCallbacksImplementation implements AndroidActivityCallbacks {
}
}
@profile
public onStart(activity: any, superFunc: Function): void {
superFunc.call(activity);
@ -832,6 +846,7 @@ class ActivityCallbacksImplementation implements AndroidActivityCallbacks {
}
}
@profile
public onStop(activity: any, superFunc: Function): void {
superFunc.call(activity);
@ -844,6 +859,7 @@ class ActivityCallbacksImplementation implements AndroidActivityCallbacks {
}
}
@profile
public onDestroy(activity: any, superFunc: Function): void {
let rootView = this._rootView;
if (rootView && rootView._context) {
@ -860,6 +876,7 @@ class ActivityCallbacksImplementation implements AndroidActivityCallbacks {
application.notify(exitArgs);
}
@profile
public onBackPressed(activity: any, superFunc: Function): void {
if (traceEnabled()) {
traceWrite("NativeScriptActivity.onBackPressed;", traceCategories.NativeLifecycle);
@ -882,6 +899,7 @@ class ActivityCallbacksImplementation implements AndroidActivityCallbacks {
}
}
@profile
public onRequestPermissionsResult(activity: any, requestCode: number, permissions: Array<String>, grantResults: Array<number>, superFunc: Function): void {
if (traceEnabled()) {
traceWrite("NativeScriptActivity.onRequestPermissionsResult;", traceCategories.NativeLifecycle);
@ -897,6 +915,7 @@ class ActivityCallbacksImplementation implements AndroidActivityCallbacks {
});
}
@profile
public onActivityResult(activity: any, requestCode: number, resultCode: number, data: android.content.Intent, superFunc: Function): void {
superFunc.call(activity, requestCode, resultCode, data);
if (traceEnabled()) {

View File

@ -1,6 +1,7 @@
// Definitions.
import { iOSFrame as iOSFrameDefinition, BackstackEntry, NavigationTransition } from ".";
import { Page } from "../page";
import { profile } from "../../profiling";
//Types.
import { FrameBase, View, application, layout, traceEnabled, traceWrite, traceCategories, isCategorySet } from "./frame-common";
@ -59,6 +60,7 @@ export class Frame extends FrameBase {
});
}
@profile
public onLoaded() {
super.onLoaded();
@ -78,6 +80,7 @@ export class Frame extends FrameBase {
}
}
@profile
public _navigateCore(backstackEntry: BackstackEntry) {
super._navigateCore(backstackEntry);
@ -491,6 +494,7 @@ class UINavigationControllerImpl extends UINavigationController {
return this._owner.get();
}
@profile
public viewDidLoad(): void {
super.viewDidLoad();
let owner = this._owner.get();

View File

@ -1,5 +1,6 @@
import { Label as LabelDefinition } from ".";
import { TextBase, WhiteSpace, whiteSpaceProperty } from "../text-base";
import { profile } from "../../profiling";
export * from "../text-base";
@ -15,6 +16,7 @@ export class Label extends TextBase implements LabelDefinition {
this.style.whiteSpace = value ? "normal" : "nowrap";
}
@profile
public createNativeView() {
if (!TextView) {
TextView = android.widget.TextView;

View File

@ -1,5 +1,6 @@
import { ListPickerBase, Color, selectedIndexProperty, itemsProperty, backgroundColorProperty, colorProperty } from "./list-picker-common";
import { ItemsSource } from ".";
import { profile } from "../../profiling";
export * from "./list-picker-common";
@ -18,6 +19,7 @@ export class ListPicker extends ListPickerBase {
this.nativeView = this._ios;
}
@profile
public onLoaded() {
super.onLoaded();
this._ios.delegate = this._delegate;

View File

@ -6,6 +6,7 @@ import {
import { StackLayout } from "../layouts/stack-layout";
import { ProxyViewContainer } from "../proxy-view-container";
import { LayoutBase } from "../layouts/layout-base";
import { profile } from "../../profiling";
export * from "./list-view-common";
@ -49,6 +50,7 @@ export class ListView extends ListViewBase {
public _realizedItems = new Map<android.view.View, View>();
public _realizedTemplates = new Map<string, Map<android.view.View, View>>();
@profile
public createNativeView() {
initializeItemClickListener();
@ -238,6 +240,7 @@ function ensureListViewAdapterClass() {
return itemViewType;
}
@profile
public getView(index: number, convertView: android.view.View, parent: android.view.ViewGroup): android.view.View {
//this.owner._dumpRealizedTemplates();

View File

@ -6,6 +6,7 @@ import {
import { StackLayout } from "../layouts/stack-layout";
import { ProxyViewContainer } from "../proxy-view-container";
import { ios } from "../../utils/utils";
import { profile } from "../../profiling";
export * from "./list-view-common";
@ -225,6 +226,7 @@ export class ListView extends ListViewBase {
this._ios.clipsToBounds = true;
}
@profile
public onLoaded() {
super.onLoaded();
if (this._isDataDirty) {

View File

@ -8,6 +8,7 @@ import { ActionBar } from "../action-bar";
import { KeyframeAnimationInfo } from "../animation/keyframe-animation";
import { StyleScope } from "../styling/style-scope";
import { File, path, knownFolders } from "../../file-system";
import { profile } from "../../profiling";
export * from "../content-view";
@ -93,6 +94,7 @@ export class PageBase extends ContentView implements PageDefinition {
return this;
}
@profile
public onLoaded(): void {
this._refreshCss();
super.onLoaded();

View File

@ -3,6 +3,7 @@ import { ActionBar } from "../action-bar";
import { GridLayout } from "../layouts/grid-layout";
import { DIALOG_FRAGMENT_TAG } from "./constants";
import { device } from "../../platform";
import { profile } from "../../profiling";
export * from "./page-common";
@ -118,6 +119,7 @@ export class Page extends PageBase {
return super._addViewToNativeVisualTree(child, atIndex);
}
@profile
public onLoaded() {
super.onLoaded();
if (this.actionBarHidden !== undefined) {

View File

@ -8,6 +8,7 @@ import { device } from "../../platform";
// HACK: Webpack. Use a fully-qualified import to allow resolve.extensions(.ios.js) to
// kick in. `../utils` doesn't seem to trigger the webpack extensions mechanism.
import * as uiUtils from "tns-core-modules/ui/utils";
import { profile } from "../../profiling";
export * from "./page-common";
@ -354,6 +355,7 @@ export class Page extends PageBase {
this._addNativeView(newView);
}
@profile
public onLoaded() {
// loaded/unloaded events are handled in page viewWillAppear/viewDidDisappear
if (this._enableLoadedEvents) {

View File

@ -5,6 +5,7 @@ import { StackLayout } from "../layouts/stack-layout";
import { ObservableArray, ChangedData } from "../../data/observable-array";
import { addWeakEventListener, removeWeakEventListener } from "../core/weak-event-listener";
import { parse } from "../builder";
import { profile } from "../../profiling";
export * from "../layouts/layout-base";
@ -27,6 +28,7 @@ export class Repeater extends CustomLayoutView implements RepeaterDefinition {
public itemTemplate: string | Template;
public itemsLayout: LayoutBase;
@profile
public onLoaded() {
if (this._isDirty) {
this.refresh();

View File

@ -1,5 +1,6 @@
import { ScrollView as ScrollViewDefinition, Orientation } from ".";
import { ContentView, Property, makeParser, makeValidator } from "../content-view";
import { profile } from "../../profiling";
export * from "../content-view";
@ -27,6 +28,7 @@ export abstract class ScrollViewBase extends ContentView implements ScrollViewDe
}
}
@profile
public onLoaded() {
super.onLoaded();

View File

@ -5,10 +5,10 @@ import {
tabTextColorProperty, tabBackgroundColorProperty, selectedTabTextColorProperty, iosIconRenderingModeProperty,
View, fontInternalProperty, layout, traceEnabled, traceWrite, traceCategories, Color, initNativeView
} from "./tab-view-common"
import { textTransformProperty, TextTransform, getTransformedText } from "../text-base";
import { fromFileOrResource } from "../../image-source";
import { Page } from "../page";
import { profile } from "../../profiling";
export * from "./tab-view-common";
@ -184,6 +184,7 @@ export class TabView extends TabViewBase {
//This delegate is set on the last line of _addTabs method.
}
@profile
public onLoaded() {
super.onLoaded();
this._ios.delegate = this._delegate;

View File

@ -2,6 +2,7 @@
TextFieldBase, secureProperty, textProperty, hintProperty, colorProperty, placeholderColorProperty,
Length, paddingTopProperty, paddingRightProperty, paddingBottomProperty, paddingLeftProperty, _updateCharactersInRangeReplacementString, Color, layout
} from "./text-field-common";
import { profile } from "../../profiling";
export * from "./text-field-common";
@ -145,6 +146,7 @@ export class TextField extends TextFieldBase {
this.nativeView = this._ios;
}
@profile
public onLoaded() {
super.onLoaded();
this._ios.delegate = this._delegate;

View File

@ -5,6 +5,7 @@ import {
paddingTopProperty, paddingRightProperty, paddingBottomProperty, paddingLeftProperty,
Length, _updateCharactersInRangeReplacementString, Color, layout
} from "../editable-text-base";
import { profile } from "../../profiling";
export * from "../editable-text-base";
@ -92,6 +93,7 @@ export class TextView extends EditableTextBase implements TextViewDefinition {
this._delegate = UITextViewDelegateImpl.initWithOwner(new WeakRef(this));
}
@profile
public onLoaded() {
super.onLoaded();
this._ios.delegate = this._delegate;

View File

@ -1,4 +1,5 @@
import { WebViewBase, knownFolders, traceWrite, traceEnabled, traceCategories, NavigationType } from "./web-view-common";
import { profile } from "../../profiling";
export * from "./web-view-common";
@ -91,6 +92,7 @@ export class WebView extends WebViewBase {
this._delegate = UIWebViewDelegateImpl.initWithOwner(new WeakRef(this));
}
@profile
public onLoaded() {
super.onLoaded();
this._ios.delegate = this._delegate;