Chromium Code Reviews| Index: sdk/lib/_internal/lib/collection_patch.dart |
| diff --git a/sdk/lib/_internal/lib/collection_patch.dart b/sdk/lib/_internal/lib/collection_patch.dart |
| index 670b4ab51d4c09cd4923f2fe11b5896ce182bfb0..3ae7dab778f86a796d70baea07aebe73c46f9bdc 100644 |
| --- a/sdk/lib/_internal/lib/collection_patch.dart |
| +++ b/sdk/lib/_internal/lib/collection_patch.dart |
| @@ -883,6 +883,30 @@ class LinkedHashMapKeyIterator<E> implements Iterator<E> { |
| } |
| patch class HashSet<E> { |
| + patch factory HashSet({ bool equals(E e1, E e2), |
| + int hashCode(E e), |
| + bool isValidKey(potentialKey) }) { |
| + if (isValidKey == null) { |
| + if (hashCode == null) { |
| + if (equals == null) { |
| + return new _HashSet<E>(); |
| + } |
| + if (identical(identical, equals)) { |
| + return new _IdentityHashSet<E>(); |
| + } |
| + hashCode = _defaultHashCode; |
| + } else if (equals == null) { |
| + equals = _defaultEquals; |
| + } |
| + } else { |
| + if (hashCode == null) hashCode = _defaultHashCode; |
| + if (equals == null) equals = _defaultEquals; |
| + } |
| + return new _CustomHashSet<E>(equals, hashCode, isValidKey); |
| + } |
| +} |
| + |
| +class _HashSet<E> extends _HashSetBase<E> implements HashSet<E> { |
| int _length = 0; |
| // The hash set contents are divided into three parts: one part for |
| @@ -904,18 +928,20 @@ patch class HashSet<E> { |
| // guard against concurrent modifications. |
| List _elements; |
| - patch HashSet(); |
| + _HashSet(); |
| + |
| + Set<E> _newSet() => new _HashSet<E>(); |
| // Iterable. |
| - patch Iterator<E> get iterator { |
| + Iterator<E> get iterator { |
| return new HashSetIterator<E>(this, _computeElements()); |
| } |
| - patch int get length => _length; |
| - patch bool get isEmpty => _length == 0; |
| - patch bool get isNotEmpty => !isEmpty; |
| + int get length => _length; |
| + bool get isEmpty => _length == 0; |
| + bool get isNotEmpty => !isEmpty; |
| - patch bool contains(Object object) { |
| + bool contains(Object object) { |
| if (_isStringElement(object)) { |
| var strings = _strings; |
| return (strings == null) ? false : _hasTableEntry(strings, object); |
| @@ -931,7 +957,7 @@ patch class HashSet<E> { |
| } |
| // Collection. |
| - patch void add(E element) { |
| + void add(E element) { |
| if (_isStringElement(element)) { |
| var strings = _strings; |
| if (strings == null) _strings = strings = _newHashTable(); |
| @@ -957,13 +983,13 @@ patch class HashSet<E> { |
| } |
| } |
| - patch void addAll(Iterable<E> objects) { |
| + void addAll(Iterable<E> objects) { |
| for (E each in objects) { |
| add(each); |
| } |
| } |
| - patch bool remove(Object object) { |
| + bool remove(Object object) { |
| if (_isStringElement(object)) { |
| return _removeHashTableEntry(_strings, object); |
| } else if (_isNumericElement(object)) { |
| @@ -985,21 +1011,21 @@ patch class HashSet<E> { |
| } |
| } |
| - patch void removeAll(Iterable<Object> objectsToRemove) { |
| + void removeAll(Iterable<Object> objectsToRemove) { |
| for (var each in objectsToRemove) { |
| remove(each); |
| } |
| } |
| - patch void removeWhere(bool test(E element)) { |
| + void removeWhere(bool test(E element)) { |
| removeAll(_computeElements().where(test)); |
| } |
| - patch void retainWhere(bool test(E element)) { |
| + void retainWhere(bool test(E element)) { |
| removeAll(_computeElements().where((E element) => !test(element))); |
| } |
| - patch void clear() { |
| + void clear() { |
| if (_length > 0) { |
| _strings = _nums = _rest = _elements = null; |
| _length = 0; |
| @@ -1086,7 +1112,7 @@ patch class HashSet<E> { |
| JS('bool', '(# & 0x3ffffff) === #', element, element); |
| } |
| - static int _computeHashCode(var element) { |
| + int _computeHashCode(var element) { |
| // We force the hash codes to be unsigned 30-bit integers to avoid |
| // issues with problematic elements like '__proto__'. Another |
| // option would be to throw an exception if the hash code isn't a |
| @@ -1111,12 +1137,12 @@ patch class HashSet<E> { |
| JS('void', 'delete #[#]', table, key); |
| } |
| - static List _getBucket(var table, var element) { |
| + List _getBucket(var table, var element) { |
| var hash = _computeHashCode(element); |
| return JS('var', '#[#]', table, hash); |
| } |
| - static int _findBucketIndex(var bucket, var element) { |
| + int _findBucketIndex(var bucket, var element) { |
| if (bucket == null) return -1; |
| int length = JS('int', '#.length', bucket); |
| for (int i = 0; i < length; i++) { |
| @@ -1139,6 +1165,56 @@ patch class HashSet<E> { |
| } |
| } |
| +class _IdentityHashSet<E> extends _HashSet<E> { |
| + Set<E> _newSet() => new _IdentityHashSet<E>(); |
| + |
| + int _findBucketIndex(var bucket, var element) { |
| + if (bucket == null) return -1; |
| + int length = JS('int', '#.length', bucket); |
| + for (int i = 0; i < length; i++) { |
| + if (identical(JS('var', '#[#]', bucket, i), element)) return i; |
| + } |
| + return -1; |
| + } |
| +} |
| + |
| +class _CustomHashSet<E> extends _HashSet<E> { |
|
floitsch
2013/09/11 11:23:17
same as for runtime-version: needs to check for co
Lasse Reichstein Nielsen
2013/09/11 12:25:43
It needs to check for concurrent modification when
|
| + _Equality<E> _equality; |
| + _Hasher<E> _hasher; |
| + _Predicate _validKey; |
| + _CustomHashSet(this._equality, this._hasher, bool validKey(potentialKey)) |
| + : _validKey = (validKey != null) ? validKey : ((x) => x is E); |
| + |
| + Set<E> _newSet() => new _CustomHashSet<E>(_equality, _hasher, _validKey); |
| + |
| + int _findBucketIndex(var bucket, var element) { |
| + if (bucket == null) return -1; |
| + int length = JS('int', '#.length', bucket); |
| + for (int i = 0; i < length; i++) { |
| + if (_equality(JS('var', '#[#]', bucket, i), element)) return i; |
| + } |
| + return -1; |
| + } |
| + |
| + int _computeHashCode(var element) { |
| + // We force the hash codes to be unsigned 30-bit integers to avoid |
| + // issues with problematic elements like '__proto__'. Another |
| + // option would be to throw an exception if the hash code isn't a |
| + // number. |
| + return JS('int', '# & 0x3ffffff', _hasher(element)); |
| + } |
| + |
| + bool contains(Object object) { |
| + if (!_validKey(object)) return false; |
| + return super.contains(object); |
| + } |
| + |
| + bool remove(Object object) { |
| + if (!_validKey(object)) return false; |
| + return super.remove(object); |
| + } |
| +} |
| + |
| // TODO(kasperl): Share this code with HashMapKeyIterator<E>? |
| class HashSetIterator<E> implements Iterator<E> { |
| final _set; |
| @@ -1169,7 +1245,31 @@ class HashSetIterator<E> implements Iterator<E> { |
| } |
| } |
| -patch class LinkedHashSet<E> extends _HashSetBase<E> { |
| +patch class LinkedHashSet<E> { |
| + patch factory LinkedHashSet({ bool equals(E e1, E e2), |
| + int hashCode(E e), |
| + bool isValidKey(potentialKey) }) { |
| + if (isValidKey == null) { |
| + if (hashCode == null) { |
| + if (equals == null) { |
| + return new _LinkedHashSet<E>(); |
| + } |
| + if (identical(identical, equals)) { |
| + return new _LinkedIdentityHashSet<E>(); |
| + } |
| + hashCode = _defaultHashCode; |
| + } else if (equals == null) { |
| + equals = _defaultEquals; |
| + } |
| + } else { |
| + if (hashCode == null) hashCode = _defaultHashCode; |
| + if (equals == null) equals = _defaultEquals; |
| + } |
| + return new _LinkedCustomHashSet<E>(equals, hashCode, isValidKey); |
| + } |
| +} |
| + |
| +class _LinkedHashSet<E> extends _HashSetBase<E> implements LinkedHashSet<E> { |
| int _length = 0; |
| // The hash set contents are divided into three parts: one part for |
| @@ -1195,22 +1295,24 @@ patch class LinkedHashSet<E> extends _HashSetBase<E> { |
| // over. |
| int _modifications = 0; |
| - patch LinkedHashSet(); |
| + _LinkedHashSet(); |
| + |
| + Set<E> _newSet() => new _LinkedHashSet<E>(); |
| void _unsupported(String operation) { |
| throw 'LinkedHashSet: unsupported $operation'; |
| } |
| // Iterable. |
| - patch Iterator<E> get iterator { |
| + Iterator<E> get iterator { |
| return new LinkedHashSetIterator(this, _modifications); |
| } |
| - patch int get length => _length; |
| - patch bool get isEmpty => _length == 0; |
| - patch bool get isNotEmpty => !isEmpty; |
| + int get length => _length; |
| + bool get isEmpty => _length == 0; |
| + bool get isNotEmpty => !isEmpty; |
| - patch bool contains(Object object) { |
| + bool contains(Object object) { |
| if (_isStringElement(object)) { |
| var strings = _strings; |
| if (strings == null) return false; |
| @@ -1229,7 +1331,7 @@ patch class LinkedHashSet<E> extends _HashSetBase<E> { |
| } |
| } |
| - patch void forEach(void action(E element)) { |
| + void forEach(void action(E element)) { |
| LinkedHashSetCell cell = _first; |
| int modifications = _modifications; |
| while (cell != null) { |
| @@ -1241,18 +1343,18 @@ patch class LinkedHashSet<E> extends _HashSetBase<E> { |
| } |
| } |
| - patch E get first { |
| + E get first { |
| if (_first == null) throw new StateError("No elements"); |
| return _first._element; |
| } |
| - patch E get last { |
| + E get last { |
| if (_last == null) throw new StateError("No elements"); |
| return _last._element; |
| } |
| // Collection. |
| - patch void add(E element) { |
| + void add(E element) { |
| if (_isStringElement(element)) { |
| var strings = _strings; |
| if (strings == null) _strings = strings = _newHashTable(); |
| @@ -1278,13 +1380,13 @@ patch class LinkedHashSet<E> extends _HashSetBase<E> { |
| } |
| } |
| - patch void addAll(Iterable<E> objects) { |
| + void addAll(Iterable<E> objects) { |
| for (E object in objects) { |
| add(object); |
| } |
| } |
| - patch bool remove(Object object) { |
| + bool remove(Object object) { |
| if (_isStringElement(object)) { |
| return _removeHashTableEntry(_strings, object); |
| } else if (_isNumericElement(object)) { |
| @@ -1303,17 +1405,17 @@ patch class LinkedHashSet<E> extends _HashSetBase<E> { |
| } |
| } |
| - patch void removeAll(Iterable objectsToRemove) { |
| + void removeAll(Iterable objectsToRemove) { |
| for (var each in objectsToRemove) { |
| remove(each); |
| } |
| } |
| - patch void removeWhere(bool test(E element)) { |
| + void removeWhere(bool test(E element)) { |
| _filterWhere(test, true); |
| } |
| - patch void retainWhere(bool test(E element)) { |
| + void retainWhere(bool test(E element)) { |
| _filterWhere(test, false); |
| } |
| @@ -1332,7 +1434,7 @@ patch class LinkedHashSet<E> extends _HashSetBase<E> { |
| } |
| } |
| - patch void clear() { |
| + void clear() { |
| if (_length > 0) { |
| _strings = _nums = _rest = _first = _last = null; |
| _length = 0; |
| @@ -1409,7 +1511,7 @@ patch class LinkedHashSet<E> extends _HashSetBase<E> { |
| JS('bool', '(# & 0x3ffffff) === #', element, element); |
| } |
| - static int _computeHashCode(var element) { |
| + int _computeHashCode(var element) { |
| // We force the hash codes to be unsigned 30-bit integers to avoid |
| // issues with problematic elements like '__proto__'. Another |
| // option would be to throw an exception if the hash code isn't a |
| @@ -1430,12 +1532,12 @@ patch class LinkedHashSet<E> extends _HashSetBase<E> { |
| JS('void', 'delete #[#]', table, key); |
| } |
| - static List _getBucket(var table, var element) { |
| + List _getBucket(var table, var element) { |
| var hash = _computeHashCode(element); |
| return JS('var', '#[#]', table, hash); |
| } |
| - static int _findBucketIndex(var bucket, var element) { |
| + int _findBucketIndex(var bucket, var element) { |
| if (bucket == null) return -1; |
| int length = JS('int', '#.length', bucket); |
| for (int i = 0; i < length; i++) { |
| @@ -1459,6 +1561,60 @@ patch class LinkedHashSet<E> extends _HashSetBase<E> { |
| } |
| } |
| +class _LinkedIdentityHashSet<E> extends _LinkedHashSet<E> { |
| + Set<E> _newSet() => new _LinkedIdentityHashSet<E>(); |
| + |
| + int _findBucketIndex(var bucket, var element) { |
| + if (bucket == null) return -1; |
| + int length = JS('int', '#.length', bucket); |
| + for (int i = 0; i < length; i++) { |
| + LinkedHashSetCell cell = JS('var', '#[#]', bucket, i); |
| + if (identical(cell._element, element)) return i; |
| + } |
| + return -1; |
| + } |
| +} |
| + |
| +class _LinkedCustomHashSet<E> extends _LinkedHashSet<E> { |
| + _Equality<E> _equality; |
| + _Hasher<E> _hasher; |
| + _Predicate _validKey; |
| + _LinkedCustomHashSet(this._equality, this._hasher, |
| + bool validKey(potentialKey)) |
| + : _validKey = (validKey != null) ? validKey : ((x) => x is E); |
| + |
| + Set<E> _newSet() => |
| + new _LinkedCustomHashSet<E>(_equality, _hasher, _validKey); |
| + |
| + int _findBucketIndex(var bucket, var element) { |
| + if (bucket == null) return -1; |
| + int length = JS('int', '#.length', bucket); |
| + for (int i = 0; i < length; i++) { |
| + LinkedHashSetCell cell = JS('var', '#[#]', bucket, i); |
| + if (_equality(cell._element, element)) return i; |
| + } |
| + return -1; |
| + } |
| + |
| + int _computeHashCode(var element) { |
| + // We force the hash codes to be unsigned 30-bit integers to avoid |
| + // issues with problematic elements like '__proto__'. Another |
| + // option would be to throw an exception if the hash code isn't a |
| + // number. |
| + return JS('int', '# & 0x3ffffff', _hasher(element)); |
| + } |
| + |
| + bool contains(Object object) { |
| + if (!_validKey(object)) return false; |
| + return super.contains(object); |
| + } |
| + |
| + bool remove(Object object) { |
| + if (!_validKey(object)) return false; |
| + return super.remove(object); |
| + } |
| +} |
| + |
| class LinkedHashSetCell { |
| final _element; |