@@ -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,11 @@ 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
- var key = record.item;
1396
+ var key = _getKey ( record.item) ;
1421
1397
_DuplicateItemRecordList duplicates = map[key];
1422
1398
if (duplicates == null ) {
1423
1399
duplicates = map[key] = new _DuplicateItemRecordList ();
@@ -1432,9 +1408,10 @@ class DuplicateMap {
1432
1408
* Use case: `[a, b, c, a, a]` if we are at index `3` which is the second `a` then asking if we
1433
1409
* have any more `a` s needs to return the last `a` not the first or second.
1434
1410
*/
1435
- ItemRecord get (key, [int afterIndex]) {
1411
+ ItemRecord get (value, [int afterIndex]) {
1412
+ var key = _getKey (value);
1436
1413
_DuplicateItemRecordList recordList = map[key];
1437
- return recordList == null ? null : recordList.get (key , afterIndex);
1414
+ return recordList == null ? null : recordList.get (value , afterIndex);
1438
1415
}
1439
1416
1440
1417
/**
@@ -1443,10 +1420,11 @@ class DuplicateMap {
1443
1420
* The list of duplicates also is removed from the map if it gets empty.
1444
1421
*/
1445
1422
ItemRecord remove (ItemRecord record) {
1446
- assert (map.containsKey (record.item));
1447
- _DuplicateItemRecordList recordList = map[record.item];
1423
+ var key = _getKey (record.item);
1424
+ assert (map.containsKey (key));
1425
+ _DuplicateItemRecordList recordList = map[key];
1448
1426
// Remove the list of duplicates when it gets empty
1449
- if (recordList.remove (record)) map.remove (record.item );
1427
+ if (recordList.remove (record)) map.remove (key );
1450
1428
return record;
1451
1429
}
1452
1430
@@ -1456,5 +1434,32 @@ class DuplicateMap {
1456
1434
map.clear ();
1457
1435
}
1458
1436
1437
+ /// Required to handle num.NAN as a Map value
1438
+ dynamic _getKey (value) => value is num && value.isNaN ? nanKey : value;
1439
+
1459
1440
String toString () => "DuplicateMap($map )" ;
1460
1441
}
1442
+
1443
+ /**
1444
+ * Returns whether the [dst] and [src] are loosely identical:
1445
+ * * true when the value are identical,
1446
+ * * true when both values are equal strings,
1447
+ * * true when both values are NaN
1448
+ *
1449
+ * If both values are equal string, src is assigned to dst.
1450
+ */
1451
+ bool _looseIdentical (dst, src) {
1452
+ if (identical (dst, src)) return true ;
1453
+
1454
+ if (dst is String && src is String && dst == src) {
1455
+ // this is false change in strings we need to recover, and pretend it is the same. We save the
1456
+ // value so that next time identity can pass
1457
+ dst = src;
1458
+ return true ;
1459
+ }
1460
+
1461
+ // we need this for JavaScript since in JS NaN !== NaN.
1462
+ if (dst is num && (dst as num ).isNaN && src is num && (src as num ).isNaN) return true ;
1463
+
1464
+ return false ;
1465
+ }
0 commit comments