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

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

Issue 1188713005: Use an ES6 map for a linked identity hash map. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: fix tests Created 5 years, 6 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
« no previous file with comments | « no previous file | sdk/lib/_internal/compiler/js_lib/foreign_helper.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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, NoSideEffects, NoThrows, patch, 8 fillLiteralMap, InternalMap, NoInline, NoSideEffects, NoThrows, patch,
9 JsLinkedHashMap, LinkedHashMapCell, LinkedHashMapKeyIterable, 9 JsLinkedHashMap, LinkedHashMapCell, LinkedHashMapKeyIterable,
10 LinkedHashMapKeyIterator; 10 LinkedHashMapKeyIterator;
11 11
12 const _USE_ES6_MAPS = const bool.fromEnvironment("dart2js.use.es6.maps");
13
12 @patch 14 @patch
13 class HashMap<K, V> { 15 class HashMap<K, V> {
14 @patch 16 @patch
15 factory HashMap({ bool equals(K key1, K key2), 17 factory HashMap({ bool equals(K key1, K key2),
16 int hashCode(K key), 18 int hashCode(K key),
17 bool isValidKey(potentialKey) }) { 19 bool isValidKey(potentialKey) }) {
18 if (isValidKey == null) { 20 if (isValidKey == null) {
19 if (hashCode == null) { 21 if (hashCode == null) {
20 if (equals == null) { 22 if (equals == null) {
21 return new _HashMap<K, V>(); 23 return new _HashMap<K, V>();
(...skipping 471 matching lines...) Expand 10 before | Expand all | Expand 10 after
493 bool isValidKey(potentialKey) }) { 495 bool isValidKey(potentialKey) }) {
494 if (isValidKey == null) { 496 if (isValidKey == null) {
495 if (hashCode == null) { 497 if (hashCode == null) {
496 if (equals == null) { 498 if (equals == null) {
497 return new JsLinkedHashMap<K, V>.es6(); 499 return new JsLinkedHashMap<K, V>.es6();
498 } 500 }
499 hashCode = _defaultHashCode; 501 hashCode = _defaultHashCode;
500 } else { 502 } else {
501 if (identical(identityHashCode, hashCode) && 503 if (identical(identityHashCode, hashCode) &&
502 identical(identical, equals)) { 504 identical(identical, equals)) {
503 return new _LinkedIdentityHashMap<K, V>(); 505 return new _LinkedIdentityHashMap<K, V>.es6();
504 } 506 }
505 if (equals == null) { 507 if (equals == null) {
506 equals = _defaultEquals; 508 equals = _defaultEquals;
507 } 509 }
508 } 510 }
509 } else { 511 } else {
510 if (hashCode == null) { 512 if (hashCode == null) {
511 hashCode = _defaultHashCode; 513 hashCode = _defaultHashCode;
512 } 514 }
513 if (equals == null) { 515 if (equals == null) {
514 equals = _defaultEquals; 516 equals = _defaultEquals;
515 } 517 }
516 } 518 }
517 return new _LinkedCustomHashMap<K, V>(equals, hashCode, isValidKey); 519 return new _LinkedCustomHashMap<K, V>(equals, hashCode, isValidKey);
518 } 520 }
519 521
520 @patch 522 @patch
521 factory LinkedHashMap.identity() = _LinkedIdentityHashMap<K, V>; 523 factory LinkedHashMap.identity() = _LinkedIdentityHashMap<K, V>.es6;
522 524
523 // Private factory constructor called by generated code for map literals. 525 // Private factory constructor called by generated code for map literals.
524 @NoInline() 526 @NoInline()
525 factory LinkedHashMap._literal(List keyValuePairs) { 527 factory LinkedHashMap._literal(List keyValuePairs) {
526 return fillLiteralMap(keyValuePairs, new JsLinkedHashMap<K, V>.es6()); 528 return fillLiteralMap(keyValuePairs, new JsLinkedHashMap<K, V>.es6());
527 } 529 }
528 530
529 // Private factory constructor called by generated code for map literals. 531 // Private factory constructor called by generated code for map literals.
530 @NoThrows() @NoInline() @NoSideEffects() 532 @NoThrows() @NoInline() @NoSideEffects()
531 factory LinkedHashMap._empty() { 533 factory LinkedHashMap._empty() {
532 return new JsLinkedHashMap<K, V>.es6(); 534 return new JsLinkedHashMap<K, V>.es6();
533 } 535 }
534 536
535 // Private factory static function called by generated code for map literals. 537 // Private factory static function called by generated code for map literals.
536 // This version is for map literals without type parameters. 538 // This version is for map literals without type parameters.
537 @NoInline() 539 @NoInline()
538 static _makeEmpty() => new JsLinkedHashMap(); 540 static _makeEmpty() => new JsLinkedHashMap();
539 541
540 // Private factory static function called by generated code for map literals. 542 // Private factory static function called by generated code for map literals.
541 // This version is for map literals without type parameters. 543 // This version is for map literals without type parameters.
542 @NoInline() 544 @NoInline()
543 static _makeLiteral(keyValuePairs) => 545 static _makeLiteral(keyValuePairs) =>
544 fillLiteralMap(keyValuePairs, new JsLinkedHashMap()); 546 fillLiteralMap(keyValuePairs, new JsLinkedHashMap());
545 } 547 }
546 548
547 // TODO(floitsch): use ES6 Maps when available.
548 class _LinkedIdentityHashMap<K, V> extends JsLinkedHashMap<K, V> { 549 class _LinkedIdentityHashMap<K, V> extends JsLinkedHashMap<K, V> {
550 static bool get _supportsEs6Maps {
551 return JS('returns:bool;depends:none;effects:none;throws:never;gvn:true',
552 'typeof Map != "undefined"');
floitsch 2015/06/18 09:48:19 indent.
Harry Terkelsen 2015/06/18 16:12:36 Done.
553 }
554
555 factory _LinkedIdentityHashMap.es6() {
556 return (_USE_ES6_MAPS && _LinkedIdentityHashMap._supportsEs6Maps)
557 ? new _LinkedIdentityHashMap<K, V>()
floitsch 2015/06/18 09:48:19 indent by 2 more.
Harry Terkelsen 2015/06/18 16:12:36 Done.
558 : new _Es6LinkedIdentityHashMap<K, V>();
559 }
560
561 _LinkedIdentityHashMap();
562
549 int internalComputeHashCode(var key) { 563 int internalComputeHashCode(var key) {
550 // We force the hash codes to be unsigned 30-bit integers to avoid 564 // We force the hash codes to be unsigned 30-bit integers to avoid
551 // issues with problematic keys like '__proto__'. Another option 565 // issues with problematic keys like '__proto__'. Another option
552 // would be to throw an exception if the hash code isn't a number. 566 // would be to throw an exception if the hash code isn't a number.
553 return JS('int', '# & 0x3ffffff', identityHashCode(key)); 567 return JS('int', '# & 0x3ffffff', identityHashCode(key));
554 } 568 }
555 569
556 int internalFindBucketIndex(var bucket, var key) { 570 int internalFindBucketIndex(var bucket, var key) {
557 if (bucket == null) return -1; 571 if (bucket == null) return -1;
558 int length = JS('int', '#.length', bucket); 572 int length = JS('int', '#.length', bucket);
559 for (int i = 0; i < length; i++) { 573 for (int i = 0; i < length; i++) {
560 LinkedHashMapCell cell = JS('var', '#[#]', bucket, i); 574 LinkedHashMapCell cell = JS('var', '#[#]', bucket, i);
561 if (identical(cell.hashMapCellKey, key)) return i; 575 if (identical(cell.hashMapCellKey, key)) return i;
562 } 576 }
563 return -1; 577 return -1;
564 } 578 }
565 } 579 }
566 580
581 class _Es6LinkedIdentityHashMap<K, V>
582 implements LinkedHashMap<K, V>, InternalMap {
583 final _map;
584 int _modifications = 0;
585
586 _Es6LinkedIdentityHashMap() : _map = JS('var', 'new Map()');
587
588 int get length => JS('int', '#.size', _map);
589 bool get isEmpty => length == 0;
590 bool get isNotEmpty => !isEmpty;
591
592 Iterable<K> get keys => new _Es6MapIterable<K>(this, true);
593
594 Iterable<V> get values =>
595 new _Es6MapIterable<V>(this, false);
596
597 bool containsKey(Object key) {
598 return JS('bool', '#.has(#)', _map, key);
599 }
600
601 bool containsValue(Object value) {
602 return values.any((each) => each == value);
603 }
604
605 void addAll(Map<K, V> other) {
606 other.forEach((K key, V value) {
607 this[key] = value;
608 _modified();
609 });
610 }
611
612 V operator[](Object key) {
613 var value = JS('var', '#.get(#)', _map, key);
614 bool isUndefined = JS('bool', '# === "undefined"', value);
615 return isUndefined ? null : value;
floitsch 2015/06/18 09:48:19 why not just return the value? undefined and null
Harry Terkelsen 2015/06/18 16:12:36 good to know
616 }
617
618 void operator[]=(K key, V value) {
619 JS('var', '#.set(#, #)', _map, key, value);
620 _modified();
621 }
622
623 V putIfAbsent(K key, V ifAbsent()) {
624 if (containsKey(key)) return this[key];
625 V value = ifAbsent();
626 this[key] = value;
627 _modified();
628 return value;
629 }
630
631 V remove(Object key) {
632 V value = this[key];
633 JS('bool', '#.delete(#)', _map, key);
634 _modified();
635 return value;
636 }
637
638 void clear() {
639 JS('void', '#.clear()', _map);
640 _modified();
641 }
642
643 void forEach(void action(K key, V value)) {
644 var jsEntries = JS('var', '#.entries()', _map);
645 int modifications = _modifications;
646 while (true) {
647 var next = JS('var', '#.next()', jsEntries);
648 bool done = JS('bool', '#.done', next);
649 if (done) break;
650 var entry = JS('var', '#.value', next);
651 var key = JS('var', '#[0]', entry);
652 var value = JS('var', '#[1]', entry);
653 action(key, value);
654 if (modifications != _modifications) {
655 throw new ConcurrentModificationError(this);
656 }
657 }
658 }
659
660 void _modified() {
661 // Value cycles after 2^30 modifications. If you keep hold of an
662 // iterator for that long, you might miss a modification
663 // detection, and iteration can go sour. Don't do that.
664 _modifications = (_modifications + 1) & 0x3ffffff;
665 }
666
667 String toString() => Maps.mapToString(this);
668 }
669
670 class _Es6MapIterable<E> extends Iterable<E>
671 implements EfficientLength {
672 final _map;
673 final bool _isKeys;
674
675 _Es6MapIterable(this._map, this._isKeys);
676
677 int get length => _map.length;
678 bool get isEmpty => _map.isEmpty;
679
680 Iterator<E> get iterator =>
681 new _Es6MapIterator<E>(_map, _map._modifications, _isKeys);
682
683 bool contains(Object element) => _map.containsKey(element);
684
685 void forEach(void f(E element)) {
686 var jsIterator;
687 if (_isKeys) {
688 jsIterator = JS('var', '#.keys()', _map._map);
689 } else {
690 jsIterator = JS('var', '#.values()', _map._map);
691 }
692 int modifications = _map.modifications;
693 while (true) {
694 var next = JS('var', '#.next()', jsIterator);
695 bool done = JS('bool', '#.done', next);
696 if (done) break;
697 var value = JS('var', '#.value', next);
698 f(value);
699 if (modifications != _map.modifications) {
700 throw new ConcurrentModificationError(_map);
701 }
702 }
703 }
704 }
705
706 class _Es6MapIterator<E> implements Iterator<E> {
707 final _map;
708 final int _modifications;
709 final bool _isKeys;
710 var _jsIterator;
711 var _next;
712 E _current;
713
714 _Es6MapIterator(this._map, this._modifications, this._isKeys) {
715 if (_isKeys) {
716 _jsIterator = JS('var', '#.keys()', _map._map);
717 } else {
718 _jsIterator = JS('var', '#.values()', _map._map);
719 }
720 _next = JS('var', '#.next()', _jsIterator);
721 }
722
723 E get current => _current;
724
725 bool moveNext() {
726 if (_modifications != _map._modifications) {
727 throw new ConcurrentModificationError(_map);
728 }
729 bool done = JS('bool', '#.done', _next);
730 if (done) {
731 _current = null;
732 return false;
733 } else {
734 _current = JS('var', '#.value', _next);
735 _next = JS('var', '#.next()', _jsIterator);
736 return true;
737 }
738 }
739 }
740
567 // TODO(floitsch): use ES6 maps when available. 741 // TODO(floitsch): use ES6 maps when available.
568 class _LinkedCustomHashMap<K, V> extends JsLinkedHashMap<K, V> { 742 class _LinkedCustomHashMap<K, V> extends JsLinkedHashMap<K, V> {
569 final _Equality<K> _equals; 743 final _Equality<K> _equals;
570 final _Hasher<K> _hashCode; 744 final _Hasher<K> _hashCode;
571 final _Predicate _validKey; 745 final _Predicate _validKey;
572 746
573 _LinkedCustomHashMap(this._equals, this._hashCode, 747 _LinkedCustomHashMap(this._equals, this._hashCode,
574 bool validKey(potentialKey)) 748 bool validKey(potentialKey))
575 : _validKey = (validKey != null) ? validKey : ((v) => v is K); 749 : _validKey = (validKey != null) ? validKey : ((v) => v is K);
576 750
(...skipping 897 matching lines...) Expand 10 before | Expand all | Expand 10 after
1474 } else if (_cell == null) { 1648 } else if (_cell == null) {
1475 _current = null; 1649 _current = null;
1476 return false; 1650 return false;
1477 } else { 1651 } else {
1478 _current = _cell._element; 1652 _current = _cell._element;
1479 _cell = _cell._next; 1653 _cell = _cell._next;
1480 return true; 1654 return true;
1481 } 1655 }
1482 } 1656 }
1483 } 1657 }
OLDNEW
« no previous file with comments | « no previous file | sdk/lib/_internal/compiler/js_lib/foreign_helper.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698