Skip to content
This repository has been archived by the owner on Feb 22, 2018. It is now read-only.

Commit

Permalink
fix(scope): should not trigger assertions on fork
Browse files Browse the repository at this point in the history
  • Loading branch information
pavelgj authored and mhevery committed Feb 21, 2014
1 parent a7cabe3 commit 484f03d
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 12 deletions.
36 changes: 25 additions & 11 deletions lib/core/scope.dart
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,9 @@ class Scope {
Scope _childHead, _childTail, _next, _prev;
_Streams _streams;

/// Do not use. Exposes internal state for testing.
bool get hasOwnStreams => _streams != null && _streams._scope == this;

Scope(Object this.context, this.rootScope, this._parentScope,
this._readWriteGroup, this._readOnlyGroup);

Expand All @@ -192,7 +195,7 @@ class Scope {
*/
Watch watch(expression, ReactionFn reactionFn,
{context, FilterMap filters, bool readOnly: false}) {
_assertInternalStateConsistency();
assert(isAttached);
assert(expression != null);
AST ast;
Watch watch;
Expand Down Expand Up @@ -606,18 +609,29 @@ class _Streams {
static ScopeStream on(Scope scope,
ExceptionHandler _exceptionHandler,
String name) {
var scopeStream = scope._streams;
if (scopeStream == null || scopeStream._scope != scope) {
// We either don't have [_ScopeStreams] or it is inherited.
var newStreams = new _Streams(scope, _exceptionHandler, scopeStream);
var scopeCursor = scope;
while (scopeCursor != null && scopeCursor._streams == scopeStream) {
scopeCursor._streams = newStreams;
scopeCursor = scopeCursor._parentScope;
_forceNewScopeStream(scope, _exceptionHandler);
return scope._streams._get(scope, name);
}

static void _forceNewScopeStream(scope, _exceptionHandler) {
_Streams streams = scope._streams;
Scope scopeCursor = scope;
bool splitMode = false;
while(scopeCursor != null) {
_Streams cursorStreams = scopeCursor._streams;
var hasStream = cursorStreams != null;
var hasOwnStream = hasStream && cursorStreams._scope == scopeCursor;
if (hasOwnStream) return;

if (!splitMode && (streams == null || (hasStream && !hasOwnStream))) {
if (hasStream && !hasOwnStream) {
splitMode = true;
}
streams = new _Streams(scopeCursor, _exceptionHandler, cursorStreams);
}
scopeStream = newStreams;
scopeCursor._streams = streams;
scopeCursor = scopeCursor._parentScope;
}
return scopeStream._get(scope, name);
}

static void destroy(Scope scope) {
Expand Down
55 changes: 54 additions & 1 deletion test/core/scope_spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,59 @@ main() => describe('scope', () {
child.broadcast('abc');
expect(log).toEqual('');
}));

it('should not trigger assertions on scope fork', inject((RootScope root) {
var d1 = root.createChild({});
var d2 = root.createChild({});
var d3 = d2.createChild({});
expect(root.apply).not.toThrow();
d1.on(ScopeEvent.DESTROY).listen((_) => null);
expect(root.apply).not.toThrow();
d3.on(ScopeEvent.DESTROY).listen((_) => null);
expect(root.apply).not.toThrow();
d2.on(ScopeEvent.DESTROY).listen((_) => null);
expect(root.apply).not.toThrow();
}));

it('should not too eagerly create own streams', inject((RootScope root) {
var a = root.createChild({});
var a2 = root.createChild({});
var b = a.createChild({});
var c = b.createChild({});
var d = c.createChild({});
var e = d.createChild({});

getStreamState() => [root.hasOwnStreams, a.hasOwnStreams, a2.hasOwnStreams,
b.hasOwnStreams, c.hasOwnStreams, d.hasOwnStreams,
e.hasOwnStreams];

expect(getStreamState()).toEqual([false, false, false, false, false, false, false]);
expect(root.apply).not.toThrow();

e.on(ScopeEvent.DESTROY).listen((_) => null);
expect(getStreamState()).toEqual([false, false, false, false, false, false, true]);
expect(root.apply).not.toThrow();

d.on(ScopeEvent.DESTROY).listen((_) => null);
expect(getStreamState()).toEqual([false, false, false, false, false, true, true]);
expect(root.apply).not.toThrow();

b.on(ScopeEvent.DESTROY).listen((_) => null);
expect(getStreamState()).toEqual([false, false, false, true, false, true, true]);
expect(root.apply).not.toThrow();

c.on(ScopeEvent.DESTROY).listen((_) => null);
expect(getStreamState()).toEqual([false, false, false, true, true, true, true]);
expect(root.apply).not.toThrow();

a.on(ScopeEvent.DESTROY).listen((_) => null);
expect(getStreamState()).toEqual([false, true, false, true, true, true, true]);
expect(root.apply).not.toThrow();

a2.on(ScopeEvent.DESTROY).listen((_) => null);
expect(getStreamState()).toEqual([true, true, true, true, true, true, true]);
expect(root.apply).not.toThrow();
}));
});


Expand Down Expand Up @@ -1047,7 +1100,7 @@ main() => describe('scope', () {
}));
});


describe('runAsync', () {
it(r'should run callback before watch', inject((RootScope rootScope) {
var log = '';
Expand Down

0 comments on commit 484f03d

Please sign in to comment.