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

Commit 484f03d

Browse files
pavelgjmhevery
authored andcommitted
fix(scope): should not trigger assertions on fork
1 parent a7cabe3 commit 484f03d

File tree

2 files changed

+79
-12
lines changed

2 files changed

+79
-12
lines changed

lib/core/scope.dart

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,9 @@ class Scope {
181181
Scope _childHead, _childTail, _next, _prev;
182182
_Streams _streams;
183183

184+
/// Do not use. Exposes internal state for testing.
185+
bool get hasOwnStreams => _streams != null && _streams._scope == this;
186+
184187
Scope(Object this.context, this.rootScope, this._parentScope,
185188
this._readWriteGroup, this._readOnlyGroup);
186189

@@ -192,7 +195,7 @@ class Scope {
192195
*/
193196
Watch watch(expression, ReactionFn reactionFn,
194197
{context, FilterMap filters, bool readOnly: false}) {
195-
_assertInternalStateConsistency();
198+
assert(isAttached);
196199
assert(expression != null);
197200
AST ast;
198201
Watch watch;
@@ -606,18 +609,29 @@ class _Streams {
606609
static ScopeStream on(Scope scope,
607610
ExceptionHandler _exceptionHandler,
608611
String name) {
609-
var scopeStream = scope._streams;
610-
if (scopeStream == null || scopeStream._scope != scope) {
611-
// We either don't have [_ScopeStreams] or it is inherited.
612-
var newStreams = new _Streams(scope, _exceptionHandler, scopeStream);
613-
var scopeCursor = scope;
614-
while (scopeCursor != null && scopeCursor._streams == scopeStream) {
615-
scopeCursor._streams = newStreams;
616-
scopeCursor = scopeCursor._parentScope;
612+
_forceNewScopeStream(scope, _exceptionHandler);
613+
return scope._streams._get(scope, name);
614+
}
615+
616+
static void _forceNewScopeStream(scope, _exceptionHandler) {
617+
_Streams streams = scope._streams;
618+
Scope scopeCursor = scope;
619+
bool splitMode = false;
620+
while(scopeCursor != null) {
621+
_Streams cursorStreams = scopeCursor._streams;
622+
var hasStream = cursorStreams != null;
623+
var hasOwnStream = hasStream && cursorStreams._scope == scopeCursor;
624+
if (hasOwnStream) return;
625+
626+
if (!splitMode && (streams == null || (hasStream && !hasOwnStream))) {
627+
if (hasStream && !hasOwnStream) {
628+
splitMode = true;
629+
}
630+
streams = new _Streams(scopeCursor, _exceptionHandler, cursorStreams);
617631
}
618-
scopeStream = newStreams;
632+
scopeCursor._streams = streams;
633+
scopeCursor = scopeCursor._parentScope;
619634
}
620-
return scopeStream._get(scope, name);
621635
}
622636

623637
static void destroy(Scope scope) {

test/core/scope_spec.dart

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,59 @@ main() => describe('scope', () {
267267
child.broadcast('abc');
268268
expect(log).toEqual('');
269269
}));
270+
271+
it('should not trigger assertions on scope fork', inject((RootScope root) {
272+
var d1 = root.createChild({});
273+
var d2 = root.createChild({});
274+
var d3 = d2.createChild({});
275+
expect(root.apply).not.toThrow();
276+
d1.on(ScopeEvent.DESTROY).listen((_) => null);
277+
expect(root.apply).not.toThrow();
278+
d3.on(ScopeEvent.DESTROY).listen((_) => null);
279+
expect(root.apply).not.toThrow();
280+
d2.on(ScopeEvent.DESTROY).listen((_) => null);
281+
expect(root.apply).not.toThrow();
282+
}));
283+
284+
it('should not too eagerly create own streams', inject((RootScope root) {
285+
var a = root.createChild({});
286+
var a2 = root.createChild({});
287+
var b = a.createChild({});
288+
var c = b.createChild({});
289+
var d = c.createChild({});
290+
var e = d.createChild({});
291+
292+
getStreamState() => [root.hasOwnStreams, a.hasOwnStreams, a2.hasOwnStreams,
293+
b.hasOwnStreams, c.hasOwnStreams, d.hasOwnStreams,
294+
e.hasOwnStreams];
295+
296+
expect(getStreamState()).toEqual([false, false, false, false, false, false, false]);
297+
expect(root.apply).not.toThrow();
298+
299+
e.on(ScopeEvent.DESTROY).listen((_) => null);
300+
expect(getStreamState()).toEqual([false, false, false, false, false, false, true]);
301+
expect(root.apply).not.toThrow();
302+
303+
d.on(ScopeEvent.DESTROY).listen((_) => null);
304+
expect(getStreamState()).toEqual([false, false, false, false, false, true, true]);
305+
expect(root.apply).not.toThrow();
306+
307+
b.on(ScopeEvent.DESTROY).listen((_) => null);
308+
expect(getStreamState()).toEqual([false, false, false, true, false, true, true]);
309+
expect(root.apply).not.toThrow();
310+
311+
c.on(ScopeEvent.DESTROY).listen((_) => null);
312+
expect(getStreamState()).toEqual([false, false, false, true, true, true, true]);
313+
expect(root.apply).not.toThrow();
314+
315+
a.on(ScopeEvent.DESTROY).listen((_) => null);
316+
expect(getStreamState()).toEqual([false, true, false, true, true, true, true]);
317+
expect(root.apply).not.toThrow();
318+
319+
a2.on(ScopeEvent.DESTROY).listen((_) => null);
320+
expect(getStreamState()).toEqual([true, true, true, true, true, true, true]);
321+
expect(root.apply).not.toThrow();
322+
}));
270323
});
271324

272325

@@ -1047,7 +1100,7 @@ main() => describe('scope', () {
10471100
}));
10481101
});
10491102

1050-
1103+
10511104
describe('runAsync', () {
10521105
it(r'should run callback before watch', inject((RootScope rootScope) {
10531106
var log = '';

0 commit comments

Comments
 (0)