| // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| // Patch file for dart:collection classes. | |
| import 'dart:_foreign_helper' show JS, JSExportName; | |
| import 'dart:_runtime' as dart; | |
| import 'dart:_js_helper' | |
| show | |
| NoInline, | |
| NoSideEffects, | |
| NoThrows, | |
| patch, | |
| LinkedMap, | |
| IdentityMap, | |
| CustomHashMap, | |
| CustomKeyHashMap, | |
| DartIterator, | |
| notNull, | |
| putLinkedMapKey; | |
| @patch | |
| class HashMap<K, V> { | |
| @patch | |
| factory HashMap( | |
| {bool equals(K key1, K key2), | |
| int hashCode(K key), | |
| bool isValidKey(Object potentialKey)}) { | |
| if (isValidKey == null) { | |
| if (hashCode == null) { | |
| if (equals == null) { | |
| if (identical(K, String) || identical(K, int)) { | |
| return new IdentityMap<K, V>(); | |
| } | |
| return new LinkedMap<K, V>(); | |
| } | |
| hashCode = dart.hashCode; | |
| } else if (identical(identityHashCode, hashCode) && | |
| identical(identical, equals)) { | |
| return new IdentityMap<K, V>(); | |
| } | |
| return new CustomHashMap<K, V>(equals ?? dart.equals, hashCode); | |
| } | |
| return new CustomKeyHashMap<K, V>( | |
| equals ?? dart.equals, hashCode ?? dart.hashCode, isValidKey); | |
| } | |
| @patch | |
| factory HashMap.identity() = IdentityMap<K, V>; | |
| } | |
| @patch | |
| class LinkedHashMap<K, V> { | |
| @patch | |
| factory LinkedHashMap( | |
| {bool equals(K key1, K key2), | |
| int hashCode(K key), | |
| bool isValidKey(Object potentialKey)}) { | |
| if (isValidKey == null) { | |
| if (hashCode == null) { | |
| if (equals == null) { | |
| if (identical(K, String) || identical(K, int)) { | |
| return new IdentityMap<K, V>(); | |
| } | |
| return new LinkedMap<K, V>(); | |
| } | |
| hashCode = dart.hashCode; | |
| } else if (identical(identityHashCode, hashCode) && | |
| identical(identical, equals)) { | |
| return new IdentityMap<K, V>(); | |
| } | |
| return new CustomHashMap<K, V>(equals ?? dart.equals, hashCode); | |
| } | |
| return new CustomKeyHashMap<K, V>( | |
| equals ?? dart.equals, hashCode ?? dart.hashCode, isValidKey); | |
| } | |
| @patch | |
| factory LinkedHashMap.identity() = IdentityMap<K, V>; | |
| } | |
| @patch | |
| class HashSet<E> { | |
| @patch | |
| factory HashSet( | |
| {bool equals(E e1, E e2), | |
| int hashCode(E e), | |
| bool isValidKey(Object potentialKey)}) { | |
| if (isValidKey == null) { | |
| if (hashCode == null) { | |
| if (equals == null) { | |
| if (identical(E, String) || identical(E, int)) { | |
| return new _IdentityHashSet<E>(); | |
| } | |
| return new _HashSet<E>(); | |
| } | |
| hashCode = dart.hashCode; | |
| } else if (identical(identityHashCode, hashCode) && | |
| identical(identical, equals)) { | |
| return new _IdentityHashSet<E>(); | |
| } | |
| return new _CustomHashSet<E>( | |
| equals ?? dart.equals, hashCode ?? dart.hashCode); | |
| } | |
| return new _CustomKeyHashSet<E>( | |
| equals ?? dart.equals, hashCode ?? dart.hashCode, isValidKey); | |
| } | |
| @patch | |
| factory HashSet.identity() = _IdentityHashSet<E>; | |
| } | |
| @patch | |
| class LinkedHashSet<E> { | |
| @patch | |
| factory LinkedHashSet( | |
| {bool equals(E e1, E e2), | |
| int hashCode(E e), | |
| bool isValidKey(Object potentialKey)}) { | |
| if (isValidKey == null) { | |
| if (hashCode == null) { | |
| if (equals == null) { | |
| if (identical(E, String) || identical(E, int)) { | |
| return new _IdentityHashSet<E>(); | |
| } | |
| return new _HashSet<E>(); | |
| } | |
| hashCode = dart.hashCode; | |
| } else if (identical(identityHashCode, hashCode) && | |
| identical(identical, equals)) { | |
| return new _IdentityHashSet<E>(); | |
| } | |
| return new _CustomHashSet<E>( | |
| equals ?? dart.equals, hashCode ?? dart.hashCode); | |
| } | |
| return new _CustomKeyHashSet<E>( | |
| equals ?? dart.equals, hashCode ?? dart.hashCode, isValidKey); | |
| } | |
| @patch | |
| factory LinkedHashSet.identity() = _IdentityHashSet<E>; | |
| } | |
| class _HashSet<E> extends _InternalSet<E> | |
| implements HashSet<E>, LinkedHashSet<E> { | |
| /// The backing store for this set. | |
| /// | |
| /// Keys that use identity equality are stored directly. For other types of | |
| /// keys, we first look them up (by hashCode) in the [_keyMap] map, then | |
| /// we lookup the key in this map. | |
| @notNull | |
| final _map = JS('', 'new Set()'); | |
| /// Items that use custom equality semantics. | |
| /// | |
| /// This maps from the item's hashCode to the canonical key, which is then | |
| /// used to lookup the item in [_map]. Keeping the data in our primary backing | |
| /// map gives us the ordering semantics requred by [LinkedHashMap], while | |
| /// also providing convenient access to keys/values. | |
| @notNull | |
| final _keyMap = JS('', 'new Map()'); | |
| // We track the number of modifications done to the key set of the | |
| // hash map to be able to throw when the map is modified while being | |
| // iterated over. | |
| // | |
| // Value cycles after 2^30 modifications so that modification counts are | |
| // always unboxed (Smi) values. Modification detection will be missed if you | |
| // make exactly some multiple of 2^30 modifications between advances of an | |
| // iterator. | |
| @notNull | |
| int _modifications = 0; | |
| _HashSet(); | |
| Set<E> _newSet() => new _HashSet<E>(); | |
| Set<R> _newSimilarSet<R>() => new _HashSet<R>(); | |
| bool contains(Object key) { | |
| if (key == null) { | |
| key = null; | |
| } else if (JS('bool', '#[#] !== #', key, dart.extensionSymbol('_equals'), | |
| dart.identityEquals)) { | |
| @notNull | |
| var k = key; | |
| var buckets = JS('', '#.get(# & 0x3ffffff)', _keyMap, k.hashCode); | |
| if (buckets != null) { | |
| for (int i = 0, n = JS('!', '#.length', buckets); i < n; i++) { | |
| k = JS('', '#[#]', buckets, i); | |
| if (k == key) return true; | |
| } | |
| } | |
| return false; | |
| } | |
| return JS('bool', '#.has(#)', _map, key); | |
| } | |
| E lookup(Object key) { | |
| if (key == null) return null; | |
| if (JS('bool', '#[#] !== #', key, dart.extensionSymbol('_equals'), | |
| dart.identityEquals)) { | |
| @notNull | |
| var k = key; | |
| var buckets = JS('', '#.get(# & 0x3ffffff)', _keyMap, k.hashCode); | |
| if (buckets != null) { | |
| for (int i = 0, n = JS('!', '#.length', buckets); i < n; i++) { | |
| k = JS('', '#[#]', buckets, i); | |
| if (k == key) return JS('', '#', k); | |
| } | |
| } | |
| return null; | |
| } | |
| return JS('', '#.has(#) ? # : null', _map, key, key); | |
| } | |
| bool add(E key) { | |
| var map = _map; | |
| if (key == null) { | |
| if (JS('', '#.has(null)', map)) return false; | |
| key = null; | |
| } else if (JS('bool', '#[#] !== #', key, dart.extensionSymbol('_equals'), | |
| dart.identityEquals)) { | |
| var keyMap = _keyMap; | |
| @notNull | |
| var k = key; | |
| int hash = JS('!', '# & 0x3ffffff', k.hashCode); | |
| var buckets = JS('', '#.get(#)', keyMap, hash); | |
| if (buckets == null) { | |
| JS('', '#.set(#, [#])', keyMap, hash, key); | |
| } else { | |
| for (int i = 0, n = JS('!', '#.length', buckets); i < n; i++) { | |
| k = JS('', '#[#]', buckets, i); | |
| if (k == key) return false; | |
| } | |
| JS('', '#.push(#)', buckets, key); | |
| } | |
| } else if (JS('', '#.has(#)', map, key)) { | |
| return false; | |
| } | |
| JS('', '#.add(#)', map, key); | |
| _modifications = (_modifications + 1) & 0x3ffffff; | |
| return true; | |
| } | |
| void addAll(Iterable<E> objects) { | |
| var map = _map; | |
| int length = JS('', '#.size', map); | |
| for (E key in objects) { | |
| if (key == null) { | |
| key = null; | |
| } else if (JS('bool', '#[#] !== #', key, dart.extensionSymbol('_equals'), | |
| dart.identityEquals)) { | |
| key = putLinkedMapKey(key, _keyMap); | |
| } | |
| JS('', '#.add(#)', map, key); | |
| } | |
| if (length != JS<int>('!', '#.size', map)) { | |
| _modifications = (_modifications + 1) & 0x3ffffff; | |
| } | |
| } | |
| bool remove(Object key) { | |
| if (key == null) { | |
| key = null; | |
| } else if (JS('bool', '#[#] !== #', key, dart.extensionSymbol('_equals'), | |
| dart.identityEquals)) { | |
| @notNull | |
| var k = key; | |
| int hash = JS('!', '# & 0x3ffffff', k.hashCode); | |
| var buckets = JS('', '#.get(#)', _keyMap, hash); | |
| if (buckets == null) return false; // not found | |
| for (int i = 0, n = JS('!', '#.length', buckets);;) { | |
| k = JS('', '#[#]', buckets, i); | |
| if (k == key) { | |
| key = k; | |
| if (n == 1) { | |
| JS('', '#.delete(#)', _keyMap, hash); | |
| } else { | |
| JS('', '#.splice(#, 1)', buckets, i); | |
| } | |
| break; | |
| } | |
| if (++i >= n) return false; // not found | |
| } | |
| } | |
| var map = _map; | |
| if (JS('bool', '#.delete(#)', map, key)) { | |
| _modifications = (_modifications + 1) & 0x3ffffff; | |
| return true; | |
| } | |
| return false; | |
| } | |
| void clear() { | |
| var map = _map; | |
| if (JS<int>('!', '#.size', map) > 0) { | |
| JS('', '#.clear()', map); | |
| JS('', '#.clear()', _keyMap); | |
| _modifications = (_modifications + 1) & 0x3ffffff; | |
| } | |
| } | |
| } | |
| class _IdentityHashSet<E> extends _InternalSet<E> | |
| implements HashSet<E>, LinkedHashSet<E> { | |
| /// The backing store for this set. | |
| @notNull | |
| final _map = JS('', 'new Set()'); | |
| @notNull | |
| int _modifications = 0; | |
| _IdentityHashSet(); | |
| Set<E> _newSet() => new _IdentityHashSet<E>(); | |
| Set<R> _newSimilarSet<R>() => new _IdentityHashSet<R>(); | |
| bool contains(Object element) { | |
| return JS('', '#.has(#)', _map, element); | |
| } | |
| E lookup(Object element) { | |
| return JS('', '#.has(#)', _map, element) ? element : null; | |
| } | |
| bool add(E element) { | |
| var map = _map; | |
| if (JS('bool', '#.has(#)', map, element)) return false; | |
| JS('', '#.add(#)', map, element); | |
| _modifications = (_modifications + 1) & 0x3ffffff; | |
| return true; | |
| } | |
| void addAll(Iterable<E> objects) { | |
| var map = _map; | |
| int length = JS('', '#.size', map); | |
| for (E key in objects) { | |
| JS('', '#.add(#)', map, key); | |
| } | |
| if (length != JS<int>('!', '#.size', map)) { | |
| _modifications = (_modifications + 1) & 0x3ffffff; | |
| } | |
| } | |
| bool remove(Object element) { | |
| if (JS('bool', '#.delete(#)', _map, element)) { | |
| _modifications = (_modifications + 1) & 0x3ffffff; | |
| return true; | |
| } | |
| return false; | |
| } | |
| void clear() { | |
| var map = _map; | |
| if (JS<int>('!', '#.size', map) > 0) { | |
| JS('', '#.clear()', map); | |
| _modifications = (_modifications + 1) & 0x3ffffff; | |
| } | |
| } | |
| } | |
| class _CustomKeyHashSet<E> extends _CustomHashSet<E> { | |
| _Predicate<Object> _validKey; | |
| _CustomKeyHashSet(_Equality<E> equals, _Hasher<E> hashCode, this._validKey) | |
| : super(equals, hashCode); | |
| Set<E> _newSet() => new _CustomKeyHashSet<E>(_equals, _hashCode, _validKey); | |
| Set<R> _newSimilarSet<R>() => new _HashSet<R>(); | |
| bool contains(Object element) { | |
| // TODO(jmesserly): there is a subtle difference here compared to Dart 1. | |
| // See the comment on CustomKeyHashMap.containsKey for more information. | |
| // Treatment of `null` is different due to strong mode's requirement to | |
| // perform an `element is E` check before calling equals/hashCode. | |
| if (!_validKey(element)) return false; | |
| return super.contains(element); | |
| } | |
| E lookup(Object element) { | |
| if (!_validKey(element)) return null; | |
| return super.lookup(element); | |
| } | |
| bool remove(Object element) { | |
| if (!_validKey(element)) return false; | |
| return super.remove(element); | |
| } | |
| } | |
| class _CustomHashSet<E> extends _InternalSet<E> | |
| implements HashSet<E>, LinkedHashSet<E> { | |
| _Equality<E> _equals; | |
| _Hasher<E> _hashCode; | |
| // We track the number of modifications done to the key set of the | |
| // hash map to be able to throw when the map is modified while being | |
| // iterated over. | |
| // | |
| // Value cycles after 2^30 modifications so that modification counts are | |
| // always unboxed (Smi) values. Modification detection will be missed if you | |
| // make exactly some multiple of 2^30 modifications between advances of an | |
| // iterator. | |
| @notNull | |
| int _modifications = 0; | |
| /// The backing store for this set, used to handle ordering. | |
| // TODO(jmesserly): a non-linked custom hash set could skip this. | |
| @notNull | |
| final _map = JS('', 'new Set()'); | |
| /// Our map used to map keys onto the canonical key that is stored in [_map]. | |
| @notNull | |
| final _keyMap = JS('', 'new Map()'); | |
| _CustomHashSet(this._equals, this._hashCode); | |
| Set<E> _newSet() => new _CustomHashSet<E>(_equals, _hashCode); | |
| Set<R> _newSimilarSet<R>() => new _HashSet<R>(); | |
| bool contains(Object key) { | |
| if (key is E) { | |
| var buckets = JS('', '#.get(# & 0x3ffffff)', _keyMap, _hashCode(key)); | |
| if (buckets != null) { | |
| var equals = _equals; | |
| for (int i = 0, n = JS('!', '#.length', buckets); i < n; i++) { | |
| E k = JS('', '#[#]', buckets, i); | |
| if (equals(k, key)) return true; | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| E lookup(Object key) { | |
| if (key is E) { | |
| var buckets = JS('', '#.get(# & 0x3ffffff)', _keyMap, _hashCode(key)); | |
| if (buckets != null) { | |
| var equals = _equals; | |
| for (int i = 0, n = JS('!', '#.length', buckets); i < n; i++) { | |
| E k = JS('', '#[#]', buckets, i); | |
| if (equals(k, key)) return JS('', '#', k); | |
| } | |
| } | |
| } | |
| return null; | |
| } | |
| bool add(E key) { | |
| var keyMap = _keyMap; | |
| var hash = JS<int>('!', '# & 0x3ffffff', _hashCode(key)); | |
| var buckets = JS('', '#.get(#)', keyMap, hash); | |
| if (buckets == null) { | |
| JS('', '#.set(#, [#])', keyMap, hash, key); | |
| } else { | |
| var equals = _equals; | |
| for (int i = 0, n = JS('!', '#.length', buckets); i < n; i++) { | |
| E k = JS('', '#[#]', buckets, i); | |
| if (equals(k, key)) return false; | |
| } | |
| JS('', '#.push(#)', buckets, key); | |
| } | |
| JS('', '#.add(#)', _map, key); | |
| _modifications = (_modifications + 1) & 0x3ffffff; | |
| return true; | |
| } | |
| void addAll(Iterable<E> objects) { | |
| // TODO(jmesserly): it'd be nice to skip the covariance check here. | |
| for (E element in objects) add(element); | |
| } | |
| bool remove(Object key) { | |
| if (key is E) { | |
| var hash = JS<int>('!', '# & 0x3ffffff', _hashCode(key)); | |
| var keyMap = _keyMap; | |
| var buckets = JS('', '#.get(#)', keyMap, hash); | |
| if (buckets == null) return false; // not found | |
| var equals = _equals; | |
| for (int i = 0, n = JS('!', '#.length', buckets); i < n; i++) { | |
| E k = JS('', '#[#]', buckets, i); | |
| if (equals(k, key)) { | |
| if (n == 1) { | |
| JS('', '#.delete(#)', keyMap, hash); | |
| } else { | |
| JS('', '#.splice(#, 1)', buckets, i); | |
| } | |
| JS('', '#.delete(#)', _map, k); | |
| _modifications = (_modifications + 1) & 0x3ffffff; | |
| return true; | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| void clear() { | |
| var map = _map; | |
| if (JS<int>('!', '#.size', map) > 0) { | |
| JS('', '#.clear()', map); | |
| JS('', '#.clear()', _keyMap); | |
| _modifications = (_modifications + 1) & 0x3ffffff; | |
| } | |
| } | |
| } | |
| /// Base class for our internal [LinkedHashSet]/[HashSet] implementations. | |
| /// | |
| /// This implements the common functionality. | |
| abstract class _InternalSet<E> extends _HashSetBase<E> { | |
| @notNull | |
| get _map; | |
| @notNull | |
| int get _modifications; | |
| @notNull | |
| int get length => JS<int>('!', '#.size', _map); | |
| @notNull | |
| bool get isEmpty => JS('bool', '#.size == 0', _map); | |
| @notNull | |
| bool get isNotEmpty => JS('bool', '#.size != 0', _map); | |
| Iterator<E> get iterator => new DartIterator<E>(_jsIterator()); | |
| @JSExportName('Symbol.iterator') | |
| _jsIterator() { | |
| var self = this; | |
| var iterator = JS('', '#.values()', self._map); | |
| int modifications = self._modifications; | |
| return JS( | |
| '', | |
| '''{ | |
| next() { | |
| if (# != #) { | |
| throw #; | |
| } | |
| return #.next(); | |
| } | |
| }''', | |
| modifications, | |
| self._modifications, | |
| new ConcurrentModificationError(self), | |
| iterator); | |
| } | |
| } |