From 77875a270d4b19b979ffbd4639b642e3dd44bbb4 Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Sun, 14 Jun 2015 16:42:26 -0700 Subject: [PATCH] feat: implement web-tracing-framework support This includes implementation and minimal instrumentation Closes #2610 --- modules/angular2/angular2.ts | 1 + modules/angular2/angular2_exports.ts | 1 + modules/angular2/profile.ts | 7 ++ .../abstract_change_detector.ts | 10 +- .../angular2/src/core/application_common.ts | 2 + .../angular2/src/core/compiler/compiler.ts | 7 +- .../src/core/compiler/view_manager.ts | 35 +++++-- .../src/core/life_cycle/life_cycle.ts | 5 + modules/angular2/src/core/zone/ng_zone.ts | 15 ++- modules/angular2/src/profile/profile.ts | 80 ++++++++++++++++ modules/angular2/src/profile/wtf_impl.dart | 96 +++++++++++++++++++ modules/angular2/src/profile/wtf_impl.ts | 56 +++++++++++ modules/angular2/src/profile/wtf_init.dart | 14 +++ modules/angular2/src/profile/wtf_init.ts | 4 + .../src/render/dom/compiler/view_loader.ts | 8 +- .../angular2/src/render/dom/dom_renderer.ts | 17 +++- .../angular2/test/profile/wtf_impl_spec.dart | 18 ++++ 17 files changed, 357 insertions(+), 19 deletions(-) create mode 100644 modules/angular2/profile.ts create mode 100644 modules/angular2/src/profile/profile.ts create mode 100644 modules/angular2/src/profile/wtf_impl.dart create mode 100644 modules/angular2/src/profile/wtf_impl.ts create mode 100644 modules/angular2/src/profile/wtf_init.dart create mode 100644 modules/angular2/src/profile/wtf_init.ts create mode 100644 modules/angular2/test/profile/wtf_impl_spec.dart diff --git a/modules/angular2/angular2.ts b/modules/angular2/angular2.ts index 08fdd54b1f..8f5032ee18 100644 --- a/modules/angular2/angular2.ts +++ b/modules/angular2/angular2.ts @@ -15,3 +15,4 @@ export * from './directives'; export * from './http'; export * from './forms'; export * from './render'; +export * from './profile'; diff --git a/modules/angular2/angular2_exports.ts b/modules/angular2/angular2_exports.ts index 90609268f3..ad7b5b6e03 100644 --- a/modules/angular2/angular2_exports.ts +++ b/modules/angular2/angular2_exports.ts @@ -6,3 +6,4 @@ export * from './directives'; export * from './http'; export * from './forms'; export * from './render'; +export * from './profile'; diff --git a/modules/angular2/profile.ts b/modules/angular2/profile.ts new file mode 100644 index 0000000000..f1ad6ec768 --- /dev/null +++ b/modules/angular2/profile.ts @@ -0,0 +1,7 @@ +export { + wtfCreateScope, + wtfLeave, + wtfStartTimeRange, + wtfEndTimeRange, + WtfScopeFn +} from './src/profile/profile'; diff --git a/modules/angular2/src/change_detection/abstract_change_detector.ts b/modules/angular2/src/change_detection/abstract_change_detector.ts index ed854c9885..4fdf665cce 100644 --- a/modules/angular2/src/change_detection/abstract_change_detector.ts +++ b/modules/angular2/src/change_detection/abstract_change_detector.ts @@ -14,6 +14,9 @@ import {BindingRecord} from './binding_record'; import {Locals} from './parser/locals'; import {Pipes} from './pipes/pipes'; import {CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED, ON_PUSH} from './constants'; +import {wtfCreateScope, wtfLeave, WtfScopeFn} from '../profile/profile'; + +var _scope_check: WtfScopeFn = wtfCreateScope(`ChangeDetector#check(ascii id, bool throwOnChange)`); class _Context { constructor(public element: any, public componentElement: any, public instance: any, @@ -69,16 +72,13 @@ export class AbstractChangeDetector implements ChangeDetector { runDetectChanges(throwOnChange: boolean): void { if (this.mode === DETACHED || this.mode === CHECKED) return; - + var s = _scope_check(this.id, throwOnChange); this.detectChangesInRecords(throwOnChange); - this._detectChangesInLightDomChildren(throwOnChange); - if (throwOnChange === false) this.callOnAllChangesDone(); - this._detectChangesInShadowDomChildren(throwOnChange); - if (this.mode === CHECK_ONCE) this.mode = CHECKED; + wtfLeave(s); } // This method is not intended to be overridden. Subclasses should instead provide an diff --git a/modules/angular2/src/core/application_common.ts b/modules/angular2/src/core/application_common.ts index 39afa709de..da1e95d54c 100644 --- a/modules/angular2/src/core/application_common.ts +++ b/modules/angular2/src/core/application_common.ts @@ -71,6 +71,7 @@ import { } from 'angular2/src/render/dom/view/shared_styles_host'; import {internalView} from 'angular2/src/core/compiler/view_ref'; import {appComponentRefPromiseToken, appComponentTypeToken} from './application_tokens'; +import {wtfInit} from '../profile/wtf_init'; var _rootInjector: Injector; @@ -290,6 +291,7 @@ export function commonBootstrap( appComponentType: /*Type*/ any, componentInjectableBindings: List> = null): Promise { BrowserDomAdapter.makeCurrent(); + wtfInit(); var bootstrapProcess = PromiseWrapper.completer(); var zone = createNgZone(new ExceptionHandler(DOM, isDart ? false : true)); zone.run(() => { diff --git a/modules/angular2/src/core/compiler/compiler.ts b/modules/angular2/src/core/compiler/compiler.ts index 7daa976896..ef5b0d6074 100644 --- a/modules/angular2/src/core/compiler/compiler.ts +++ b/modules/angular2/src/core/compiler/compiler.ts @@ -25,6 +25,7 @@ import {ProtoViewFactory} from './proto_view_factory'; import {UrlResolver} from 'angular2/src/services/url_resolver'; import {AppRootUrl} from 'angular2/src/services/app_root_url'; import {ElementBinder} from './element_binder'; +import {wtfStartTimeRange, wtfEndTimeRange} from '../../profile/profile'; import * as renderApi from 'angular2/src/render/api'; @@ -127,6 +128,7 @@ export class Compiler { compileInHost(componentTypeOrBinding: Type | Binding): Promise { var componentType = isType(componentTypeOrBinding) ? componentTypeOrBinding : (componentTypeOrBinding).token; + var r = wtfStartTimeRange('Compiler#compile()', stringify(componentType)); var hostAppProtoView = this._compilerCache.getHost(componentType); var hostPvPromise; @@ -149,7 +151,10 @@ export class Compiler { return appProtoView; }); } - return hostPvPromise.then(hostAppProtoView => hostAppProtoView.ref); + return hostPvPromise.then((hostAppProtoView) => { + wtfEndTimeRange(r); + return hostAppProtoView.ref; + }); } private _compile(componentBinding: DirectiveBinding, diff --git a/modules/angular2/src/core/compiler/view_manager.ts b/modules/angular2/src/core/compiler/view_manager.ts index 9f6d856a0f..14092d0f44 100644 --- a/modules/angular2/src/core/compiler/view_manager.ts +++ b/modules/angular2/src/core/compiler/view_manager.ts @@ -15,6 +15,7 @@ import { import {AppViewManagerUtils} from './view_manager_utils'; import {AppViewPool} from './view_pool'; import {AppViewListener} from './view_listener'; +import {wtfCreateScope, wtfLeave, WtfScopeFn} from '../../profile/profile'; /** * Entry point for creating, moving views in the view hierarchy and destroying views. @@ -83,6 +84,7 @@ export class AppViewManager { return this._utils.getComponentInstance(hostView, boundElementIndex); } + _scope_createRootHostView: WtfScopeFn = wtfCreateScope('AppViewManager#createRootHostView()'); /** * Load component view into existing element. * @@ -139,6 +141,7 @@ export class AppViewManager { */ createRootHostView(hostProtoViewRef: ProtoViewRef, overrideSelector: string, injector: Injector): HostViewRef { + var s = this._scope_createRootHostView(); var hostProtoView: viewModule.AppProtoView = internalProtoView(hostProtoViewRef); var hostElementSelector = overrideSelector; if (isBlank(hostElementSelector)) { @@ -151,38 +154,45 @@ export class AppViewManager { this._renderer.hydrateView(hostView.render); this._utils.hydrateRootHostView(hostView, injector); - - return hostView.ref; + return wtfLeave(s, hostView.ref); } + _scope_destroyRootHostView: WtfScopeFn = wtfCreateScope('AppViewManager#destroyRootHostView()'); /** * Remove the View created with {@link AppViewManager#createRootHostView}. */ destroyRootHostView(hostViewRef: HostViewRef) { // Note: Don't put the hostView into the view pool // as it is depending on the element for which it was created. + var s = this._scope_destroyRootHostView(); var hostView = internalView(hostViewRef); this._renderer.detachFragment(hostView.renderFragment); this._renderer.dehydrateView(hostView.render); this._viewDehydrateRecurse(hostView); this._viewListener.viewDestroyed(hostView); this._renderer.destroyView(hostView.render); + wtfLeave(s); } + _scope_createEmbeddedViewInContainer: WtfScopeFn = + wtfCreateScope('AppViewManager#createEmbeddedViewInContainer()'); /** * * See {@link AppViewManager#destroyViewInContainer}. */ createEmbeddedViewInContainer(viewContainerLocation: ElementRef, atIndex: number, templateRef: TemplateRef): ViewRef { + var s = this._scope_createEmbeddedViewInContainer(); var protoView = internalProtoView(templateRef.protoViewRef); if (protoView.type !== ViewType.EMBEDDED) { throw new BaseException('This method can only be called with embedded ProtoViews!'); } - return this._createViewInContainer(viewContainerLocation, atIndex, protoView, - templateRef.elementRef, null); + return wtfLeave(s, this._createViewInContainer(viewContainerLocation, atIndex, protoView, + templateRef.elementRef, null)); } + _scope_createHostViewInContainer: WtfScopeFn = + wtfCreateScope('AppViewManager#createHostViewInContainer()'); /** * * See {@link AppViewManager#destroyViewInContainer}. @@ -190,12 +200,14 @@ export class AppViewManager { createHostViewInContainer(viewContainerLocation: ElementRef, atIndex: number, protoViewRef: ProtoViewRef, imperativelyCreatedInjector: ResolvedBinding[]): HostViewRef { + var s = this._scope_createHostViewInContainer(); var protoView = internalProtoView(protoViewRef); if (protoView.type !== ViewType.HOST) { throw new BaseException('This method can only be called with host ProtoViews!'); } - return this._createViewInContainer(viewContainerLocation, atIndex, protoView, - viewContainerLocation, imperativelyCreatedInjector); + return wtfLeave( + s, this._createViewInContainer(viewContainerLocation, atIndex, protoView, + viewContainerLocation, imperativelyCreatedInjector)); } /** @@ -243,22 +255,27 @@ export class AppViewManager { } } + _scope_destroyViewInContainer = wtfCreateScope('AppViewMananger#destroyViewInContainer()'); /** * * See {@link AppViewManager#createViewInContainer}. */ destroyViewInContainer(viewContainerLocation: ElementRef, atIndex: number) { + var s = this._scope_destroyViewInContainer(); var parentView = internalView(viewContainerLocation.parentView); var boundElementIndex = viewContainerLocation.boundElementIndex; this._destroyViewInContainer(parentView, boundElementIndex, atIndex); + wtfLeave(s); } + _scope_attachViewInContainer = wtfCreateScope('AppViewMananger#attachViewInContainer()'); /** * * See {@link AppViewManager#detachViewInContainer}. */ attachViewInContainer(viewContainerLocation: ElementRef, atIndex: number, viewRef: ViewRef): ViewRef { + var s = this._scope_attachViewInContainer(); var view = internalView(viewRef); var parentView = internalView(viewContainerLocation.parentView); var boundElementIndex = viewContainerLocation.boundElementIndex; @@ -270,21 +287,23 @@ export class AppViewManager { // context view that might have been used. this._utils.attachViewInContainer(parentView, boundElementIndex, null, null, atIndex, view); this._attachRenderView(parentView, boundElementIndex, atIndex, view); - return viewRef; + return wtfLeave(s, viewRef); } + _scope_detachViewInContainer = wtfCreateScope('AppViewMananger#detachViewInContainer()'); /** * * See {@link AppViewManager#attachViewInContainer}. */ detachViewInContainer(viewContainerLocation: ElementRef, atIndex: number): ViewRef { + var s = this._scope_detachViewInContainer(); var parentView = internalView(viewContainerLocation.parentView); var boundElementIndex = viewContainerLocation.boundElementIndex; var viewContainer = parentView.viewContainers[boundElementIndex]; var view = viewContainer.views[atIndex]; this._utils.detachViewInContainer(parentView, boundElementIndex, atIndex); this._renderer.detachFragment(view.renderFragment); - return view.ref; + return wtfLeave(s, view.ref); } _createMainView(protoView: viewModule.AppProtoView, diff --git a/modules/angular2/src/core/life_cycle/life_cycle.ts b/modules/angular2/src/core/life_cycle/life_cycle.ts index 996a434426..f591c9b486 100644 --- a/modules/angular2/src/core/life_cycle/life_cycle.ts +++ b/modules/angular2/src/core/life_cycle/life_cycle.ts @@ -2,6 +2,7 @@ import {Injectable} from 'angular2/di'; import {ChangeDetector} from 'angular2/src/change_detection/change_detection'; import {NgZone} from 'angular2/src/core/zone/ng_zone'; import {isPresent, BaseException} from 'angular2/src/facade/lang'; +import {wtfLeave, wtfCreateScope, WtfScopeFn} from '../../profile/profile'; /** * Provides access to explicitly trigger change detection in an application. @@ -31,6 +32,8 @@ import {isPresent, BaseException} from 'angular2/src/facade/lang'; */ @Injectable() export class LifeCycle { + static _scope_tick: WtfScopeFn = wtfCreateScope('LifeCycle#tick()'); + _changeDetector: ChangeDetector; _enforceNoNewChanges: boolean; _runningTick: boolean = false; @@ -71,6 +74,7 @@ export class LifeCycle { throw new BaseException("LifeCycle.tick is called recursively"); } + var s = LifeCycle._scope_tick(); try { this._runningTick = true; this._changeDetector.detectChanges(); @@ -79,6 +83,7 @@ export class LifeCycle { } } finally { this._runningTick = false; + wtfLeave(s); } } } diff --git a/modules/angular2/src/core/zone/ng_zone.ts b/modules/angular2/src/core/zone/ng_zone.ts index bbef67832d..e44f7cc924 100644 --- a/modules/angular2/src/core/zone/ng_zone.ts +++ b/modules/angular2/src/core/zone/ng_zone.ts @@ -1,5 +1,7 @@ import {List, ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection'; import {normalizeBlank, isPresent, global} from 'angular2/src/facade/lang'; +import {wtfLeave, wtfCreateScope, WtfScopeFn} from '../../profile/profile'; + export interface NgZoneZone extends Zone { _innerZone: boolean; } @@ -13,6 +15,9 @@ export interface NgZoneZone extends Zone { _innerZone: boolean; } * `Zone`. The default `onTurnDone` runs the Angular change detection. */ export class NgZone { + _zone_run_scope: WtfScopeFn = wtfCreateScope(`NgZone#run()`); + _zone_microtask: WtfScopeFn = wtfCreateScope(`NgZone#microtask()`); + // Code executed in _mountZone does not trigger the onTurnDone. _mountZone; // _innerZone is the child of _mountZone. Any code executed in this zone will trigger the @@ -134,7 +139,12 @@ export class NgZone { */ run(fn: () => any): any { if (this._disabled) { - return fn(); + var s = this._zone_run_scope(); + try { + return fn(); + } finally { + wtfLeave(s); + } } else { return this._innerZone.run(fn); } @@ -165,6 +175,7 @@ export class NgZone { } _createInnerZone(zone, enableLongStackTrace) { + var _zone_microtask = this._zone_microtask; var ngZone = this; var errorHandling; @@ -217,10 +228,12 @@ export class NgZone { return function(fn) { ngZone._pendingMicrotasks++; var microtask = function() { + var s = _zone_microtask(); try { fn(); } finally { ngZone._pendingMicrotasks--; + wtfLeave(s); } }; parentScheduleMicrotask.call(this, microtask); diff --git a/modules/angular2/src/profile/profile.ts b/modules/angular2/src/profile/profile.ts new file mode 100644 index 0000000000..3aff501e70 --- /dev/null +++ b/modules/angular2/src/profile/profile.ts @@ -0,0 +1,80 @@ +export {WtfScopeFn} from './wtf_impl'; + +import * as impl from "./wtf_impl"; + +// Change exports to const once https://github.com/angular/ts2dart/issues/150 + +/** + * True if WTF is enabled. + */ +export var wtfEnabled = impl.detectWTF(); + +function noopScope(arg0?: any, arg1?: any): any { + return null; +} + +/** + * Create trace scope. + * + * Scopes must be strictly nested and are analogous to stack frames, but + * do not have to follow the stack frames. Instead it is recommended that they follow logical + * nesting. You may want to use + * [Event + * Signatures](http://google.github.io/tracing-framework/instrumenting-code.html#custom-events) + * as they are defined in WTF. + * + * Used to mark scope entry. The return value is used to leave the scope. + * + * final myScope = wtfCreateScope('MyClass#myMethod(ascii someVal)'); + * + * someMethod() { + * var s = myScope('Foo'); // 'Foo' gets stored in tracing UI + * // DO SOME WORK HERE + * return wtfLeave(s, 123); // Return value 123 + * } + * + * Note, adding try-finally block around the work to ensure that `wtfLeave` gets called can + * negatively impact the performance of your application. For this reason we recommend that + * you don't add them to ensure that `wtfLeave` gets called. In production `wtfLeave` is a noop and + * so try-finally block has no value. When debugging perf issues, skipping `wtfLeave`, do to + * exception, will produce incorrect trace, but presence of exception signifies logic error which + * needs to be fixed before the app should be profiled. Add try-finally only when you expect that + * an exception is expected during normal execution while profiling. + * + */ +export var wtfCreateScope: (signature: string, flags?: any) => impl.WtfScopeFn = + wtfEnabled ? impl.createScope : (signature: string, flags?: any) => noopScope; + +/** + * Used to mark end of Scope. + * + * - `scope` to end. + * - `returnValue` (optional) to be passed to the WTF. + * + * Returns the `returnValue for easy chaining. + */ +export var wtfLeave:(scope: any, returnValue?: T) => T = + wtfEnabled ? impl.leave : (s: any, r?: any) => r; + +/** + * Used to mark Async start. Async are similar to scope but they don't have to be strictly nested. + * The return value is used in the call to [endAsync]. Async ranges only work if WTF has been + * enabled. + * + * someMethod() { + * var s = wtfStartTimeRange('HTTP:GET', 'some.url'); + * var future = new Future.delay(5).then((_) { + * wtfEndTimeRange(s); + * }); + * } + */ +export var wtfStartTimeRange: (rangeType: string, action: string) => any = + wtfEnabled ? impl.startTimeRange : (rangeType: string, action: string) => null; + +/** + * Ends a async time range operation. + * [range] is the return value from [wtfStartTimeRange] Async ranges only work if WTF has been + * enabled. + */ +export var wtfEndTimeRange: (range: any) => void = wtfEnabled ? impl.endTimeRange : (r: any) => + null; diff --git a/modules/angular2/src/profile/wtf_impl.dart b/modules/angular2/src/profile/wtf_impl.dart new file mode 100644 index 0000000000..21471bf998 --- /dev/null +++ b/modules/angular2/src/profile/wtf_impl.dart @@ -0,0 +1,96 @@ +/** + * Tracing for Dart applications. + * + * The tracing API hooks up to either [WTF](http://google.github.io/tracing-framework/) or + * [Dart Observatory](https://www.dartlang.org/tools/observatory/). + */ +library angular2.src.core.wtf_impl; + +typedef dynamic WtfScopeFn([arg0, arg1]); + +var context = null; +var _trace; +var _events; +var _createScope; +var _leaveScope; +var _beginTimeRange; +var _endTimeRange; +final List _arg1 = [null]; +final List _arg2 = [null, null]; + +bool detectWTF() { + if (context != null && context.hasProperty('wtf')) { + var wtf = context['wtf']; + if (wtf.hasProperty('trace')) { + _trace = wtf['trace']; + _events = _trace['events']; + _createScope = _events['createScope']; + _leaveScope = _trace['leaveScope']; + _beginTimeRange = _trace['beginTimeRange']; + _endTimeRange = _trace['endTimeRange']; + return true; + } + } + return false; +} + +int getArgSize(String signature) { + int start = signature.indexOf('(') + 1; + int end = signature.indexOf(')', start); + bool found = false; + int count = 0; + for(var i = start; i < end; i++) { + var ch = signature[i]; + if (identical(ch, ',')) { + found = false; + } + if (!found) { + found = true; + count ++; + } + } + return count; +} + +dynamic createScope(String signature, [flags]) { + _arg2[0] = signature; + _arg2[1] = flags; + var jsScope = _createScope.apply(_arg2, thisArg: _events); + switch(getArgSize(signature)) { + case 0: + return ([arg0, arg1]) { + return jsScope.apply(const []); + }; + case 1: + return ([arg0, arg1]) { + _arg1[0] = arg0; + return jsScope.apply(_arg1); + }; + case 2: + return ([arg0, arg1]) { + _arg2[0] = arg0; + _arg2[1] = arg1; + return jsScope.apply(_arg1); + }; + default: + throw "Max 2 arguments are supported."; + } +} + +void leave(scope, [returnValue]) { + _arg2[0] = scope; + _arg2[1] = returnValue; + _leaveScope.apply(_arg2, thisArg: _trace); + return returnValue; +} + +dynamic startTimeRange(String rangeType, String action) { + _arg2[0] = rangeType; + _arg2[1] = action; + return _beginTimeRange.apply(_arg2, thisArg: _trace); +} + +void endTimeRange(dynamic range) { + _arg1[0] = range; + _endTimeRange.apply(_arg1, thisArg: _trace); +} diff --git a/modules/angular2/src/profile/wtf_impl.ts b/modules/angular2/src/profile/wtf_impl.ts new file mode 100644 index 0000000000..54ca43a075 --- /dev/null +++ b/modules/angular2/src/profile/wtf_impl.ts @@ -0,0 +1,56 @@ +import {global} from '../facade/lang'; + +export interface WtfScopeFn { (arg0?: any, arg1?: any): any; } + +interface WTF { + trace: Trace; +} + +interface Trace { + events: Events; + leaveScope(scope: Scope, returnValue: any); + beginTimeRange(rangeType: string, action: string): Range; + endTimeRange(range: Range); +} + +interface Range {} + +interface Events { + createScope(signature: string, flags: any): Scope; +} + +interface Scope { + (...args): any; +} + +var trace: Trace; +var events: Events; + +export function detectWTF(): boolean { + var wtf: WTF = global['wtf']; + if (wtf) { + trace = wtf['trace']; + if (trace) { + events = trace['events']; + return true; + } + } + return false; +} + +export function createScope(signature: string, flags: any = null): any { + return events.createScope(signature, flags); +} + +export function leave(scope: Scope, returnValue?: T): T { + trace.leaveScope(scope, returnValue); + return returnValue; +} + +export function startTimeRange(rangeType: string, action: string): Range { + return trace.beginTimeRange(rangeType, action); +} + +export function endTimeRange(range: Range): void { + trace.endTimeRange(range); +} diff --git a/modules/angular2/src/profile/wtf_init.dart b/modules/angular2/src/profile/wtf_init.dart new file mode 100644 index 0000000000..870eb5f74d --- /dev/null +++ b/modules/angular2/src/profile/wtf_init.dart @@ -0,0 +1,14 @@ +library angular2.src.core.wtf_init; + +import 'dart:js' as js; +import 'wtf_impl.dart' as impl; + +/** + * Must be executed explicitly in Dart to set the JS Context. + * + * NOTE: this is done explicitly to allow WTF api not to depend on + * JS context and possible to run the noop WTF stubs outside the browser. + */ +wtfInit() { + impl.context = js.context; +} diff --git a/modules/angular2/src/profile/wtf_init.ts b/modules/angular2/src/profile/wtf_init.ts new file mode 100644 index 0000000000..7601efe72a --- /dev/null +++ b/modules/angular2/src/profile/wtf_init.ts @@ -0,0 +1,4 @@ +/** + * This is here because DART requires it. It is noop in JS. + */ +export function wtfInit() {} diff --git a/modules/angular2/src/render/dom/compiler/view_loader.ts b/modules/angular2/src/render/dom/compiler/view_loader.ts index 1f806d503a..aa140268ae 100644 --- a/modules/angular2/src/render/dom/compiler/view_loader.ts +++ b/modules/angular2/src/render/dom/compiler/view_loader.ts @@ -16,6 +16,7 @@ import {XHR} from 'angular2/src/render/xhr'; import {StyleInliner} from './style_inliner'; import {StyleUrlResolver} from './style_url_resolver'; +import {wtfStartTimeRange, wtfEndTimeRange} from '../../../profile/profile'; export class TemplateAndStyles { constructor(public template: string, public styles: string[]) {} @@ -33,6 +34,7 @@ export class ViewLoader { private _styleUrlResolver: StyleUrlResolver) {} load(viewDef: ViewDefinition): Promise { + var r = wtfStartTimeRange('ViewLoader#load()', stringify(viewDef.componentId)); let tplAndStyles: List| Promise| string> = [this._loadHtml(viewDef.template, viewDef.templateAbsUrl)]; if (isPresent(viewDef.styles)) { @@ -56,8 +58,10 @@ export class ViewLoader { let loadedTplAndStyles = res[0]; let styles = ListWrapper.slice(res, 1); - return new TemplateAndStyles(loadedTplAndStyles.template, - loadedTplAndStyles.styles.concat(styles)); + var templateAndStyles = new TemplateAndStyles(loadedTplAndStyles.template, + loadedTplAndStyles.styles.concat(styles)); + wtfEndTimeRange(r); + return templateAndStyles; }); } diff --git a/modules/angular2/src/render/dom/dom_renderer.ts b/modules/angular2/src/render/dom/dom_renderer.ts index 32350ca698..ccc4b937a8 100644 --- a/modules/angular2/src/render/dom/dom_renderer.ts +++ b/modules/angular2/src/render/dom/dom_renderer.ts @@ -22,6 +22,7 @@ import { cloneAndQueryProtoView, camelCaseToDashCase } from './util'; +import {WtfScopeFn, wtfLeave, wtfCreateScope} from '../../profile/profile'; import { Renderer, @@ -38,6 +39,7 @@ import {DOCUMENT_TOKEN, DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES} from './dom_tokens const REFLECT_PREFIX: string = 'ng-reflect-'; + @Injectable() export class DomRenderer extends Renderer { _document; @@ -53,19 +55,24 @@ export class DomRenderer extends Renderer { this._document = document; } + _scope_createRootHostView: WtfScopeFn = wtfCreateScope('DomRenderer#createRootHostView()'); createRootHostView(hostProtoViewRef: RenderProtoViewRef, fragmentCount: number, hostElementSelector: string): RenderViewWithFragments { + var s = this._scope_createRootHostView(); var hostProtoView = resolveInternalDomProtoView(hostProtoViewRef); var element = DOM.querySelector(this._document, hostElementSelector); if (isBlank(element)) { + wtfLeave(s); throw new BaseException(`The selector "${hostElementSelector}" did not match any elements`); } - return this._createView(hostProtoView, element); + return wtfLeave(s, this._createView(hostProtoView, element)); } + _scope_createView = wtfCreateScope('DomRenderer#createView()'); createView(protoViewRef: RenderProtoViewRef, fragmentCount: number): RenderViewWithFragments { + var s = this._scope_createView(); var protoView = resolveInternalDomProtoView(protoViewRef); - return this._createView(protoView, null); + return wtfLeave(s, this._createView(protoView, null)); } destroyView(viewRef: RenderViewRef) { @@ -109,11 +116,14 @@ export class DomRenderer extends Renderer { moveNodesAfterSibling(element, resolveInternalDomFragment(fragmentRef)); } + _scope_detachFragment = wtfCreateScope('DomRenderer#detachFragment()'); detachFragment(fragmentRef: RenderFragmentRef) { + var s = this._scope_detachFragment(); var fragmentNodes = resolveInternalDomFragment(fragmentRef); for (var i = 0; i < fragmentNodes.length; i++) { DOM.remove(fragmentNodes[i]); } + wtfLeave(s); } hydrateView(viewRef: RenderViewRef) { @@ -203,9 +213,12 @@ export class DomRenderer extends Renderer { DOM.setText(view.boundTextNodes[textNodeIndex], text); } + _scope_setEventDispatcher = wtfCreateScope('DomRenderer#setEventDispatcher()'); setEventDispatcher(viewRef: RenderViewRef, dispatcher: any /*api.EventDispatcher*/): void { + var s = this._scope_setEventDispatcher(); var view = resolveInternalDomView(viewRef); view.eventDispatcher = dispatcher; + wtfLeave(s); } _createView(protoView: DomProtoView, inplaceElement: HTMLElement): RenderViewWithFragments { diff --git a/modules/angular2/test/profile/wtf_impl_spec.dart b/modules/angular2/test/profile/wtf_impl_spec.dart new file mode 100644 index 0000000000..4be667f2aa --- /dev/null +++ b/modules/angular2/test/profile/wtf_impl_spec.dart @@ -0,0 +1,18 @@ +/// This file contains tests that make sense only in Dart +library angular2.test.core.wtf_impl; + +import 'package:angular2/test_lib.dart'; +import 'package:angular2/src/profile/wtf_impl.dart' as impl; + +main() { + describe('WTF', () { + describe('getArgSize', () { + it("should parse args", () { + expect(impl.getArgSize('foo#bar')).toBe(0); + expect(impl.getArgSize('foo#bar()')).toBe(0); + expect(impl.getArgSize('foo#bar(foo bar)')).toBe(1); + expect(impl.getArgSize('foo#bar(foo bar, baz q)')).toBe(2); + }); + }); + }); +}