Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(411)

Side by Side Diff: sdk/lib/_internal/compiler/js_lib/collection_patch.dart

Issue 949733003: Share the JavaScript based LinkedHashMap implementation between constant maps and the LinkedHashMap… (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 import 'dart:_js_helper' show 7 import 'dart:_js_helper' show
8 fillLiteralMap, InternalMap, NoInline, NoThrows, patch; 8 fillLiteralMap, InternalMap, NoInline, NoThrows, patch, JsLinkedHashMap,
9 LinkedHashMapCell, LinkedHashMapKeyIterable, LinkedHashMapKeyIterator;
9 10
10 @patch 11 @patch
11 class HashMap<K, V> { 12 class HashMap<K, V> {
12 @patch 13 @patch
13 factory HashMap({ bool equals(K key1, K key2), 14 factory HashMap({ bool equals(K key1, K key2),
14 int hashCode(K key), 15 int hashCode(K key),
15 bool isValidKey(potentialKey) }) { 16 bool isValidKey(potentialKey) }) {
16 if (isValidKey == null) { 17 if (isValidKey == null) {
17 if (hashCode == null) { 18 if (hashCode == null) {
18 if (equals == null) { 19 if (equals == null) {
(...skipping 465 matching lines...) Expand 10 before | Expand all | Expand 10 after
484 485
485 @patch 486 @patch
486 class LinkedHashMap<K, V> { 487 class LinkedHashMap<K, V> {
487 @patch 488 @patch
488 factory LinkedHashMap({ bool equals(K key1, K key2), 489 factory LinkedHashMap({ bool equals(K key1, K key2),
489 int hashCode(K key), 490 int hashCode(K key),
490 bool isValidKey(potentialKey) }) { 491 bool isValidKey(potentialKey) }) {
491 if (isValidKey == null) { 492 if (isValidKey == null) {
492 if (hashCode == null) { 493 if (hashCode == null) {
493 if (equals == null) { 494 if (equals == null) {
494 return new _LinkedHashMap<K, V>(); 495 return new JsLinkedHashMap<K, V>();
495 } 496 }
496 hashCode = _defaultHashCode; 497 hashCode = _defaultHashCode;
497 } else { 498 } else {
498 if (identical(identityHashCode, hashCode) && 499 if (identical(identityHashCode, hashCode) &&
499 identical(identical, equals)) { 500 identical(identical, equals)) {
500 return new _LinkedIdentityHashMap<K, V>(); 501 return new _LinkedIdentityHashMap<K, V>();
501 } 502 }
502 if (equals == null) { 503 if (equals == null) {
503 equals = _defaultEquals; 504 equals = _defaultEquals;
504 } 505 }
505 } 506 }
506 } else { 507 } else {
507 if (hashCode == null) { 508 if (hashCode == null) {
508 hashCode = _defaultHashCode; 509 hashCode = _defaultHashCode;
509 } 510 }
510 if (equals == null) { 511 if (equals == null) {
511 equals = _defaultEquals; 512 equals = _defaultEquals;
512 } 513 }
513 } 514 }
514 return new _LinkedCustomHashMap<K, V>(equals, hashCode, isValidKey); 515 return new _LinkedCustomHashMap<K, V>(equals, hashCode, isValidKey);
515 } 516 }
516 517
517 @patch 518 @patch
518 factory LinkedHashMap.identity() = _LinkedIdentityHashMap<K, V>; 519 factory LinkedHashMap.identity() = _LinkedIdentityHashMap<K, V>;
519 520
520 // Private factory constructor called by generated code for map literals. 521 // Private factory constructor called by generated code for map literals.
521 @NoInline() 522 @NoInline()
522 factory LinkedHashMap._literal(List keyValuePairs) { 523 factory LinkedHashMap._literal(List keyValuePairs) {
523 return fillLiteralMap(keyValuePairs, new _LinkedHashMap<K, V>()); 524 return fillLiteralMap(keyValuePairs, new JsLinkedHashMap<K, V>());
524 } 525 }
525 526
526 // Private factory constructor called by generated code for map literals. 527 // Private factory constructor called by generated code for map literals.
527 @NoThrows() @NoInline() 528 @NoThrows() @NoInline()
528 factory LinkedHashMap._empty() { 529 factory LinkedHashMap._empty() {
529 return new _LinkedHashMap<K, V>(); 530 return new JsLinkedHashMap<K, V>();
530 } 531 }
531 } 532 }
532 533
533 class _LinkedHashMap<K, V> implements LinkedHashMap<K, V>, InternalMap { 534 class _LinkedIdentityHashMap<K, V> extends JsLinkedHashMap<K, V> {
534 int _length = 0; 535 int internalComputeHashCode(var key) {
535
536 // The hash map contents are divided into three parts: one part for
537 // string keys, one for numeric keys, and one for the rest. String
538 // and numeric keys map directly to their linked cells, but the rest
539 // of the entries are stored in bucket lists of the form:
540 //
541 // [cell-0, cell-1, ...]
542 //
543 // where all keys in the same bucket share the same hash code.
544 var _strings;
545 var _nums;
546 var _rest;
547
548 // The keys and values are stored in cells that are linked together
549 // to form a double linked list.
550 LinkedHashMapCell _first;
551 LinkedHashMapCell _last;
552
553 // We track the number of modifications done to the key set of the
554 // hash map to be able to throw when the map is modified while being
555 // iterated over.
556 int _modifications = 0;
557
558 _LinkedHashMap();
559
560
561 int get length => _length;
562 bool get isEmpty => _length == 0;
563 bool get isNotEmpty => !isEmpty;
564
565 Iterable<K> get keys {
566 return new LinkedHashMapKeyIterable<K>(this);
567 }
568
569 Iterable<V> get values {
570 return new MappedIterable<K, V>(keys, (each) => this[each]);
571 }
572
573 bool containsKey(Object key) {
574 if (_isStringKey(key)) {
575 var strings = _strings;
576 if (strings == null) return false;
577 LinkedHashMapCell cell = _getTableEntry(strings, key);
578 return cell != null;
579 } else if (_isNumericKey(key)) {
580 var nums = _nums;
581 if (nums == null) return false;
582 LinkedHashMapCell cell = _getTableEntry(nums, key);
583 return cell != null;
584 } else {
585 return _containsKey(key);
586 }
587 }
588
589 bool _containsKey(Object key) {
590 var rest = _rest;
591 if (rest == null) return false;
592 var bucket = _getBucket(rest, key);
593 return _findBucketIndex(bucket, key) >= 0;
594 }
595
596 bool containsValue(Object value) {
597 return keys.any((each) => this[each] == value);
598 }
599
600 void addAll(Map<K, V> other) {
601 other.forEach((K key, V value) {
602 this[key] = value;
603 });
604 }
605
606 V operator[](Object key) {
607 if (_isStringKey(key)) {
608 var strings = _strings;
609 if (strings == null) return null;
610 LinkedHashMapCell cell = _getTableEntry(strings, key);
611 return (cell == null) ? null : cell._value;
612 } else if (_isNumericKey(key)) {
613 var nums = _nums;
614 if (nums == null) return null;
615 LinkedHashMapCell cell = _getTableEntry(nums, key);
616 return (cell == null) ? null : cell._value;
617 } else {
618 return _get(key);
619 }
620 }
621
622 V _get(Object key) {
623 var rest = _rest;
624 if (rest == null) return null;
625 var bucket = _getBucket(rest, key);
626 int index = _findBucketIndex(bucket, key);
627 if (index < 0) return null;
628 LinkedHashMapCell cell = JS('var', '#[#]', bucket, index);
629 return cell._value;
630 }
631
632 void operator[]=(K key, V value) {
633 if (_isStringKey(key)) {
634 var strings = _strings;
635 if (strings == null) _strings = strings = _newHashTable();
636 _addHashTableEntry(strings, key, value);
637 } else if (_isNumericKey(key)) {
638 var nums = _nums;
639 if (nums == null) _nums = nums = _newHashTable();
640 _addHashTableEntry(nums, key, value);
641 } else {
642 _set(key, value);
643 }
644 }
645
646 void _set(K key, V value) {
647 var rest = _rest;
648 if (rest == null) _rest = rest = _newHashTable();
649 var hash = _computeHashCode(key);
650 var bucket = JS('var', '#[#]', rest, hash);
651 if (bucket == null) {
652 LinkedHashMapCell cell = _newLinkedCell(key, value);
653 _setTableEntry(rest, hash, JS('var', '[#]', cell));
654 } else {
655 int index = _findBucketIndex(bucket, key);
656 if (index >= 0) {
657 LinkedHashMapCell cell = JS('var', '#[#]', bucket, index);
658 cell._value = value;
659 } else {
660 LinkedHashMapCell cell = _newLinkedCell(key, value);
661 JS('void', '#.push(#)', bucket, cell);
662 }
663 }
664 }
665
666 V putIfAbsent(K key, V ifAbsent()) {
667 if (containsKey(key)) return this[key];
668 V value = ifAbsent();
669 this[key] = value;
670 return value;
671 }
672
673 V remove(Object key) {
674 if (_isStringKey(key)) {
675 return _removeHashTableEntry(_strings, key);
676 } else if (_isNumericKey(key)) {
677 return _removeHashTableEntry(_nums, key);
678 } else {
679 return _remove(key);
680 }
681 }
682
683 V _remove(Object key) {
684 var rest = _rest;
685 if (rest == null) return null;
686 var bucket = _getBucket(rest, key);
687 int index = _findBucketIndex(bucket, key);
688 if (index < 0) return null;
689 // Use splice to remove the [cell] element at the index and
690 // unlink the cell before returning its value.
691 LinkedHashMapCell cell = JS('var', '#.splice(#, 1)[0]', bucket, index);
692 _unlinkCell(cell);
693 // TODO(kasperl): Consider getting rid of the bucket list when
694 // the length reaches zero.
695 return cell._value;
696 }
697
698 void clear() {
699 if (_length > 0) {
700 _strings = _nums = _rest = _first = _last = null;
701 _length = 0;
702 _modified();
703 }
704 }
705
706 void forEach(void action(K key, V value)) {
707 LinkedHashMapCell cell = _first;
708 int modifications = _modifications;
709 while (cell != null) {
710 action(cell._key, cell._value);
711 if (modifications != _modifications) {
712 throw new ConcurrentModificationError(this);
713 }
714 cell = cell._next;
715 }
716 }
717
718 void _addHashTableEntry(var table, K key, V value) {
719 LinkedHashMapCell cell = _getTableEntry(table, key);
720 if (cell == null) {
721 _setTableEntry(table, key, _newLinkedCell(key, value));
722 } else {
723 cell._value = value;
724 }
725 }
726
727 V _removeHashTableEntry(var table, Object key) {
728 if (table == null) return null;
729 LinkedHashMapCell cell = _getTableEntry(table, key);
730 if (cell == null) return null;
731 _unlinkCell(cell);
732 _deleteTableEntry(table, key);
733 return cell._value;
734 }
735
736 void _modified() {
737 // Value cycles after 2^30 modifications. If you keep hold of an
738 // iterator for that long, you might miss a modification
739 // detection, and iteration can go sour. Don't do that.
740 _modifications = (_modifications + 1) & 0x3ffffff;
741 }
742
743 // Create a new cell and link it in as the last one in the list.
744 LinkedHashMapCell _newLinkedCell(K key, V value) {
745 LinkedHashMapCell cell = new LinkedHashMapCell(key, value);
746 if (_first == null) {
747 _first = _last = cell;
748 } else {
749 LinkedHashMapCell last = _last;
750 cell._previous = last;
751 _last = last._next = cell;
752 }
753 _length++;
754 _modified();
755 return cell;
756 }
757
758 // Unlink the given cell from the linked list of cells.
759 void _unlinkCell(LinkedHashMapCell cell) {
760 LinkedHashMapCell previous = cell._previous;
761 LinkedHashMapCell next = cell._next;
762 if (previous == null) {
763 assert(cell == _first);
764 _first = next;
765 } else {
766 previous._next = next;
767 }
768 if (next == null) {
769 assert(cell == _last);
770 _last = previous;
771 } else {
772 next._previous = previous;
773 }
774 _length--;
775 _modified();
776 }
777
778 static bool _isStringKey(var key) {
779 return key is String && key != '__proto__';
780 }
781
782 static bool _isNumericKey(var key) {
783 // Only treat unsigned 30-bit integers as numeric keys. This way,
784 // we avoid converting them to strings when we use them as keys in
785 // the JavaScript hash table object.
786 return key is num && JS('bool', '(# & 0x3ffffff) === #', key, key);
787 }
788
789 int _computeHashCode(var key) {
790 // We force the hash codes to be unsigned 30-bit integers to avoid
791 // issues with problematic keys like '__proto__'. Another option
792 // would be to throw an exception if the hash code isn't a number.
793 return JS('int', '# & 0x3ffffff', key.hashCode);
794 }
795
796 static _getTableEntry(var table, var key) {
797 return JS('var', '#[#]', table, key);
798 }
799
800 static void _setTableEntry(var table, var key, var value) {
801 assert(value != null);
802 JS('void', '#[#] = #', table, key, value);
803 }
804
805 static void _deleteTableEntry(var table, var key) {
806 JS('void', 'delete #[#]', table, key);
807 }
808
809 List _getBucket(var table, var key) {
810 var hash = _computeHashCode(key);
811 return JS('var', '#[#]', table, hash);
812 }
813
814 int _findBucketIndex(var bucket, var key) {
815 if (bucket == null) return -1;
816 int length = JS('int', '#.length', bucket);
817 for (int i = 0; i < length; i++) {
818 LinkedHashMapCell cell = JS('var', '#[#]', bucket, i);
819 if (cell._key == key) return i;
820 }
821 return -1;
822 }
823
824 static _newHashTable() {
825 // Create a new JavaScript object to be used as a hash table. Use
826 // Object.create to avoid the properties on Object.prototype
827 // showing up as entries.
828 var table = JS('var', 'Object.create(null)');
829 // Attempt to force the hash table into 'dictionary' mode by
830 // adding a property to it and deleting it again.
831 var temporaryKey = '<non-identifier-key>';
832 _setTableEntry(table, temporaryKey, table);
833 _deleteTableEntry(table, temporaryKey);
834 return table;
835 }
836
837 String toString() => Maps.mapToString(this);
838 }
839
840 class _LinkedIdentityHashMap<K, V> extends _LinkedHashMap<K, V> {
841 int _computeHashCode(var key) {
842 // We force the hash codes to be unsigned 30-bit integers to avoid 536 // We force the hash codes to be unsigned 30-bit integers to avoid
843 // issues with problematic keys like '__proto__'. Another option 537 // issues with problematic keys like '__proto__'. Another option
844 // would be to throw an exception if the hash code isn't a number. 538 // would be to throw an exception if the hash code isn't a number.
845 return JS('int', '# & 0x3ffffff', identityHashCode(key)); 539 return JS('int', '# & 0x3ffffff', identityHashCode(key));
846 } 540 }
847 541
848 int _findBucketIndex(var bucket, var key) { 542 int internalFindBucketIndex(var bucket, var key) {
sra1 2015/05/15 22:59:33 Can this be rewritten to avoid public names that a
Johnni Winther 2015/05/18 07:11:30 If not, maybe we should make named of injected mem
849 if (bucket == null) return -1; 543 if (bucket == null) return -1;
850 int length = JS('int', '#.length', bucket); 544 int length = JS('int', '#.length', bucket);
851 for (int i = 0; i < length; i++) { 545 for (int i = 0; i < length; i++) {
852 LinkedHashMapCell cell = JS('var', '#[#]', bucket, i); 546 LinkedHashMapCell cell = JS('var', '#[#]', bucket, i);
853 if (identical(cell._key, key)) return i; 547 if (identical(cell.key, key)) return i;
854 } 548 }
855 return -1; 549 return -1;
856 } 550 }
857 } 551 }
858 552
859 class _LinkedCustomHashMap<K, V> extends _LinkedHashMap<K, V> { 553 class _LinkedCustomHashMap<K, V> extends JsLinkedHashMap<K, V> {
860 final _Equality<K> _equals; 554 final _Equality<K> _equals;
861 final _Hasher<K> _hashCode; 555 final _Hasher<K> _hashCode;
862 final _Predicate _validKey; 556 final _Predicate _validKey;
863 _LinkedCustomHashMap(this._equals, this._hashCode, 557 _LinkedCustomHashMap(this._equals, this._hashCode,
864 bool validKey(potentialKey)) 558 bool validKey(potentialKey))
865 : _validKey = (validKey != null) ? validKey : ((v) => v is K); 559 : _validKey = (validKey != null) ? validKey : ((v) => v is K);
866 560
867 V operator[](Object key) { 561 V operator[](Object key) {
868 if (!_validKey(key)) return null; 562 if (!_validKey(key)) return null;
869 return super._get(key); 563 return super.internalGet(key);
870 } 564 }
871 565
872 void operator[]=(K key, V value) { 566 void operator[]=(K key, V value) {
873 super._set(key, value); 567 super.internalSet(key, value);
874 } 568 }
875 569
876 bool containsKey(Object key) { 570 bool containsKey(Object key) {
877 if (!_validKey(key)) return false; 571 if (!_validKey(key)) return false;
878 return super._containsKey(key); 572 return super.internalContainsKey(key);
879 } 573 }
880 574
881 V remove(Object key) { 575 V remove(Object key) {
882 if (!_validKey(key)) return null; 576 if (!_validKey(key)) return null;
883 return super._remove(key); 577 return super.internalRemove(key);
884 } 578 }
885 579
886 int _computeHashCode(var key) { 580 int internalComputeHashCode(var key) {
887 // We force the hash codes to be unsigned 30-bit integers to avoid 581 // We force the hash codes to be unsigned 30-bit integers to avoid
888 // issues with problematic keys like '__proto__'. Another option 582 // issues with problematic keys like '__proto__'. Another option
889 // would be to throw an exception if the hash code isn't a number. 583 // would be to throw an exception if the hash code isn't a number.
890 return JS('int', '# & 0x3ffffff', _hashCode(key)); 584 return JS('int', '# & 0x3ffffff', _hashCode(key));
891 } 585 }
892 586
893 int _findBucketIndex(var bucket, var key) { 587 int internalFindBucketIndex(var bucket, var key) {
894 if (bucket == null) return -1; 588 if (bucket == null) return -1;
895 int length = JS('int', '#.length', bucket); 589 int length = JS('int', '#.length', bucket);
896 for (int i = 0; i < length; i++) { 590 for (int i = 0; i < length; i++) {
897 LinkedHashMapCell cell = JS('var', '#[#]', bucket, i); 591 LinkedHashMapCell cell = JS('var', '#[#]', bucket, i);
898 if (_equals(cell._key, key)) return i; 592 if (_equals(cell.key, key)) return i;
899 } 593 }
900 return -1; 594 return -1;
901 } 595 }
902 } 596 }
903 597
904 class LinkedHashMapCell {
905 final _key;
906 var _value;
907
908 LinkedHashMapCell _next;
909 LinkedHashMapCell _previous;
910
911 LinkedHashMapCell(this._key, this._value);
912 }
913
914 class LinkedHashMapKeyIterable<E> extends IterableBase<E>
915 implements EfficientLength {
916 final _map;
917 LinkedHashMapKeyIterable(this._map);
918
919 int get length => _map._length;
920 bool get isEmpty => _map._length == 0;
921
922 Iterator<E> get iterator {
923 return new LinkedHashMapKeyIterator<E>(_map, _map._modifications);
924 }
925
926 bool contains(Object element) {
927 return _map.containsKey(element);
928 }
929
930 void forEach(void f(E element)) {
931 LinkedHashMapCell cell = _map._first;
932 int modifications = _map._modifications;
933 while (cell != null) {
934 f(cell._key);
935 if (modifications != _map._modifications) {
936 throw new ConcurrentModificationError(_map);
937 }
938 cell = cell._next;
939 }
940 }
941 }
942
943 class LinkedHashMapKeyIterator<E> implements Iterator<E> {
944 final _map;
945 final int _modifications;
946 LinkedHashMapCell _cell;
947 E _current;
948
949 LinkedHashMapKeyIterator(this._map, this._modifications) {
950 _cell = _map._first;
951 }
952
953 E get current => _current;
954
955 bool moveNext() {
956 if (_modifications != _map._modifications) {
957 throw new ConcurrentModificationError(_map);
958 } else if (_cell == null) {
959 _current = null;
960 return false;
961 } else {
962 _current = _cell._key;
963 _cell = _cell._next;
964 return true;
965 }
966 }
967 }
968
969 @patch 598 @patch
970 class HashSet<E> { 599 class HashSet<E> {
971 @patch 600 @patch
972 factory HashSet({ bool equals(E e1, E e2), 601 factory HashSet({ bool equals(E e1, E e2),
973 int hashCode(E e), 602 int hashCode(E e),
974 bool isValidKey(potentialKey) }) { 603 bool isValidKey(potentialKey) }) {
975 if (isValidKey == null) { 604 if (isValidKey == null) {
976 if (hashCode == null) { 605 if (hashCode == null) {
977 if (equals == null) { 606 if (equals == null) {
978 return new _HashSet<E>(); 607 return new _HashSet<E>();
(...skipping 850 matching lines...) Expand 10 before | Expand all | Expand 10 after
1829 } else if (_cell == null) { 1458 } else if (_cell == null) {
1830 _current = null; 1459 _current = null;
1831 return false; 1460 return false;
1832 } else { 1461 } else {
1833 _current = _cell._element; 1462 _current = _cell._element;
1834 _cell = _cell._next; 1463 _cell = _cell._next;
1835 return true; 1464 return true;
1836 } 1465 }
1837 } 1466 }
1838 } 1467 }
OLDNEW
« no previous file with comments | « pkg/compiler/lib/src/resolution/registry.dart ('k') | sdk/lib/_internal/compiler/js_lib/constant_map.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698