From d1dd964a5a37f6421cabe478e4ff6a5fee9b550c Mon Sep 17 00:00:00 2001 From: "deploy-test@travis-ci.org" Date: Tue, 10 Jun 2014 23:37:05 +1000 Subject: [PATCH] feat(Scope): Instrument Scope to use User tags in Dart Obervatory --- lib/change_detection/watch_group.dart | 38 ++++++++++++++--- lib/core/module_internal.dart | 1 + lib/core/scope.dart | 59 ++++++++++++++++++++++----- 3 files changed, 81 insertions(+), 17 deletions(-) diff --git a/lib/change_detection/watch_group.dart b/lib/change_detection/watch_group.dart index f257260d5..bf089ec58 100644 --- a/lib/change_detection/watch_group.dart +++ b/lib/change_detection/watch_group.dart @@ -1,5 +1,6 @@ library angular.watch_group; +import 'dart:profiler'; import 'package:angular/change_detection/change_detection.dart'; import 'dart:collection'; @@ -367,6 +368,8 @@ class WatchGroup implements _EvalWatchList, _WatchGroupList { class RootWatchGroup extends WatchGroup { final FieldGetterFactory _fieldGetterFactory; Watch _dirtyWatchHead, _dirtyWatchTail; + List _detectChangesTags = []; + List _reactionFnTags = []; /** * Every time a [WatchGroup] is destroyed we increment the counter. During @@ -378,10 +381,22 @@ class RootWatchGroup extends WatchGroup { int _removeCount = 0; - RootWatchGroup(this._fieldGetterFactory, - ChangeDetector changeDetector, - Object context) - : super._root(changeDetector, context); + RootWatchGroup(this._fieldGetterFactory, ChangeDetector changeDetector, Object context, { + bool profile: false, int ttl }) + : super._root(changeDetector, context) { + if (profile) { + _detectChangesTags = new List(ttl + 2); + _reactionFnTags = new List(ttl + 2); + for (int i = 0; i < ttl; i++) { + _detectChangesTags[i] = new UserTag('DetectChanges / NgDigest${i}'); + _reactionFnTags[i] = new UserTag('ReactionFn / NgDigest${i}'); + } + _detectChangesTags[ttl] = new UserTag('DetectChanges / NgFlush'); + _detectChangesTags[ttl + 1] = new UserTag('DetectChanges / NgAssert'); + _reactionFnTags[ttl] = new UserTag('ReactionFn / NgFlush'); + _reactionFnTags[ttl + 1] = new UserTag('ReactionFn / NgAssert'); + } + } RootWatchGroup get _rootGroup => this; @@ -400,7 +415,10 @@ class RootWatchGroup extends WatchGroup { ChangeLog changeLog, AvgStopwatch fieldStopwatch, AvgStopwatch evalStopwatch, - AvgStopwatch processStopwatch}) { + AvgStopwatch processStopwatch, + num sequenceNumber}) { + var previous; + if (sequenceNumber != null) previous = _detectChangesTags[sequenceNumber].makeCurrent(); // Process the Records from the change detector Iterator> changedRecordIterator = (_changeDetector as ChangeDetector<_Handler>).collectChanges( @@ -448,7 +466,14 @@ class RootWatchGroup extends WatchGroup { count++; try { if (root._removeCount == 0 || dirtyWatch._watchGroup.isAttached) { - dirtyWatch.invoke(); + if (sequenceNumber != null) { + var previous = _reactionFnTags[sequenceNumber].makeCurrent(); + dirtyWatch.invoke(); + previous.makeCurrent(); + } + else { + dirtyWatch.invoke(); + } } } catch (e, s) { if (exceptionHandler == null) rethrow; else exceptionHandler(e, s); @@ -462,6 +487,7 @@ class RootWatchGroup extends WatchGroup { root._removeCount = 0; } if (processStopwatch != null) processStopwatch..stop()..increment(count); + if (previous != null) previous.makeCurrent(); return count; } diff --git a/lib/core/module_internal.dart b/lib/core/module_internal.dart index 04ed84507..d1201bad3 100644 --- a/lib/core/module_internal.dart +++ b/lib/core/module_internal.dart @@ -3,6 +3,7 @@ library angular.core_internal; import 'dart:async' as async; import 'dart:collection'; import 'dart:math'; +import 'dart:profiler'; import 'package:intl/intl.dart'; import 'package:di/di.dart'; diff --git a/lib/core/scope.dart b/lib/core/scope.dart index 9a875d8c2..96816331f 100644 --- a/lib/core/scope.dart +++ b/lib/core/scope.dart @@ -3,6 +3,13 @@ part of angular.core_internal; typedef EvalFunction0(); typedef EvalFunction1(context); +final UserTag APPLY_USER_TAG = new UserTag('Apply'); +final UserTag FLUSH_TAG = new UserTag('NgFlush'); +final UserTag RUN_ASYNC_TAG = new UserTag('NgRunAsync'); +final UserTag DOM_READ_TAG = new UserTag('NgDomRead'); +final UserTag DOM_WRITE_TAG = new UserTag('NgDomWrite'); +final UserTag ASSERT_TAG = new UserTag('NgAssert'); + /** * Injected into the listener function within [Scope.on] to provide * event-specific details to the scope listener. @@ -282,6 +289,7 @@ class Scope { } dynamic apply([expression, Map locals]) { + var previous = APPLY_USER_TAG.makeCurrent(); _assertInternalStateConsistency(); rootScope._transitionState(null, RootScope.STATE_APPLY); try { @@ -289,6 +297,7 @@ class Scope { } catch (e, s) { rootScope._exceptionHandler(e, s); } finally { + previous.makeCurrent(); rootScope.._transitionState(RootScope.STATE_APPLY, null) ..digest() ..flush(); @@ -567,6 +576,8 @@ class RootScope extends Scope { String _state; + final List _digestTags = []; + /** * * While processing data bindings, Angular passes through multiple states. When testing or @@ -616,23 +627,29 @@ class RootScope extends Scope { */ String get state => _state; - RootScope(Object context, Parser parser, ASTParser astParser, FieldGetterFactory fieldGetterFactory, - FormatterMap formatters, this._exceptionHandler, this._ttl, this._zone, - ScopeStats _scopeStats) + RootScope(Object context, Parser parser, ASTParser astParser, + FieldGetterFactory fieldGetterFactory, FormatterMap formatters, this._exceptionHandler, + ScopeDigestTTL ttl, this._zone, ScopeStats _scopeStats) : _scopeStats = _scopeStats, + _ttl = ttl, _parser = parser, _astParser = astParser, super(context, null, null, new RootWatchGroup(fieldGetterFactory, - new DirtyCheckingChangeDetector(fieldGetterFactory), context), + new DirtyCheckingChangeDetector(fieldGetterFactory), context, profile: true, + ttl: ttl.ttl), new RootWatchGroup(fieldGetterFactory, - new DirtyCheckingChangeDetector(fieldGetterFactory), context), + new DirtyCheckingChangeDetector(fieldGetterFactory), context, profile: true, + ttl: ttl.ttl), '', _scopeStats) { _zone.onTurnDone = apply; _zone.onError = (e, s, ls) => _exceptionHandler(e, s); _zone.onScheduleMicrotask = runAsync; + for(num i = 0; i <= _ttl.ttl; i++) { + _digestTags.add(new UserTag('NgDigest${i}')); + } } RootScope get rootScope => this; @@ -656,11 +673,12 @@ class RootScope extends Scope { * [ScopeDigestTTL]. */ void digest() { + int digestTTL = _ttl.ttl; _transitionState(null, STATE_DIGEST); try { var rootWatchGroup = _readWriteGroup as RootWatchGroup; - int digestTTL = _ttl.ttl; + const int LOG_COUNT = 3; List log; List digestLog; @@ -668,6 +686,8 @@ class RootScope extends Scope { ChangeLog changeLog; _scopeStats.digestStart(); do { + var sequenceNo = _ttl.ttl - digestTTL; + var previousTag = _digestTags[sequenceNo].makeCurrent(); int asyncCount = _runAsyncFns(); @@ -677,7 +697,8 @@ class RootScope extends Scope { changeLog: changeLog, fieldStopwatch: _scopeStats.fieldStopwatch, evalStopwatch: _scopeStats.evalStopwatch, - processStopwatch: _scopeStats.processStopwatch); + processStopwatch: _scopeStats.processStopwatch, + sequenceNumber: sequenceNo); if (digestTTL <= LOG_COUNT) { if (changeLog == null) { @@ -690,10 +711,13 @@ class RootScope extends Scope { } } if (digestTTL == 0) { + if (previousTag != null) previousTag.makeCurrent(); throw 'Model did not stabilize in ${_ttl.ttl} digests. ' - 'Last $LOG_COUNT iterations:\n${log.join('\n')}'; + 'Last $LOG_COUNT iterations:\n${log.join('\n')}'; } + _scopeStats.digestLoop(count); + previousTag.makeCurrent(); } while (count > 0 || _runAsyncHead != null); } finally { _scopeStats.digestEnd(); @@ -702,6 +726,7 @@ class RootScope extends Scope { } void flush() { + var previousTag = FLUSH_TAG.makeCurrent(); _stats.flushStart(); _transitionState(null, STATE_FLUSH); RootWatchGroup readOnlyGroup = this._readOnlyGroup as RootWatchGroup; @@ -724,7 +749,8 @@ class RootScope extends Scope { readOnlyGroup.detectChanges(exceptionHandler:_exceptionHandler, fieldStopwatch: _scopeStats.fieldStopwatch, evalStopwatch: _scopeStats.evalStopwatch, - processStopwatch: _scopeStats.processStopwatch); + processStopwatch: _scopeStats.processStopwatch, + sequenceNumber: _ttl.ttl); } if (_domReadHead != null) _stats.domReadStart(); while (_domReadHead != null) { @@ -741,6 +767,7 @@ class RootScope extends Scope { } while (_domWriteHead != null || _domReadHead != null || _runAsyncHead != null); _stats.flushEnd(); assert((() { + var previousTag = ASSERT_TAG.makeCurrent(); _stats.flushAssertStart(); var digestLog = []; var flushLog = []; @@ -748,23 +775,27 @@ class RootScope extends Scope { changeLog: (s, c, p) => digestLog.add('$s: $c <= $p'), fieldStopwatch: _scopeStats.fieldStopwatch, evalStopwatch: _scopeStats.evalStopwatch, - processStopwatch: _scopeStats.processStopwatch); + processStopwatch: _scopeStats.processStopwatch, + sequenceNumber: _ttl.ttl + 1); (_readOnlyGroup as RootWatchGroup).detectChanges( changeLog: (s, c, p) => flushLog.add('$s: $c <= $p'), fieldStopwatch: _scopeStats.fieldStopwatch, evalStopwatch: _scopeStats.evalStopwatch, - processStopwatch: _scopeStats.processStopwatch); + processStopwatch: _scopeStats.processStopwatch, + sequenceNumber: _ttl.ttl + 1); if (digestLog.isNotEmpty || flushLog.isNotEmpty) { throw 'Observer reaction functions should not change model. \n' 'These watch changes were detected: ${digestLog.join('; ')}\n' 'These observe changes were detected: ${flushLog.join('; ')}'; } _stats.flushAssertEnd(); + previousTag.makeCurrent(); return true; })()); } finally { _stats.cycleEnd(); _transitionState(STATE_FLUSH, null); + previousTag.makeCurrent(); } } @@ -773,12 +804,14 @@ class RootScope extends Scope { if (_state == STATE_FLUSH_ASSERT) { throw "Scheduling microtasks not allowed in $state state."; } + var previousTag = RUN_ASYNC_TAG.makeCurrent(); var chain = new _FunctionChain(fn); if (_runAsyncHead == null) { _runAsyncHead = _runAsyncTail = chain; } else { _runAsyncTail = _runAsyncTail._next = chain; } + previousTag.makeCurrent(); } _runAsyncFns() { @@ -797,21 +830,25 @@ class RootScope extends Scope { } void domWrite(fn()) { + var previousTag = DOM_WRITE_TAG.makeCurrent(); var chain = new _FunctionChain(fn); if (_domWriteHead == null) { _domWriteHead = _domWriteTail = chain; } else { _domWriteTail = _domWriteTail._next = chain; } + previousTag.makeCurrent(); } void domRead(fn()) { + var previousTag = DOM_READ_TAG.makeCurrent(); var chain = new _FunctionChain(fn); if (_domReadHead == null) { _domReadHead = _domReadTail = chain; } else { _domReadTail = _domReadTail._next = chain; } + previousTag.makeCurrent(); } void destroy() {}