@@ -514,19 +514,10 @@ class DirtyCheckingRecord<H> implements Record<H>, WatchRecord<H> {
514
514
}
515
515
516
516
var last = currentValue;
517
- if (! identical (last, current)) {
518
- if (last is String && current is String &&
519
- last == current) {
520
- // This is false change in strings we need to recover, and pretend it
521
- // is the same. We save the value so that next time identity will pass
522
- currentValue = current;
523
- } else if (last is num && last.isNaN && current is num && current.isNaN) {
524
- // we need this for the compiled JavaScript since in JS NaN !== NaN.
525
- } else {
526
- previousValue = last;
527
- currentValue = current;
528
- return true ;
529
- }
517
+ if (! _looseIdentical (last, current)) {
518
+ previousValue = currentValue;
519
+ currentValue = current;
520
+ return true ;
530
521
}
531
522
return false ;
532
523
}
@@ -620,14 +611,10 @@ class _MapChangeRecord<K, V> implements MapChangeRecord<K, V> {
620
611
var newSeqRecord;
621
612
if (oldSeqRecord != null && key == oldSeqRecord.key) {
622
613
newSeqRecord = oldSeqRecord;
623
- if (! identical (value, oldSeqRecord._currentValue)) {
614
+ if (! _looseIdentical (value, oldSeqRecord._currentValue)) {
624
615
var prev = oldSeqRecord._previousValue = oldSeqRecord._currentValue;
625
616
oldSeqRecord._currentValue = value;
626
- if (! ((value is String && prev is String && value == prev) ||
627
- (value is num && value.isNaN && prev is num && prev.isNaN))) {
628
- // Check string by value rather than reference
629
- _addToChanges (oldSeqRecord);
630
- }
617
+ _addToChanges (oldSeqRecord);
631
618
}
632
619
} else {
633
620
seqChanged = true ;
@@ -955,7 +942,7 @@ class _CollectionChangeRecord<V> implements CollectionChangeRecord<V> {
955
942
_length = list.length;
956
943
for (int index = 0 ; index < _length; index++ ) {
957
944
var item = list[index];
958
- if (record == null || ! identical ( item, record. item)) {
945
+ if (record == null || ! _looseIdentical (record. item, item)) {
959
946
record = mismatch (record, item, index);
960
947
maybeDirty = true ;
961
948
} else if (maybeDirty) {
@@ -967,7 +954,7 @@ class _CollectionChangeRecord<V> implements CollectionChangeRecord<V> {
967
954
} else {
968
955
int index = 0 ;
969
956
for (var item in collection) {
970
- if (record == null || ! identical ( item, record. item)) {
957
+ if (record == null || ! _looseIdentical (record. item, item)) {
971
958
record = mismatch (record, item, index);
972
959
maybeDirty = true ;
973
960
} else if (maybeDirty) {
@@ -1037,19 +1024,6 @@ class _CollectionChangeRecord<V> implements CollectionChangeRecord<V> {
1037
1024
* - [index] is the position of the item in the collection
1038
1025
*/
1039
1026
ItemRecord <V > mismatch (ItemRecord <V > record, item, int index) {
1040
- if (record != null ) {
1041
- if (item is String && record.item is String && record.item == item) {
1042
- // this is false change in strings we need to recover, and pretend it is
1043
- // the same. We save the value so that next time identity can pass
1044
- return record..item = item;
1045
- }
1046
-
1047
- if (item is num && (item as num ).isNaN && record.item is num && (record.item as num ).isNaN) {
1048
- // we need this for JavaScript since in JS NaN !== NaN.
1049
- return record;
1050
- }
1051
- }
1052
-
1053
1027
// The previous record after which we will append the current one.
1054
1028
ItemRecord <V > previousRecord;
1055
1029
@@ -1343,7 +1317,8 @@ class _DuplicateItemRecordList {
1343
1317
record._prevDup = null ;
1344
1318
} else {
1345
1319
// adding a duplicate [ItemRecord] to the list
1346
- assert (record.item == _head.item);
1320
+ assert (record.item == _head.item ||
1321
+ record.item is num && record.item.isNaN && _head.item is num && _head.item.isNaN);
1347
1322
if (insertBefore == null ) {
1348
1323
_tail._nextDup = record;
1349
1324
record._prevDup = _tail;
@@ -1370,10 +1345,10 @@ class _DuplicateItemRecordList {
1370
1345
ItemRecord record;
1371
1346
for (record = _head; record != null ; record = record._nextDup) {
1372
1347
if ((afterIndex == null || afterIndex < record.currentIndex) &&
1373
- identical (record.item, item)) {
1374
- return record;
1348
+ _looseIdentical (record.item, item)) {
1349
+ return record;
1350
+ }
1375
1351
}
1376
- }
1377
1352
return null ;
1378
1353
}
1379
1354
@@ -1414,10 +1389,12 @@ class _DuplicateItemRecordList {
1414
1389
* The list of duplicates is implemented by [_DuplicateItemRecordList] .
1415
1390
*/
1416
1391
class DuplicateMap {
1392
+ static final nanKey = const Object ();
1417
1393
final map = new HashMap <dynamic , _DuplicateItemRecordList >();
1418
1394
1419
1395
void put (ItemRecord record, [ItemRecord insertBefore = null ]) {
1420
- map.putIfAbsent (record.item, () => new _DuplicateItemRecordList ()).add (record, insertBefore);
1396
+ var key = _getKey (record.item);
1397
+ map.putIfAbsent (key, () => new _DuplicateItemRecordList ()).add (record, insertBefore);
1421
1398
}
1422
1399
1423
1400
/**
@@ -1427,9 +1404,10 @@ class DuplicateMap {
1427
1404
* Use case: `[a, b, c, a, a]` if we are at index `3` which is the second `a` then asking if we
1428
1405
* have any more `a` s needs to return the last `a` not the first or second.
1429
1406
*/
1430
- ItemRecord get (key, [int afterIndex]) {
1407
+ ItemRecord get (value, [int afterIndex]) {
1408
+ var key = _getKey (value);
1431
1409
_DuplicateItemRecordList recordList = map[key];
1432
- return recordList == null ? null : recordList.get (key , afterIndex);
1410
+ return recordList == null ? null : recordList.get (value , afterIndex);
1433
1411
}
1434
1412
1435
1413
/**
@@ -1438,10 +1416,11 @@ class DuplicateMap {
1438
1416
* The list of duplicates also is removed from the map if it gets empty.
1439
1417
*/
1440
1418
ItemRecord remove (ItemRecord record) {
1441
- assert (map.containsKey (record.item));
1442
- _DuplicateItemRecordList recordList = map[record.item];
1419
+ var key = _getKey (record.item);
1420
+ assert (map.containsKey (key));
1421
+ _DuplicateItemRecordList recordList = map[key];
1443
1422
// Remove the list of duplicates when it gets empty
1444
- if (recordList.remove (record)) map.remove (record.item );
1423
+ if (recordList.remove (record)) map.remove (key );
1445
1424
return record;
1446
1425
}
1447
1426
@@ -1451,5 +1430,32 @@ class DuplicateMap {
1451
1430
map.clear ();
1452
1431
}
1453
1432
1433
+ /// Required to handle num.NAN as a Map value
1434
+ dynamic _getKey (value) => value is num && value.isNaN ? nanKey : value;
1435
+
1454
1436
String toString () => "DuplicateMap($map )" ;
1455
1437
}
1438
+
1439
+ /**
1440
+ * Returns whether the [dst] and [src] are lossely identical:
1441
+ * * true when the value are identical,
1442
+ * * true when both values are equal strings,
1443
+ * * true when both values are NaN
1444
+ *
1445
+ * If both values are equal string, src is assigned to dst.
1446
+ */
1447
+ bool _looseIdentical (dst, src) {
1448
+ if (identical (dst, src)) return true ;
1449
+
1450
+ if (dst is String && src is String && dst == src) {
1451
+ // this is false change in strings we need to recover, and pretend it is the same. We save the
1452
+ // value so that next time identity can pass
1453
+ dst = src;
1454
+ return true ;
1455
+ }
1456
+
1457
+ // we need this for JavaScript since in JS NaN !== NaN.
1458
+ if (dst is num && (dst as num ).isNaN && src is num && (src as num ).isNaN) return true ;
1459
+
1460
+ return false ;
1461
+ }
0 commit comments