OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 // Patch file for dart:collection classes. | 5 // Patch file for dart:collection classes. |
6 import 'dart:_foreign_helper' show JS; | 6 import 'dart:_foreign_helper' show JS; |
7 | 7 |
8 patch class HashMap<K, V> { | 8 patch class HashMap<K, V> { |
9 patch factory HashMap({ bool equals(K key1, K key2), int hashCode(K key) }) { | 9 patch factory HashMap({ bool equals(K key1, K key2), int hashCode(K key) }) { |
10 if (hashCode == null) { | 10 if (hashCode == null) { |
11 if (equals == null) { | 11 if (equals == null) { |
12 return new _HashMapImpl<K, V>(); | 12 return new _Hash<K, V>(); |
13 } | 13 } |
14 if (identical(identical, equals)) { | 14 if (identical(identical, equals)) { |
15 return new _IdentityHashMap<K, V>(); | 15 return new _IdentityHashMap<K, V>(); |
16 } | 16 } |
17 hashCode = _defaultHashCode; | 17 hashCode = _defaultHashCode; |
18 } else if (equals == null) { | 18 } else if (equals == null) { |
19 equals = _defaultEquals; | 19 equals = _defaultEquals; |
20 } | 20 } |
21 return new _CustomHashMap<K, V>(equals, hashCode); | 21 return new _CustomHashMap<K, V>(equals, hashCode); |
22 } | 22 } |
23 } | 23 } |
24 | 24 |
25 class _HashMapImpl<K, V> implements HashMap<K, V> { | 25 class _Hash<K, V> implements HashMap<K, V> { |
26 int _length = 0; | 26 int _length = 0; |
27 | 27 |
28 // The hash map contents are divided into three parts: one part for | 28 // The hash map contents are divided into three parts: one part for |
29 // string keys, one for numeric keys, and one for the rest. String | 29 // string keys, one for numeric keys, and one for the rest. String |
30 // and numeric keys map directly to their values, but the rest of | 30 // and numeric keys map directly to their values, but the rest of |
31 // the entries are stored in bucket lists of the form: | 31 // the entries are stored in bucket lists of the form: |
32 // | 32 // |
33 // [key-0, value-0, key-1, value-1, ...] | 33 // [key-0, value-0, key-1, value-1, ...] |
34 // | 34 // |
35 // where all keys in the same bucket share the same hash code. | 35 // where all keys in the same bucket share the same hash code. |
36 var _strings; | 36 var _strings; |
37 var _nums; | 37 var _nums; |
38 var _rest; | 38 var _rest; |
39 | 39 |
40 // When iterating over the hash map, it is very convenient to have a | 40 // When iterating over the hash map, it is very convenient to have a |
41 // list of all the keys. We cache that on the instance and clear the | 41 // list of all the keys. We cache that on the instance and clear the |
42 // the cache whenever the key set changes. This is also used to | 42 // the cache whenever the key set changes. This is also used to |
43 // guard against concurrent modifications. | 43 // guard against concurrent modifications. |
44 List _keys; | 44 List _keys; |
45 | 45 |
46 _HashMapImpl(); | 46 _Hash(); |
47 | 47 |
48 int get length => _length; | 48 int get length => _length; |
49 bool get isEmpty => _length == 0; | 49 bool get isEmpty => _length == 0; |
50 bool get isNotEmpty => !isEmpty; | 50 bool get isNotEmpty => !isEmpty; |
51 | 51 |
52 Iterable<K> get keys { | 52 Iterable<K> get keys { |
53 return new HashMapKeyIterable<K>(this); | 53 return new HashMapKeyIterable<K>(this); |
54 } | 54 } |
55 | 55 |
56 Iterable<V> get values { | 56 Iterable<V> get values { |
(...skipping 261 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
318 var table = JS('var', 'Object.create(null)'); | 318 var table = JS('var', 'Object.create(null)'); |
319 // Attempt to force the hash table into 'dictionary' mode by | 319 // Attempt to force the hash table into 'dictionary' mode by |
320 // adding a property to it and deleting it again. | 320 // adding a property to it and deleting it again. |
321 var temporaryKey = '<non-identifier-key>'; | 321 var temporaryKey = '<non-identifier-key>'; |
322 _setTableEntry(table, temporaryKey, table); | 322 _setTableEntry(table, temporaryKey, table); |
323 _deleteTableEntry(table, temporaryKey); | 323 _deleteTableEntry(table, temporaryKey); |
324 return table; | 324 return table; |
325 } | 325 } |
326 } | 326 } |
327 | 327 |
328 class _IdentityHashMap<K, V> extends _HashMapImpl<K, V> { | 328 class _IdentityHashMap<K, V> extends _Hash<K, V> { |
329 int _findBucketIndex(var bucket, var key) { | 329 int _findBucketIndex(var bucket, var key) { |
330 if (bucket == null) return -1; | 330 if (bucket == null) return -1; |
331 int length = JS('int', '#.length', bucket); | 331 int length = JS('int', '#.length', bucket); |
332 for (int i = 0; i < length; i += 2) { | 332 for (int i = 0; i < length; i += 2) { |
333 if (identical(JS('var', '#[#]', bucket, i), key)) return i; | 333 if (identical(JS('var', '#[#]', bucket, i), key)) return i; |
334 } | 334 } |
335 return -1; | 335 return -1; |
336 } | 336 } |
337 } | 337 } |
338 | 338 |
339 class _CustomHashMap<K, V> extends _HashMapImpl<K, V> { | 339 class _CustomHashMap<K, V> extends _Hash<K, V> { |
340 final _Equality<K> _equals; | 340 final _Equality<K> _equals; |
341 final _Hasher<K> _hashCode; | 341 final _Hasher<K> _hashCode; |
342 _CustomHashMap(this._equals, this._hashCode); | 342 _CustomHashMap(this._equals, this._hashCode); |
343 | 343 |
344 int _computeHashCode(var key) { | 344 int _computeHashCode(var key) { |
345 // We force the hash codes to be unsigned 30-bit integers to avoid | 345 // We force the hash codes to be unsigned 30-bit integers to avoid |
346 // issues with problematic keys like '__proto__'. Another option | 346 // issues with problematic keys like '__proto__'. Another option |
347 // would be to throw an exception if the hash code isn't a number. | 347 // would be to throw an exception if the hash code isn't a number. |
348 return JS('int', '# & 0x3ffffff', _hashCode(key)); | 348 return JS('int', '# & 0x3ffffff', _hashCode(key)); |
349 } | 349 } |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
409 // TODO(kasperl): For now, we have to tell the type inferrer to | 409 // TODO(kasperl): For now, we have to tell the type inferrer to |
410 // treat the result of doing offset + 1 as an int. Otherwise, we | 410 // treat the result of doing offset + 1 as an int. Otherwise, we |
411 // get unnecessary bailout code. | 411 // get unnecessary bailout code. |
412 _offset = JS('int', '#', offset + 1); | 412 _offset = JS('int', '#', offset + 1); |
413 return true; | 413 return true; |
414 } | 414 } |
415 } | 415 } |
416 } | 416 } |
417 | 417 |
418 patch class LinkedHashMap<K, V> { | 418 patch class LinkedHashMap<K, V> { |
| 419 patch factory LinkedHashMap({ bool equals(K key1, K key2), |
| 420 int hashCode(K key) }) { |
| 421 if (hashCode == null) { |
| 422 if (equals == null) { |
| 423 return new _LinkedHash<K, V>(); |
| 424 } |
| 425 if (identical(identical, equals)) { |
| 426 return new _LinkedIdentityHashMap<K, V>(); |
| 427 } |
| 428 hashCode = _defaultHashCode; |
| 429 } else if (equals == null) { |
| 430 equals = _defaultEquals; |
| 431 } |
| 432 return new _LinkedCustomHashMap<K, V>(equals, hashCode); |
| 433 } |
| 434 } |
| 435 |
| 436 class _LinkedHash<K, V> implements LinkedHashMap<K, V> { |
419 int _length = 0; | 437 int _length = 0; |
420 | 438 |
421 // The hash map contents are divided into three parts: one part for | 439 // The hash map contents are divided into three parts: one part for |
422 // string keys, one for numeric keys, and one for the rest. String | 440 // string keys, one for numeric keys, and one for the rest. String |
423 // and numeric keys map directly to their linked cells, but the rest | 441 // and numeric keys map directly to their linked cells, but the rest |
424 // of the entries are stored in bucket lists of the form: | 442 // of the entries are stored in bucket lists of the form: |
425 // | 443 // |
426 // [cell-0, cell-1, ...] | 444 // [cell-0, cell-1, ...] |
427 // | 445 // |
428 // where all keys in the same bucket share the same hash code. | 446 // where all keys in the same bucket share the same hash code. |
429 var _strings; | 447 var _strings; |
430 var _nums; | 448 var _nums; |
431 var _rest; | 449 var _rest; |
432 | 450 |
433 // The keys and values are stored in cells that are linked together | 451 // The keys and values are stored in cells that are linked together |
434 // to form a double linked list. | 452 // to form a double linked list. |
435 LinkedHashMapCell _first; | 453 LinkedHashMapCell _first; |
436 LinkedHashMapCell _last; | 454 LinkedHashMapCell _last; |
437 | 455 |
438 // We track the number of modifications done to the key set of the | 456 // We track the number of modifications done to the key set of the |
439 // hash map to be able to throw when the map is modified while being | 457 // hash map to be able to throw when the map is modified while being |
440 // iterated over. | 458 // iterated over. |
441 int _modifications = 0; | 459 int _modifications = 0; |
442 | 460 |
443 patch LinkedHashMap(); | 461 _LinkedHash(); |
444 | 462 |
445 patch int get length => _length; | 463 int get length => _length; |
446 patch bool get isEmpty => _length == 0; | 464 bool get isEmpty => _length == 0; |
447 patch bool get isNotEmpty => !isEmpty; | 465 bool get isNotEmpty => !isEmpty; |
448 | 466 |
449 | 467 |
450 patch Iterable<K> get keys { | 468 Iterable<K> get keys { |
451 return new LinkedHashMapKeyIterable<K>(this); | 469 return new LinkedHashMapKeyIterable<K>(this); |
452 } | 470 } |
453 | 471 |
454 patch Iterable<V> get values { | 472 Iterable<V> get values { |
455 return keys.map((each) => this[each]); | 473 return keys.map((each) => this[each]); |
456 } | 474 } |
457 | 475 |
458 patch bool containsKey(Object key) { | 476 bool containsKey(Object key) { |
459 if (_isStringKey(key)) { | 477 if (_isStringKey(key)) { |
460 var strings = _strings; | 478 var strings = _strings; |
461 if (strings == null) return false; | 479 if (strings == null) return false; |
462 LinkedHashMapCell cell = _getTableEntry(strings, key); | 480 LinkedHashMapCell cell = _getTableEntry(strings, key); |
463 return cell != null; | 481 return cell != null; |
464 } else if (_isNumericKey(key)) { | 482 } else if (_isNumericKey(key)) { |
465 var nums = _nums; | 483 var nums = _nums; |
466 if (nums == null) return false; | 484 if (nums == null) return false; |
467 LinkedHashMapCell cell = _getTableEntry(nums, key); | 485 LinkedHashMapCell cell = _getTableEntry(nums, key); |
468 return cell != null; | 486 return cell != null; |
469 } else { | 487 } else { |
470 var rest = _rest; | 488 var rest = _rest; |
471 if (rest == null) return false; | 489 if (rest == null) return false; |
472 var bucket = _getBucket(rest, key); | 490 var bucket = _getBucket(rest, key); |
473 return _findBucketIndex(bucket, key) >= 0; | 491 return _findBucketIndex(bucket, key) >= 0; |
474 } | 492 } |
475 } | 493 } |
476 | 494 |
477 patch bool containsValue(Object value) { | 495 bool containsValue(Object value) { |
478 return keys.any((each) => this[each] == value); | 496 return keys.any((each) => this[each] == value); |
479 } | 497 } |
480 | 498 |
481 patch void addAll(Map<K, V> other) { | 499 void addAll(Map<K, V> other) { |
482 other.forEach((K key, V value) { | 500 other.forEach((K key, V value) { |
483 this[key] = value; | 501 this[key] = value; |
484 }); | 502 }); |
485 } | 503 } |
486 | 504 |
487 patch V operator[](Object key) { | 505 V operator[](Object key) { |
488 if (_isStringKey(key)) { | 506 if (_isStringKey(key)) { |
489 var strings = _strings; | 507 var strings = _strings; |
490 if (strings == null) return null; | 508 if (strings == null) return null; |
491 LinkedHashMapCell cell = _getTableEntry(strings, key); | 509 LinkedHashMapCell cell = _getTableEntry(strings, key); |
492 return (cell == null) ? null : cell._value; | 510 return (cell == null) ? null : cell._value; |
493 } else if (_isNumericKey(key)) { | 511 } else if (_isNumericKey(key)) { |
494 var nums = _nums; | 512 var nums = _nums; |
495 if (nums == null) return null; | 513 if (nums == null) return null; |
496 LinkedHashMapCell cell = _getTableEntry(nums, key); | 514 LinkedHashMapCell cell = _getTableEntry(nums, key); |
497 return (cell == null) ? null : cell._value; | 515 return (cell == null) ? null : cell._value; |
498 } else { | 516 } else { |
499 var rest = _rest; | 517 var rest = _rest; |
500 if (rest == null) return null; | 518 if (rest == null) return null; |
501 var bucket = _getBucket(rest, key); | 519 var bucket = _getBucket(rest, key); |
502 int index = _findBucketIndex(bucket, key); | 520 int index = _findBucketIndex(bucket, key); |
503 if (index < 0) return null; | 521 if (index < 0) return null; |
504 LinkedHashMapCell cell = JS('var', '#[#]', bucket, index); | 522 LinkedHashMapCell cell = JS('var', '#[#]', bucket, index); |
505 return cell._value; | 523 return cell._value; |
506 } | 524 } |
507 } | 525 } |
508 | 526 |
509 patch void operator[]=(K key, V value) { | 527 void operator[]=(K key, V value) { |
510 if (_isStringKey(key)) { | 528 if (_isStringKey(key)) { |
511 var strings = _strings; | 529 var strings = _strings; |
512 if (strings == null) _strings = strings = _newHashTable(); | 530 if (strings == null) _strings = strings = _newHashTable(); |
513 _addHashTableEntry(strings, key, value); | 531 _addHashTableEntry(strings, key, value); |
514 } else if (_isNumericKey(key)) { | 532 } else if (_isNumericKey(key)) { |
515 var nums = _nums; | 533 var nums = _nums; |
516 if (nums == null) _nums = nums = _newHashTable(); | 534 if (nums == null) _nums = nums = _newHashTable(); |
517 _addHashTableEntry(nums, key, value); | 535 _addHashTableEntry(nums, key, value); |
518 } else { | 536 } else { |
519 var rest = _rest; | 537 var rest = _rest; |
520 if (rest == null) _rest = rest = _newHashTable(); | 538 if (rest == null) _rest = rest = _newHashTable(); |
521 var hash = _computeHashCode(key); | 539 var hash = _computeHashCode(key); |
522 var bucket = JS('var', '#[#]', rest, hash); | 540 var bucket = JS('var', '#[#]', rest, hash); |
523 if (bucket == null) { | 541 if (bucket == null) { |
524 LinkedHashMapCell cell = _newLinkedCell(key, value); | 542 LinkedHashMapCell cell = _newLinkedCell(key, value); |
525 _setTableEntry(rest, hash, JS('var', '[#]', cell)); | 543 _setTableEntry(rest, hash, JS('var', '[#]', cell)); |
526 } else { | 544 } else { |
527 int index = _findBucketIndex(bucket, key); | 545 int index = _findBucketIndex(bucket, key); |
528 if (index >= 0) { | 546 if (index >= 0) { |
529 LinkedHashMapCell cell = JS('var', '#[#]', bucket, index); | 547 LinkedHashMapCell cell = JS('var', '#[#]', bucket, index); |
530 cell._value = value; | 548 cell._value = value; |
531 } else { | 549 } else { |
532 LinkedHashMapCell cell = _newLinkedCell(key, value); | 550 LinkedHashMapCell cell = _newLinkedCell(key, value); |
533 JS('void', '#.push(#)', bucket, cell); | 551 JS('void', '#.push(#)', bucket, cell); |
534 } | 552 } |
535 } | 553 } |
536 } | 554 } |
537 } | 555 } |
538 | 556 |
539 patch V putIfAbsent(K key, V ifAbsent()) { | 557 V putIfAbsent(K key, V ifAbsent()) { |
540 if (containsKey(key)) return this[key]; | 558 if (containsKey(key)) return this[key]; |
541 V value = ifAbsent(); | 559 V value = ifAbsent(); |
542 this[key] = value; | 560 this[key] = value; |
543 return value; | 561 return value; |
544 } | 562 } |
545 | 563 |
546 patch V remove(Object key) { | 564 V remove(Object key) { |
547 if (_isStringKey(key)) { | 565 if (_isStringKey(key)) { |
548 return _removeHashTableEntry(_strings, key); | 566 return _removeHashTableEntry(_strings, key); |
549 } else if (_isNumericKey(key)) { | 567 } else if (_isNumericKey(key)) { |
550 return _removeHashTableEntry(_nums, key); | 568 return _removeHashTableEntry(_nums, key); |
551 } else { | 569 } else { |
552 var rest = _rest; | 570 var rest = _rest; |
553 if (rest == null) return null; | 571 if (rest == null) return null; |
554 var bucket = _getBucket(rest, key); | 572 var bucket = _getBucket(rest, key); |
555 int index = _findBucketIndex(bucket, key); | 573 int index = _findBucketIndex(bucket, key); |
556 if (index < 0) return null; | 574 if (index < 0) return null; |
557 // Use splice to remove the [cell] element at the index and | 575 // Use splice to remove the [cell] element at the index and |
558 // unlink the cell before returning its value. | 576 // unlink the cell before returning its value. |
559 LinkedHashMapCell cell = JS('var', '#.splice(#, 1)[0]', bucket, index); | 577 LinkedHashMapCell cell = JS('var', '#.splice(#, 1)[0]', bucket, index); |
560 _unlinkCell(cell); | 578 _unlinkCell(cell); |
561 // TODO(kasperl): Consider getting rid of the bucket list when | 579 // TODO(kasperl): Consider getting rid of the bucket list when |
562 // the length reaches zero. | 580 // the length reaches zero. |
563 return cell._value; | 581 return cell._value; |
564 } | 582 } |
565 } | 583 } |
566 | 584 |
567 patch void clear() { | 585 void clear() { |
568 if (_length > 0) { | 586 if (_length > 0) { |
569 _strings = _nums = _rest = _first = _last = null; | 587 _strings = _nums = _rest = _first = _last = null; |
570 _length = 0; | 588 _length = 0; |
571 _modified(); | 589 _modified(); |
572 } | 590 } |
573 } | 591 } |
574 | 592 |
575 patch void forEach(void action(K key, V value)) { | 593 void forEach(void action(K key, V value)) { |
576 LinkedHashMapCell cell = _first; | 594 LinkedHashMapCell cell = _first; |
577 int modifications = _modifications; | 595 int modifications = _modifications; |
578 while (cell != null) { | 596 while (cell != null) { |
579 action(cell._key, cell._value); | 597 action(cell._key, cell._value); |
580 if (modifications != _modifications) { | 598 if (modifications != _modifications) { |
581 throw new ConcurrentModificationError(this); | 599 throw new ConcurrentModificationError(this); |
582 } | 600 } |
583 cell = cell._next; | 601 cell = cell._next; |
584 } | 602 } |
585 } | 603 } |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
648 return key is String && key != '__proto__'; | 666 return key is String && key != '__proto__'; |
649 } | 667 } |
650 | 668 |
651 static bool _isNumericKey(var key) { | 669 static bool _isNumericKey(var key) { |
652 // Only treat unsigned 30-bit integers as numeric keys. This way, | 670 // Only treat unsigned 30-bit integers as numeric keys. This way, |
653 // we avoid converting them to strings when we use them as keys in | 671 // we avoid converting them to strings when we use them as keys in |
654 // the JavaScript hash table object. | 672 // the JavaScript hash table object. |
655 return key is num && JS('bool', '(# & 0x3ffffff) === #', key, key); | 673 return key is num && JS('bool', '(# & 0x3ffffff) === #', key, key); |
656 } | 674 } |
657 | 675 |
658 static int _computeHashCode(var key) { | 676 int _computeHashCode(var key) { |
659 // We force the hash codes to be unsigned 30-bit integers to avoid | 677 // We force the hash codes to be unsigned 30-bit integers to avoid |
660 // issues with problematic keys like '__proto__'. Another option | 678 // issues with problematic keys like '__proto__'. Another option |
661 // would be to throw an exception if the hash code isn't a number. | 679 // would be to throw an exception if the hash code isn't a number. |
662 return JS('int', '# & 0x3ffffff', key.hashCode); | 680 return JS('int', '# & 0x3ffffff', key.hashCode); |
663 } | 681 } |
664 | 682 |
665 static _getTableEntry(var table, var key) { | 683 static _getTableEntry(var table, var key) { |
666 return JS('var', '#[#]', table, key); | 684 return JS('var', '#[#]', table, key); |
667 } | 685 } |
668 | 686 |
669 static void _setTableEntry(var table, var key, var value) { | 687 static void _setTableEntry(var table, var key, var value) { |
670 assert(value != null); | 688 assert(value != null); |
671 JS('void', '#[#] = #', table, key, value); | 689 JS('void', '#[#] = #', table, key, value); |
672 } | 690 } |
673 | 691 |
674 static void _deleteTableEntry(var table, var key) { | 692 static void _deleteTableEntry(var table, var key) { |
675 JS('void', 'delete #[#]', table, key); | 693 JS('void', 'delete #[#]', table, key); |
676 } | 694 } |
677 | 695 |
678 static List _getBucket(var table, var key) { | 696 List _getBucket(var table, var key) { |
679 var hash = _computeHashCode(key); | 697 var hash = _computeHashCode(key); |
680 return JS('var', '#[#]', table, hash); | 698 return JS('var', '#[#]', table, hash); |
681 } | 699 } |
682 | 700 |
683 static int _findBucketIndex(var bucket, var key) { | 701 int _findBucketIndex(var bucket, var key) { |
684 if (bucket == null) return -1; | 702 if (bucket == null) return -1; |
685 int length = JS('int', '#.length', bucket); | 703 int length = JS('int', '#.length', bucket); |
686 for (int i = 0; i < length; i++) { | 704 for (int i = 0; i < length; i++) { |
687 LinkedHashMapCell cell = JS('var', '#[#]', bucket, i); | 705 LinkedHashMapCell cell = JS('var', '#[#]', bucket, i); |
688 if (cell._key == key) return i; | 706 if (cell._key == key) return i; |
689 } | 707 } |
690 return -1; | 708 return -1; |
691 } | 709 } |
692 | 710 |
693 static _newHashTable() { | 711 static _newHashTable() { |
694 // Create a new JavaScript object to be used as a hash table. Use | 712 // Create a new JavaScript object to be used as a hash table. Use |
695 // Object.create to avoid the properties on Object.prototype | 713 // Object.create to avoid the properties on Object.prototype |
696 // showing up as entries. | 714 // showing up as entries. |
697 var table = JS('var', 'Object.create(null)'); | 715 var table = JS('var', 'Object.create(null)'); |
698 // Attempt to force the hash table into 'dictionary' mode by | 716 // Attempt to force the hash table into 'dictionary' mode by |
699 // adding a property to it and deleting it again. | 717 // adding a property to it and deleting it again. |
700 var temporaryKey = '<non-identifier-key>'; | 718 var temporaryKey = '<non-identifier-key>'; |
701 _setTableEntry(table, temporaryKey, table); | 719 _setTableEntry(table, temporaryKey, table); |
702 _deleteTableEntry(table, temporaryKey); | 720 _deleteTableEntry(table, temporaryKey); |
703 return table; | 721 return table; |
704 } | 722 } |
| 723 |
| 724 String toString() => Maps.mapToString(this); |
| 725 } |
| 726 |
| 727 class _LinkedIdentityHashMap<K, V> extends _LinkedHash<K, V> { |
| 728 int _findBucketIndex(var bucket, var key) { |
| 729 if (bucket == null) return -1; |
| 730 int length = JS('int', '#.length', bucket); |
| 731 for (int i = 0; i < length; i++) { |
| 732 LinkedHashMapCell cell = JS('var', '#[#]', bucket, i); |
| 733 if (identical(cell._key, key)) return i; |
| 734 } |
| 735 return -1; |
| 736 } |
| 737 } |
| 738 |
| 739 class _LinkedCustomHashMap<K, V> extends _LinkedHash<K, V> { |
| 740 final _Equality<K> _equals; |
| 741 final _Hasher<K> _hashCode; |
| 742 _LinkedCustomHashMap(this._equals, this._hashCode); |
| 743 |
| 744 int _computeHashCode(var key) { |
| 745 // We force the hash codes to be unsigned 30-bit integers to avoid |
| 746 // issues with problematic keys like '__proto__'. Another option |
| 747 // would be to throw an exception if the hash code isn't a number. |
| 748 return JS('int', '# & 0x3ffffff', _hashCode(key)); |
| 749 } |
| 750 |
| 751 int _findBucketIndex(var bucket, var key) { |
| 752 if (bucket == null) return -1; |
| 753 int length = JS('int', '#.length', bucket); |
| 754 for (int i = 0; i < length; i++) { |
| 755 LinkedHashMapCell cell = JS('var', '#[#]', bucket, i); |
| 756 if (_equals(cell._key, key)) return i; |
| 757 } |
| 758 return -1; |
| 759 } |
705 } | 760 } |
706 | 761 |
707 class LinkedHashMapCell { | 762 class LinkedHashMapCell { |
708 final _key; | 763 final _key; |
709 var _value; | 764 var _value; |
710 | 765 |
711 LinkedHashMapCell _next; | 766 LinkedHashMapCell _next; |
712 LinkedHashMapCell _previous; | 767 LinkedHashMapCell _previous; |
713 | 768 |
714 LinkedHashMapCell(this._key, this._value); | 769 LinkedHashMapCell(this._key, this._value); |
(...skipping 658 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1373 } else if (_cell == null) { | 1428 } else if (_cell == null) { |
1374 _current = null; | 1429 _current = null; |
1375 return false; | 1430 return false; |
1376 } else { | 1431 } else { |
1377 _current = _cell._element; | 1432 _current = _cell._element; |
1378 _cell = _cell._next; | 1433 _cell = _cell._next; |
1379 return true; | 1434 return true; |
1380 } | 1435 } |
1381 } | 1436 } |
1382 } | 1437 } |
OLD | NEW |