@@ -426,16 +426,29 @@ class DirtyCheckingRecord<H> implements Record<H>, WatchRecord<H> {
426
426
_getter = null ;
427
427
if (obj is Map ) {
428
428
if (_mode != _MODE_MAP_ ) {
429
- // Last one was collection as well, don't reset state.
430
429
_mode = _MODE_MAP_ ;
431
430
currentValue = new _MapChangeRecord ();
432
431
}
432
+ if (currentValue.isDirty) {
433
+ // We're dirty because the mapping we tracked by reference mutated.
434
+ // In addition, our reference has now changed. We should compare the
435
+ // previous reported value of that mapping with the one from the
436
+ // new reference.
437
+ currentValue._revertToPreviousState ();
438
+ }
439
+
433
440
} else if (obj is Iterable ) {
434
441
if (_mode != _MODE_ITERABLE_ ) {
435
- // Last one was collection as well, don't reset state.
436
- _mode = _MODE_ITERABLE_ ;
442
+ _mode = _MODE_ITERABLE_ ;
437
443
currentValue = new _CollectionChangeRecord ();
438
444
}
445
+ if (currentValue.isDirty) {
446
+ // We're dirty because the collection we tracked by reference mutated.
447
+ // In addition, our reference has now changed. We should compare the
448
+ // previous reported value of that collection with the one from the
449
+ // new reference.
450
+ currentValue._revertToPreviousState ();
451
+ }
439
452
} else {
440
453
_mode = _MODE_IDENTITY_ ;
441
454
}
@@ -507,12 +520,14 @@ class _MapChangeRecord<K, V> implements MapChangeRecord<K, V> {
507
520
final Map <dynamic , KeyValueRecord > _records = new Map <dynamic , KeyValueRecord >();
508
521
Map _map;
509
522
KeyValueRecord _mapHead;
523
+ KeyValueRecord _previousMapHead;
510
524
KeyValueRecord _changesHead, _changesTail;
511
525
KeyValueRecord _additionsHead, _additionsTail;
512
526
KeyValueRecord _removalsHead, _removalsTail;
513
527
514
528
Map get map => _map;
515
529
KeyValue <K , V > get mapHead => _mapHead;
530
+ PreviousKeyValue <K , V > get previousMapHead => _previousMapHead;
516
531
ChangedKeyValue <K , V > get changesHead => _changesHead;
517
532
AddedKeyValue <K , V > get additionsHead => _additionsHead;
518
533
RemovedKeyValue <K , V > get removalsHead => _removalsHead;
@@ -521,6 +536,24 @@ class _MapChangeRecord<K, V> implements MapChangeRecord<K, V> {
521
536
_changesHead != null ||
522
537
_removalsHead != null ;
523
538
539
+ _revertToPreviousState () {
540
+ if (! isDirty) {
541
+ return ;
542
+ }
543
+ KeyValueRecord record, prev;
544
+ int i = 0 ;
545
+ for (record = _mapHead = _previousMapHead;
546
+ record != null ;
547
+ prev = record, record = record._previousNextKeyValue, ++ i) {
548
+ record._currentValue = record._previousValue;
549
+ if (prev != null ) {
550
+ prev._nextKeyValue = prev._previousNextKeyValue = record;
551
+ }
552
+ }
553
+ prev._nextKeyValue = null ;
554
+ _undoDeltas ();
555
+ }
556
+
524
557
void forEachChange (void f (ChangedKeyValue <K , V > change)) {
525
558
KeyValueRecord record = _changesHead;
526
559
while (record != null ) {
@@ -602,6 +635,18 @@ class _MapChangeRecord<K, V> implements MapChangeRecord<K, V> {
602
635
}
603
636
604
637
void _reset () {
638
+ if (isDirty) {
639
+ // Record the state of the mapping for a possible _revertToPreviousState()
640
+ for (KeyValueRecord record = _previousMapHead = _mapHead;
641
+ record != null ;
642
+ record = record._nextKeyValue) {
643
+ record._previousNextKeyValue = record._nextKeyValue;
644
+ }
645
+ _undoDeltas ();
646
+ }
647
+ }
648
+
649
+ void _undoDeltas () {
605
650
var record = _changesHead;
606
651
while (record != null ) {
607
652
record._previousValue = record._currentValue;
@@ -748,14 +793,42 @@ class _MapChangeRecord<K, V> implements MapChangeRecord<K, V> {
748
793
_changesTail = record;
749
794
}
750
795
}
796
+
797
+ String toString () {
798
+ List itemsList = [], previousList = [], changesList = [], additionsList = [], removalsList = [];
799
+ KeyValueRecord record;
800
+ for (record = _mapHead; record != null ; record = record._nextKeyValue) {
801
+ itemsList.add ("$record " );
802
+ }
803
+ for (record = _previousMapHead; record != null ; record = record._previousNextKeyValue) {
804
+ previousList.add ("$record " );
805
+ }
806
+ for (record = _changesHead; record != null ; record = record._nextChangedKeyValue) {
807
+ changesList.add ("$record " );
808
+ }
809
+ for (record = _additionsHead; record != null ; record = record._nextAddedKeyValue) {
810
+ additionsList.add ("$record " );
811
+ }
812
+ for (record = _removalsHead; record != null ; record = record._nextRemovedKeyValue) {
813
+ removalsList.add ("$record " );
814
+ }
815
+ return """
816
+ map: ${itemsList .join (", " )}
817
+ previous: ${previousList .join (", " )}
818
+ changes: ${changesList .join (", " )}
819
+ additions: ${additionsList .join (", " )}
820
+ removals: ${removalsList .join (", " )}
821
+ """ ;
822
+ }
751
823
}
752
824
753
- class KeyValueRecord <K , V > implements KeyValue <K , V >, AddedKeyValue <K , V >,
754
- RemovedKeyValue <K , V >, ChangedKeyValue <K , V > {
825
+ class KeyValueRecord <K , V > implements KeyValue <K , V >, PreviousKeyValue <K , V >,
826
+ AddedKeyValue < K , V >, RemovedKeyValue <K , V >, ChangedKeyValue <K , V > {
755
827
final K key;
756
828
V _previousValue, _currentValue;
757
829
758
830
KeyValueRecord <K , V > _nextKeyValue;
831
+ KeyValueRecord <K , V > _previousNextKeyValue;
759
832
KeyValueRecord <K , V > _nextAddedKeyValue;
760
833
KeyValueRecord <K , V > _nextRemovedKeyValue, _prevRemovedKeyValue;
761
834
KeyValueRecord <K , V > _nextChangedKeyValue;
@@ -765,12 +838,13 @@ class KeyValueRecord<K, V> implements KeyValue<K, V>, AddedKeyValue<K, V>,
765
838
V get previousValue => _previousValue;
766
839
V get currentValue => _currentValue;
767
840
KeyValue <K , V > get nextKeyValue => _nextKeyValue;
841
+ PreviousKeyValue <K , V > get previousNextKeyValue => _previousNextKeyValue;
768
842
AddedKeyValue <K , V > get nextAddedKeyValue => _nextAddedKeyValue;
769
843
RemovedKeyValue <K , V > get nextRemovedKeyValue => _nextRemovedKeyValue;
770
844
ChangedKeyValue <K , V > get nextChangedKeyValue => _nextChangedKeyValue;
771
845
772
846
String toString () => _previousValue == _currentValue
773
- ? key
847
+ ? "$ key "
774
848
: '$key [$_previousValue -> $_currentValue ]' ;
775
849
}
776
850
@@ -785,16 +859,40 @@ class _CollectionChangeRecord<V> implements CollectionChangeRecord<V> {
785
859
/** Used to keep track of removed items. */
786
860
DuplicateMap _removedItems = new DuplicateMap ();
787
861
862
+ ItemRecord <V > _previousCollectionHead;
788
863
ItemRecord <V > _collectionHead, _collectionTail;
789
864
ItemRecord <V > _additionsHead, _additionsTail;
790
865
ItemRecord <V > _movesHead, _movesTail;
791
866
ItemRecord <V > _removalsHead, _removalsTail;
792
867
868
+ CollectionChangeItem <V > get previousCollectionHead => _previousCollectionHead;
793
869
CollectionChangeItem <V > get collectionHead => _collectionHead;
794
870
CollectionChangeItem <V > get additionsHead => _additionsHead;
795
871
CollectionChangeItem <V > get movesHead => _movesHead;
796
872
CollectionChangeItem <V > get removalsHead => _removalsHead;
797
873
874
+ _revertToPreviousState () {
875
+ if (! isDirty) {
876
+ return ;
877
+ }
878
+ _items.clear ();
879
+ ItemRecord <V > record, prev;
880
+ int i = 0 ;
881
+ for (record = _collectionHead = _previousCollectionHead;
882
+ record != null ;
883
+ prev = record, record = record._previousNextRec, ++ i) {
884
+ record.currentIndex = record.previousIndex = i;
885
+ record._prevRec = prev;
886
+ if (prev != null ) {
887
+ prev._nextRec = prev._previousNextRec = record;
888
+ }
889
+ _items.put (record);
890
+ }
891
+ prev._nextRec = null ;
892
+ _collectionTail = prev;
893
+ _undoDeltas ();
894
+ }
895
+
798
896
void forEachAddition (void f (AddedItem <V > addition)){
799
897
ItemRecord record = _additionsHead;
800
898
while (record != null ) {
@@ -824,14 +922,15 @@ class _CollectionChangeRecord<V> implements CollectionChangeRecord<V> {
824
922
825
923
bool _check (Iterable collection) {
826
924
_reset ();
827
- ItemRecord record = _collectionHead;
828
- bool maybeDirty = false ;
829
925
if ((collection is UnmodifiableListView ) &&
830
- identical (_iterable, collection)) {
926
+ identical (_iterable, collection)) {
831
927
// Short circuit and assume that the list has not been modified.
832
928
return false ;
833
929
}
834
930
931
+ ItemRecord record = _collectionHead;
932
+ bool maybeDirty = false ;
933
+
835
934
if (collection is List ) {
836
935
List list = collection;
837
936
_length = list.length;
@@ -873,6 +972,18 @@ class _CollectionChangeRecord<V> implements CollectionChangeRecord<V> {
873
972
* removals).
874
973
*/
875
974
void _reset () {
975
+ if (isDirty) {
976
+ // Record the state of the collection for a possible _revertToPreviousState()
977
+ for (ItemRecord record = _previousCollectionHead = _collectionHead;
978
+ record != null ;
979
+ record = record._nextRec) {
980
+ record._previousNextRec = record._nextRec;
981
+ }
982
+ _undoDeltas ();
983
+ }
984
+ }
985
+
986
+ void _undoDeltas () {
876
987
ItemRecord record;
877
988
878
989
record = _additionsHead;
@@ -1157,6 +1268,13 @@ class _CollectionChangeRecord<V> implements CollectionChangeRecord<V> {
1157
1268
record = record._nextRec;
1158
1269
}
1159
1270
1271
+ var previous = [];
1272
+ record = _previousCollectionHead;
1273
+ while (record != null ) {
1274
+ previous.add (record);
1275
+ record = record._previousNextRec;
1276
+ }
1277
+
1160
1278
var additions = [];
1161
1279
record = _additionsHead;
1162
1280
while (record != null ) {
@@ -1180,24 +1298,28 @@ class _CollectionChangeRecord<V> implements CollectionChangeRecord<V> {
1180
1298
1181
1299
return """
1182
1300
collection: ${list .join (", " )}
1301
+ previous: ${previous .join (", " )}
1183
1302
additions: ${additions .join (", " )}
1184
1303
moves: ${moves .join (", " )}
1185
1304
removals: ${removals .join (", " )}
1186
1305
""" ;
1187
1306
}
1188
1307
}
1189
1308
1190
- class ItemRecord <V > implements CollectionItem <V >, AddedItem <V >, MovedItem <V >,
1309
+ class ItemRecord <V > implements PreviousCollectionItem < V >, CollectionItem <V >, AddedItem <V >, MovedItem <V >,
1191
1310
RemovedItem <V > {
1192
1311
int previousIndex = null ;
1193
1312
int currentIndex = null ;
1194
1313
V item = _INITIAL_ ;
1195
1314
1315
+
1316
+ ItemRecord <V > _previousNextRec;
1196
1317
ItemRecord <V > _prevRec, _nextRec;
1197
1318
ItemRecord <V > _prevDupRec, _nextDupRec;
1198
1319
ItemRecord <V > _prevRemovedRec, _nextRemovedRec;
1199
1320
ItemRecord <V > _nextAddedRec, _nextMovedRec;
1200
1321
1322
+ PreviousCollectionItem <V > get previousNextItem => _previousNextRec;
1201
1323
CollectionItem <V > get nextCollectionItem => _nextRec;
1202
1324
RemovedItem <V > get nextRemovedItem => _nextRemovedRec;
1203
1325
AddedItem <V > get nextAddedItem => _nextAddedRec;
@@ -1314,7 +1436,11 @@ class DuplicateMap {
1314
1436
return record;
1315
1437
}
1316
1438
1439
+ bool get isEmpty => map.isEmpty;
1440
+
1317
1441
void clear () {
1318
1442
map.clear ();
1319
1443
}
1444
+
1445
+ String toString () => "$runtimeType ($map )" ;
1320
1446
}
0 commit comments