Skip to content
This repository was archived by the owner on Feb 22, 2018. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 32 additions & 6 deletions lib/change_detection/watch_group.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
library angular.watch_group;

import 'dart:profiler';
import 'package:angular/change_detection/change_detection.dart';
import 'dart:collection';

Expand Down Expand Up @@ -367,6 +368,8 @@ class WatchGroup implements _EvalWatchList, _WatchGroupList {
class RootWatchGroup extends WatchGroup {
final FieldGetterFactory _fieldGetterFactory;
Watch _dirtyWatchHead, _dirtyWatchTail;
List<UserTag> _detectChangesTags = [];
List<UserTag> _reactionFnTags = [];

/**
* Every time a [WatchGroup] is destroyed we increment the counter. During
Expand All @@ -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;

Expand All @@ -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<Record<_Handler>> changedRecordIterator =
(_changeDetector as ChangeDetector<_Handler>).collectChanges(
Expand Down Expand Up @@ -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);
Expand All @@ -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;
}

Expand Down
1 change: 1 addition & 0 deletions lib/core/module_internal.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
59 changes: 48 additions & 11 deletions lib/core/scope.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -282,13 +289,15 @@ class Scope {
}

dynamic apply([expression, Map locals]) {
var previous = APPLY_USER_TAG.makeCurrent();
_assertInternalStateConsistency();
rootScope._transitionState(null, RootScope.STATE_APPLY);
try {
return eval(expression, locals);
} catch (e, s) {
rootScope._exceptionHandler(e, s);
} finally {
previous.makeCurrent();
rootScope.._transitionState(RootScope.STATE_APPLY, null)
..digest()
..flush();
Expand Down Expand Up @@ -567,6 +576,8 @@ class RootScope extends Scope {

String _state;

final List<UserTag> _digestTags = [];

/**
*
* While processing data bindings, Angular passes through multiple states. When testing or
Expand Down Expand Up @@ -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;
Expand All @@ -656,18 +673,21 @@ 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;
var count;
ChangeLog changeLog;
_scopeStats.digestStart();
do {
var sequenceNo = _ttl.ttl - digestTTL;
var previousTag = _digestTags[sequenceNo].makeCurrent();

int asyncCount = _runAsyncFns();

Expand All @@ -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) {
Expand All @@ -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();
Expand All @@ -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;
Expand All @@ -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) {
Expand All @@ -741,30 +767,35 @@ 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 = [];
(_readWriteGroup as RootWatchGroup).detectChanges(
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();
}
}

Expand All @@ -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() {
Expand All @@ -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() {}
Expand Down