@@ -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
@@ -1340,7 +1314,8 @@ class _DuplicateItemRecordList {
1340
1314
record._prevDup = null ;
1341
1315
} else {
1342
1316
// adding a duplicate [ItemRecord] to the list
1343
- assert (record.item == _head.item);
1317
+ assert (record.item == _head.item ||
1318
+ record.item is num && record.item.isNaN && _head.item is num && _head.item.isNaN);
1344
1319
if (insertBefore == null ) {
1345
1320
_tail._nextDup = record;
1346
1321
record._prevDup = _tail;
@@ -1367,10 +1342,10 @@ class _DuplicateItemRecordList {
1367
1342
ItemRecord record;
1368
1343
for (record = _head; record != null ; record = record._nextDup) {
1369
1344
if ((afterIndex == null || afterIndex < record.currentIndex) &&
1370
- identical (record.item, item)) {
1371
- return record;
1345
+ _looseIdentical (record.item, item)) {
1346
+ return record;
1347
+ }
1372
1348
}
1373
- }
1374
1349
return null ;
1375
1350
}
1376
1351
@@ -1411,10 +1386,11 @@ class _DuplicateItemRecordList {
1411
1386
* The list of duplicates is implemented by [_DuplicateItemRecordList] .
1412
1387
*/
1413
1388
class DuplicateMap {
1389
+ static final nanKey = const Object ();
1414
1390
final map = new HashMap <dynamic , _DuplicateItemRecordList >();
1415
1391
1416
1392
void put (ItemRecord record, [ItemRecord insertBefore = null ]) {
1417
- var key = record.item;
1393
+ var key = _getKey ( record.item) ;
1418
1394
_DuplicateItemRecordList duplicates = map[key];
1419
1395
if (duplicates == null ) {
1420
1396
duplicates = map[key] = new _DuplicateItemRecordList ();
@@ -1429,9 +1405,10 @@ class DuplicateMap {
1429
1405
* Use case: `[a, b, c, a, a]` if we are at index `3` which is the second `a` then asking if we
1430
1406
* have any more `a` s needs to return the last `a` not the first or second.
1431
1407
*/
1432
- ItemRecord get (key, [int afterIndex]) {
1408
+ ItemRecord get (value, [int afterIndex]) {
1409
+ var key = _getKey (value);
1433
1410
_DuplicateItemRecordList recordList = map[key];
1434
- return recordList == null ? null : recordList.get (key , afterIndex);
1411
+ return recordList == null ? null : recordList.get (value , afterIndex);
1435
1412
}
1436
1413
1437
1414
/**
@@ -1440,10 +1417,11 @@ class DuplicateMap {
1440
1417
* The list of duplicates also is removed from the map if it gets empty.
1441
1418
*/
1442
1419
ItemRecord remove (ItemRecord record) {
1443
- assert (map.containsKey (record.item));
1444
- _DuplicateItemRecordList recordList = map[record.item];
1420
+ var key = _getKey (record.item);
1421
+ assert (map.containsKey (key));
1422
+ _DuplicateItemRecordList recordList = map[key];
1445
1423
// Remove the list of duplicates when it gets empty
1446
- if (recordList.remove (record)) map.remove (record.item );
1424
+ if (recordList.remove (record)) map.remove (key );
1447
1425
return record;
1448
1426
}
1449
1427
@@ -1453,5 +1431,32 @@ class DuplicateMap {
1453
1431
map.clear ();
1454
1432
}
1455
1433
1434
+ /// Required to handle num.NAN as a Map value
1435
+ dynamic _getKey (value) => value is num && value.isNaN ? nanKey : value;
1436
+
1456
1437
String toString () => "DuplicateMap($map )" ;
1457
1438
}
1439
+
1440
+ /**
1441
+ * Returns whether the [dst] and [src] are loosely identical:
1442
+ * * true when the value are identical,
1443
+ * * true when both values are equal strings,
1444
+ * * true when both values are NaN
1445
+ *
1446
+ * If both values are equal string, src is assigned to dst.
1447
+ */
1448
+ bool _looseIdentical (dst, src) {
1449
+ if (identical (dst, src)) return true ;
1450
+
1451
+ if (dst is String && src is String && dst == src) {
1452
+ // this is false change in strings we need to recover, and pretend it is the same. We save the
1453
+ // value so that next time identity can pass
1454
+ dst = src;
1455
+ return true ;
1456
+ }
1457
+
1458
+ // we need this for JavaScript since in JS NaN !== NaN.
1459
+ if (dst is num && (dst as num ).isNaN && src is num && (src as num ).isNaN) return true ;
1460
+
1461
+ return false ;
1462
+ }
0 commit comments