@@ -58,8 +58,32 @@ class DirtyCheckingChangeDetectorGroup<H> implements ChangeDetectorGroup<H> {
58
58
59
59
/**
60
60
* All records for group are kept together and are denoted by head/tail.
61
+ * The [_recordHead] -[_recordTail] only include our own records. If you want
62
+ * to see our childGroup records as well use
63
+ * [_head] -[_childInclRecordTail] .
61
64
*/
62
- DirtyCheckingRecord _head, _tail;
65
+ DirtyCheckingRecord _recordHead, _recordTail;
66
+
67
+ /**
68
+ * Same as [_tail] but includes child-group records as well.
69
+ */
70
+ DirtyCheckingRecord get _childInclRecordTail {
71
+ DirtyCheckingChangeDetectorGroup tail = this , nextTail;
72
+ while ((nextTail = tail._childTail) != null ) {
73
+ tail = nextTail;
74
+ }
75
+ return tail._recordTail;
76
+ }
77
+
78
+
79
+ DirtyCheckingChangeDetector get _root {
80
+ var root = this ;
81
+ var next;
82
+ while ((next = root._parent) != null ) {
83
+ root = next;
84
+ }
85
+ return root is DirtyCheckingChangeDetector ? root : null ;
86
+ }
63
87
64
88
/**
65
89
* ChangeDetectorGroup is organized hierarchically, a root group can have
@@ -71,16 +95,12 @@ class DirtyCheckingChangeDetectorGroup<H> implements ChangeDetectorGroup<H> {
71
95
DirtyCheckingChangeDetectorGroup (this ._parent, this ._getterCache) {
72
96
// we need to insert the marker record at the beginning.
73
97
if (_parent == null ) {
74
- _head = _marker;
75
- _tail = _marker;
98
+ _recordHead = _marker;
99
+ _recordTail = _marker;
76
100
} else {
77
- // we need to find the tail of previous record
78
- // If we are first then it is the tail of the parent group
79
- // otherwise it is the tail of the previous group
80
- DirtyCheckingChangeDetectorGroup tail = _parent._childTail;
81
- _tail = (tail == null ? _parent : tail)._tail;
82
- // _recordAdd uses _tail from above.
83
- _head = _tail = _recordAdd (_marker);
101
+ _recordTail = _parent._childInclRecordTail;
102
+ // _recordAdd uses _recordTail from above.
103
+ _recordHead = _recordTail = _recordAdd (_marker);
84
104
}
85
105
}
86
106
@@ -89,17 +109,20 @@ class DirtyCheckingChangeDetectorGroup<H> implements ChangeDetectorGroup<H> {
89
109
*/
90
110
get count {
91
111
int count = 0 ;
92
- DirtyCheckingRecord cursor = _head == _marker ?
93
- _head._nextWatch :
94
- _head;
95
- while (cursor != null ) {
96
- count++ ;
97
- cursor = cursor._nextWatch;
112
+ DirtyCheckingRecord cursor = _recordHead;
113
+ DirtyCheckingRecord end = _childInclRecordTail;
114
+ while (cursor != null ) {
115
+ if (cursor._mode != DirtyCheckingRecord ._MODE_MARKER_ ) {
116
+ count++ ;
117
+ }
118
+ if (cursor == end) break ;
119
+ cursor = cursor._nextRecord;
98
120
}
99
121
return count;
100
122
}
101
123
102
124
WatchRecord <H > watch (Object object, String field, H handler) {
125
+ assert (_root != null ); // prove that we are not deleted connected;
103
126
var getter = field == null ? null : _getterCache (field);
104
127
return _recordAdd (new DirtyCheckingRecord (this , object, field, getter,
105
128
handler));
@@ -109,6 +132,7 @@ class DirtyCheckingChangeDetectorGroup<H> implements ChangeDetectorGroup<H> {
109
132
* Create a child [ChangeDetector] group.
110
133
*/
111
134
DirtyCheckingChangeDetectorGroup <H > newGroup () {
135
+ assert (_root._assertRecordsOk ());
112
136
var child = new DirtyCheckingChangeDetectorGroup (this , _getterCache);
113
137
if (_childHead == null ) {
114
138
_childHead = _childTail = child;
@@ -117,19 +141,27 @@ class DirtyCheckingChangeDetectorGroup<H> implements ChangeDetectorGroup<H> {
117
141
_childTail._next = child;
118
142
_childTail = child;
119
143
}
144
+ assert (_root._assertRecordsOk ());
120
145
return child;
121
146
}
122
147
123
148
/**
124
149
* Bulk remove all records.
125
150
*/
126
151
void remove () {
127
- DirtyCheckingRecord previousRecord = _head._prevWatch;
128
- var childTail = _childTail == null ? this : _childTail;
129
- DirtyCheckingRecord nextRecord = childTail._tail._nextWatch;
130
-
131
- if (previousRecord != null ) previousRecord._nextWatch = nextRecord;
132
- if (nextRecord != null ) nextRecord._prevWatch = previousRecord;
152
+ var root;
153
+ assert ((root = _root) != null );
154
+ assert (root._assertRecordsOk ());
155
+ DirtyCheckingRecord prevRecord = _recordHead._prevRecord;
156
+ DirtyCheckingRecord nextRecord = _childInclRecordTail._nextRecord;
157
+
158
+ if (prevRecord != null ) prevRecord._nextRecord = nextRecord;
159
+ if (nextRecord != null ) nextRecord._prevRecord = prevRecord;
160
+
161
+ var cursor = _recordHead;
162
+ while (cursor != nextRecord) {
163
+ cursor = cursor._nextRecord;
164
+ }
133
165
134
166
var prevGroup = _prev;
135
167
var nextGroup = _next;
@@ -144,61 +176,65 @@ class DirtyCheckingChangeDetectorGroup<H> implements ChangeDetectorGroup<H> {
144
176
} else {
145
177
nextGroup._prev = prevGroup;
146
178
}
179
+ _parent = null ;
180
+ assert (root._assertRecordsOk ());
147
181
}
148
182
149
183
DirtyCheckingRecord _recordAdd (DirtyCheckingRecord record) {
150
- DirtyCheckingRecord previous = _tail ;
151
- DirtyCheckingRecord next = previous == null ? null : previous._nextWatch ;
184
+ DirtyCheckingRecord previous = _recordTail ;
185
+ DirtyCheckingRecord next = previous == null ? null : previous._nextRecord ;
152
186
153
- record._nextWatch = next;
154
- record._prevWatch = previous;
187
+ record._nextRecord = next;
188
+ record._prevRecord = previous;
155
189
156
- if (previous != null ) previous._nextWatch = record;
157
- if (next != null ) next._prevWatch = record;
190
+ if (previous != null ) previous._nextRecord = record;
191
+ if (next != null ) next._prevRecord = record;
158
192
159
- _tail = record;
193
+ _recordTail = record;
160
194
161
195
if (previous == _marker) _recordRemove (_marker);
162
196
163
197
return record;
164
198
}
165
199
166
200
void _recordRemove (DirtyCheckingRecord record) {
167
- DirtyCheckingRecord previous = record._prevWatch ;
168
- DirtyCheckingRecord next = record._nextWatch ;
201
+ DirtyCheckingRecord previous = record._prevRecord ;
202
+ DirtyCheckingRecord next = record._nextRecord ;
169
203
170
- if (record == _head && record == _tail ) {
204
+ if (record == _recordHead && record == _recordTail ) {
171
205
// we are the last one, must leave marker behind.
172
- _head = _tail = _marker;
173
- _marker._nextWatch = next;
174
- _marker._prevWatch = previous;
175
- if (previous != null ) previous._nextWatch = _marker;
176
- if (next != null ) next._prevWatch = _marker;
206
+ _recordHead = _recordTail = _marker;
207
+ _marker._nextRecord = next;
208
+ _marker._prevRecord = previous;
209
+ if (previous != null ) previous._nextRecord = _marker;
210
+ if (next != null ) next._prevRecord = _marker;
177
211
} else {
178
- if (record == _tail) _tail = previous;
179
- if (record == _head) _head = next;
180
- if (previous != null ) previous._nextWatch = next;
181
- if (next != null ) next._prevWatch = previous;
212
+ if (record == _recordTail) _recordTail = previous;
213
+ if (record == _recordHead) _recordHead = next;
214
+ if (previous != null ) previous._nextRecord = next;
215
+ if (next != null ) next._prevRecord = previous;
182
216
}
183
217
}
184
218
185
219
String toString () {
186
220
var lines = [];
187
221
if (_parent == null ) {
188
222
var allRecords = [];
189
- DirtyCheckingRecord record = _head;
190
- while (record != null ) {
223
+ DirtyCheckingRecord record = _recordHead;
224
+ var includeChildrenTail = _childInclRecordTail;
225
+ do {
191
226
allRecords.add (record.toString ());
192
- record = record._nextWatch ;
227
+ record = record._nextRecord ;
193
228
}
229
+ while (record != includeChildrenTail);
194
230
lines.add ('FIELDS: ${allRecords .join (', ' )}' );
195
231
}
196
232
197
233
var records = [];
198
- DirtyCheckingRecord record = _head ;
199
- while (record != _tail ) {
234
+ DirtyCheckingRecord record = _recordHead ;
235
+ while (record != _recordTail ) {
200
236
records.add (record.toString ());
201
- record = record._nextWatch ;
237
+ record = record._nextRecord ;
202
238
}
203
239
records.add (record.toString ());
204
240
@@ -216,10 +252,39 @@ class DirtyCheckingChangeDetector<H> extends DirtyCheckingChangeDetectorGroup<H>
216
252
implements ChangeDetector <H > {
217
253
DirtyCheckingChangeDetector (GetterCache getterCache): super (null , getterCache);
218
254
255
+ DirtyCheckingChangeDetector get _root => this ;
256
+
257
+ _assertRecordsOk () {
258
+ var record = this ._recordHead;
259
+ var groups = [this ];
260
+ DirtyCheckingChangeDetectorGroup group;
261
+ while (groups.isNotEmpty) {
262
+ group = groups.removeAt (0 );
263
+ DirtyCheckingChangeDetectorGroup childGroup = group._childTail;
264
+ while (childGroup != null ) {
265
+ groups.insert (0 , childGroup);
266
+ childGroup = childGroup._prev;
267
+ }
268
+ var groupRecord = group._recordHead;
269
+ var groupLast = group._recordTail;
270
+ while (true ) {
271
+ if (groupRecord == record) {
272
+ record = record._nextRecord;
273
+ } else {
274
+ throw 'lost: $record found $groupRecord \n $this ' ;
275
+ }
276
+
277
+ if (groupRecord == groupLast) break ;
278
+ groupRecord = groupRecord._nextRecord;
279
+ }
280
+ }
281
+ return true ;
282
+ }
283
+
219
284
DirtyCheckingRecord <H > collectChanges ([EvalExceptionHandler exceptionHandler]) {
220
285
DirtyCheckingRecord changeHead = null ;
221
286
DirtyCheckingRecord changeTail = null ;
222
- DirtyCheckingRecord current = _head ; // current index
287
+ DirtyCheckingRecord current = _recordHead ; // current index
223
288
224
289
while (current != null ) {
225
290
try {
@@ -237,7 +302,7 @@ class DirtyCheckingChangeDetector<H> extends DirtyCheckingChangeDetectorGroup<H>
237
302
exceptionHandler (e, s);
238
303
}
239
304
}
240
- current = current._nextWatch ;
305
+ current = current._nextRecord ;
241
306
}
242
307
if (changeTail != null ) changeTail.nextChange = null ;
243
308
return changeHead;
@@ -278,8 +343,8 @@ class DirtyCheckingRecord<H> implements ChangeRecord<H>, WatchRecord<H> {
278
343
279
344
var previousValue;
280
345
var currentValue;
281
- DirtyCheckingRecord <H > _nextWatch ;
282
- DirtyCheckingRecord <H > _prevWatch ;
346
+ DirtyCheckingRecord <H > _nextRecord ;
347
+ DirtyCheckingRecord <H > _prevRecord ;
283
348
ChangeRecord <H > nextChange;
284
349
var _object;
285
350
InstanceMirror _instanceMirror;
@@ -395,7 +460,7 @@ class DirtyCheckingRecord<H> implements ChangeRecord<H>, WatchRecord<H> {
395
460
_group._recordRemove (this );
396
461
}
397
462
398
- String toString () => '${_MODE_NAMES [_mode ]}[$field ]' ;
463
+ String toString () => '${_MODE_NAMES [_mode ]}[$field ]{$ hashCode } ' ;
399
464
}
400
465
401
466
final Object _INITIAL_ = new Object ();
0 commit comments