Index: runtime/lib/collection_patch.dart |
diff --git a/runtime/lib/collection_patch.dart b/runtime/lib/collection_patch.dart |
index 3b3bcc089f41ee5237c695fff916420420072575..ee4e86ee391a4fa748f1be4e6e7112d91f1b3fa6 100644 |
--- a/runtime/lib/collection_patch.dart |
+++ b/runtime/lib/collection_patch.dart |
@@ -4,25 +4,35 @@ |
patch class HashMap<K, V> { |
/* patch */ factory HashMap({ bool equals(K key1, K key2), |
- int hashCode(K key) }) { |
- if (hashCode == null) { |
- if (equals == null) { |
- return new _HashMapImpl<K, V>(); |
+ int hashCode(K key), |
+ bool isValidKey(potentialKey) }) { |
+ if (isValidKey == null) { |
+ if (hashCode == null) { |
+ if (equals == null) { |
+ return new _HashMap<K, V>(); |
+ } |
+ if (identical(identical, equals)) { |
+ return new _IdentityHashMap<K, V>(); |
+ } |
+ hashCode = _defaultHashCode; |
+ } else if (equals == null) { |
+ equals = _defaultEquals; |
} |
- if (identical(identical, equals)) { |
- return new _IdentityHashMap<K, V>(); |
+ } else { |
+ if (hashCode == null) { |
+ hashCode = _defaultHashCode; |
+ } |
+ if (equals == null) { |
+ equals = _defaultEquals; |
} |
- hashCode = _defaultHashCode; |
- } else if (equals == null) { |
- equals = _defaultEquals; |
} |
- return new _CustomHashMap<K, V>(equals, hashCode); |
+ return new _CustomHashMap<K, V>(equals, hashCode, isValidKey); |
} |
} |
const int _MODIFICATION_COUNT_MASK = 0x3fffffff; |
-class _HashMapImpl<K, V> implements HashMap<K, V> { |
+class _HashMap<K, V> implements HashMap<K, V> { |
static const int _INITIAL_CAPACITY = 8; |
int _elementCount = 0; |
@@ -144,11 +154,7 @@ class _HashMapImpl<K, V> implements HashMap<K, V> { |
while (entry != null) { |
_HashMapEntry next = entry.next; |
if (hashCode == entry.hashCode && entry.key == key) { |
- if (previous == null) { |
- buckets[index] = next; |
- } else { |
- previous.next = next; |
- } |
+ _removeEntry(entry, previous, index); |
_elementCount--; |
_modificationCount = |
(_modificationCount + 1) & _MODIFICATION_COUNT_MASK; |
@@ -166,6 +172,16 @@ class _HashMapImpl<K, V> implements HashMap<K, V> { |
_modificationCount = (_modificationCount + 1) & _MODIFICATION_COUNT_MASK; |
} |
+ void _removeEntry(_HashMapEntry entry, |
+ _HashMapEntry previousInBucket, |
+ int bucketIndex) { |
+ if (previousInBucket == null) { |
+ _buckets[bucketIndex] = entry.next; |
+ } else { |
+ previousInBucket.next = entry.next; |
+ } |
+ } |
+ |
void _addEntry(List buckets, int index, int length, |
K key, V value, int hashCode) { |
_HashMapEntry entry = |
@@ -201,12 +217,15 @@ class _HashMapImpl<K, V> implements HashMap<K, V> { |
String toString() => Maps.mapToString(this); |
} |
-class _CustomHashMap<K, V> extends _HashMapImpl<K, V> { |
+class _CustomHashMap<K, V> extends _HashMap<K, V> { |
final _Equality<K> _equals; |
final _Hasher<K> _hashCode; |
- _CustomHashMap(this._equals, this._hashCode); |
+ final _Predicate _validKey; |
+ _CustomHashMap(this._equals, this._hashCode, validKey) |
+ : _validKey = (validKey != null) ? validKey : new _TypeTest<K>().test; |
bool containsKey(Object key) { |
+ if (!_validKey(key)) return false; |
int hashCode = _hashCode(key); |
List buckets = _buckets; |
int index = hashCode & (buckets.length - 1); |
@@ -219,6 +238,7 @@ class _CustomHashMap<K, V> extends _HashMapImpl<K, V> { |
} |
V operator[](Object key) { |
+ if (!_validKey(key)) return null; |
int hashCode = _hashCode(key); |
List buckets = _buckets; |
int index = hashCode & (buckets.length - 1); |
@@ -271,6 +291,7 @@ class _CustomHashMap<K, V> extends _HashMapImpl<K, V> { |
} |
V remove(Object key) { |
+ if (!_validKey(key)) return null; |
int hashCode = _hashCode(key); |
List buckets = _buckets; |
int index = hashCode & (buckets.length - 1); |
@@ -279,11 +300,7 @@ class _CustomHashMap<K, V> extends _HashMapImpl<K, V> { |
while (entry != null) { |
_HashMapEntry next = entry.next; |
if (hashCode == entry.hashCode && _equals(entry.key, key)) { |
- if (previous == null) { |
- buckets[index] = next; |
- } else { |
- previous.next = next; |
- } |
+ _removeEntry(entry, previous, index); |
_elementCount--; |
_modificationCount = |
(_modificationCount + 1) & _MODIFICATION_COUNT_MASK; |
@@ -298,7 +315,7 @@ class _CustomHashMap<K, V> extends _HashMapImpl<K, V> { |
String toString() => Maps.mapToString(this); |
} |
-class _IdentityHashMap<K, V> extends _HashMapImpl<K, V> { |
+class _IdentityHashMap<K, V> extends _HashMap<K, V> { |
bool containsKey(Object key) { |
int hashCode = key.hashCode; |
List buckets = _buckets; |
@@ -372,11 +389,7 @@ class _IdentityHashMap<K, V> extends _HashMapImpl<K, V> { |
while (entry != null) { |
_HashMapEntry next = entry.next; |
if (hashCode == entry.hashCode && identical(entry.key, key)) { |
- if (previous == null) { |
- buckets[index] = next; |
- } else { |
- previous.next = next; |
- } |
+ _removeEntry(entry, previous, index); |
_elementCount--; |
_modificationCount = |
(_modificationCount + 1) & _MODIFICATION_COUNT_MASK; |
@@ -596,11 +609,11 @@ class _LinkedHashMapValueIterable<V> extends IterableBase<V> { |
} |
abstract class _LinkedHashMapIterator<T> implements Iterator<T> { |
- final _LinkedHashMap _map; |
+ final LinkedHashMap _map; |
var _next; |
T _current; |
int _modificationCount; |
- _LinkedHashMapIterator(_LinkedHashMap map) |
+ _LinkedHashMapIterator(LinkedHashMap map) |
: _map = map, |
_current = null, |
_next = map._nextEntry, |
@@ -626,12 +639,12 @@ abstract class _LinkedHashMapIterator<T> implements Iterator<T> { |
} |
class _LinkedHashMapKeyIterator<K> extends _LinkedHashMapIterator<K> { |
- _LinkedHashMapKeyIterator(_LinkedHashMap map) : super(map); |
+ _LinkedHashMapKeyIterator(LinkedHashMap map) : super(map); |
K _getValue(_LinkedHashMapEntry entry) => entry.key; |
} |
class _LinkedHashMapValueIterator<V> extends _LinkedHashMapIterator<V> { |
- _LinkedHashMapValueIterator(_LinkedHashMap map) : super(map); |
+ _LinkedHashMapValueIterator(LinkedHashMap map) : super(map); |
V _getValue(_LinkedHashMapEntry entry) => entry.value; |
} |
@@ -640,153 +653,72 @@ class _LinkedHashMapValueIterator<V> extends _LinkedHashMapIterator<V> { |
* A hash-based map that iterates keys and values in key insertion order. |
*/ |
patch class LinkedHashMap<K, V> { |
- static const int _INITIAL_CAPACITY = 8; |
- static const int _MODIFICATION_COUNT_MASK = 0x3fffffff; |
- |
- int _elementCount = 0; |
- List<_HashMapEntry> _buckets = new List(_INITIAL_CAPACITY); |
- int _modificationCount = 0; |
- |
var _nextEntry; |
var _previousEntry; |
- /* patch */ LinkedHashMap() { |
- _nextEntry = _previousEntry = this; |
- } |
- |
- /* patch */ int get length => _elementCount; |
- /* patch */ bool get isEmpty => _elementCount == 0; |
- /* patch */ bool get isNotEmpty => _elementCount != 0; |
- |
- /* patch */ Iterable<K> get keys => new _LinkedHashMapKeyIterable<K>(this); |
- /* patch */ Iterable<V> get values => new _LinkedHashMapValueIterable<V>(this); |
- |
- /* patch */ bool containsKey(Object key) { |
- int hashCode = key.hashCode; |
- List buckets = _buckets; |
- int index = hashCode & (buckets.length - 1); |
- _HashMapEntry entry = buckets[index]; |
- while (entry != null) { |
- if (hashCode == entry.hashCode && entry.key == key) return true; |
- entry = entry.next; |
+ /* patch */ factory LinkedHashMap({ bool equals(K key1, K key2), |
+ int hashCode(K key), |
+ bool isValidKey(potentialKey) }) { |
+ if (isValidKey == null) { |
+ if (hashCode == null) { |
+ if (equals == null) { |
+ return new _LinkedHashMap<K, V>(); |
+ } |
+ if (identical(identical, equals)) { |
+ return new _LinkedIdentityHashMap<K, V>(); |
+ } |
+ hashCode = _defaultHashCode; |
+ } else if (equals == null) { |
+ equals = _defaultEquals; |
+ } |
+ } else { |
+ if (hashCode == null) { |
+ hashCode = _defaultHashCode; |
+ } |
+ if (equals == null) { |
+ equals = _defaultEquals; |
+ } |
} |
- return false; |
+ return new _LinkedCustomHashMap<K, V>(equals, hashCode, isValidKey); |
} |
+} |
- /* patch */ bool containsValue(Object value) { |
- var cursor = _nextEntry; |
+// Methods that are exactly the same in all three linked hash map variants. |
+abstract class _LinkedHashMapMixin<K, V> implements LinkedHashMap<K, V> { |
+ var _nextEntry; |
+ var _previousEntry; |
+ |
+ bool containsValue(Object value) { |
int modificationCount = _modificationCount; |
+ var cursor = _nextEntry; |
while (!identical(cursor, this)) { |
_HashMapEntry entry = cursor; |
if (entry.value == value) return true; |
+ if (modificationCount != _modificationCount) { |
+ throw new ConcurrentModificationError(this); |
+ } |
cursor = cursor._nextEntry; |
} |
return false; |
} |
- /* patch */ V operator[](Object key) { |
- int hashCode = key.hashCode; |
- List buckets = _buckets; |
- int index = hashCode & (buckets.length - 1); |
- _HashMapEntry entry = buckets[index]; |
- while (entry != null) { |
- if (hashCode == entry.hashCode && entry.key == key) { |
- return entry.value; |
- } |
- entry = entry.next; |
- } |
- return null; |
- } |
- |
- /* patch */ void operator []=(K key, V value) { |
- int hashCode = key.hashCode; |
- List buckets = _buckets; |
- int length = buckets.length; |
- int index = hashCode & (length - 1); |
- _HashMapEntry entry = buckets[index]; |
- while (entry != null) { |
- if (hashCode == entry.hashCode && entry.key == key) { |
- entry.value = value; |
- return; |
- } |
- entry = entry.next; |
- } |
- _addEntry(buckets, index, length, key, value, hashCode); |
- } |
- |
- /* patch */ V putIfAbsent(K key, V ifAbsent()) { |
- int hashCode = key.hashCode; |
- List buckets = _buckets; |
- int length = buckets.length; |
- int index = hashCode & (length - 1); |
- _HashMapEntry entry = buckets[index]; |
- while (entry != null) { |
- if (hashCode == entry.hashCode && entry.key == key) { |
- return entry.value; |
- } |
- entry = entry.next; |
- } |
- int stamp = _modificationCount; |
- V value = ifAbsent(); |
- if (stamp == _modificationCount) { |
- _addEntry(buckets, index, length, key, value, hashCode); |
- } else { |
- this[key] = value; |
- } |
- return value; |
- } |
- |
- /* patch */ void addAll(Map<K, V> other) { |
- other.forEach((K key, V value) { |
- this[key] = value; |
- }); |
- } |
- |
- /* patch */ void forEach(void action(K key, V value)) { |
- int stamp = _modificationCount; |
+ void forEach(void action(K key, V value)) { |
+ int modificationCount = _modificationCount; |
var cursor = _nextEntry; |
while (!identical(cursor, this)) { |
_HashMapEntry entry = cursor; |
action(entry.key, entry.value); |
- if (stamp != _modificationCount) { |
+ if (modificationCount != _modificationCount) { |
throw new ConcurrentModificationError(this); |
} |
cursor = cursor._nextEntry; |
} |
} |
- /* patch */ V remove(Object key) { |
- int hashCode = key.hashCode; |
- List buckets = _buckets; |
- int index = hashCode & (buckets.length - 1); |
- _LinkedHashMapEntry entry = buckets[index]; |
- _HashMapEntry previous = null; |
- while (entry != null) { |
- _HashMapEntry next = entry.next; |
- if (hashCode == entry.hashCode && entry.key == key) { |
- if (previous == null) { |
- buckets[index] = next; |
- } else { |
- previous.next = next; |
- } |
- entry._previousEntry._nextEntry = entry._nextEntry; |
- entry._nextEntry._previousEntry = entry._previousEntry; |
- entry._nextEntry = entry._previousEntry = null; |
- _elementCount--; |
- _modificationCount = |
- (_modificationCount + 1) & _MODIFICATION_COUNT_MASK; |
- return entry.value; |
- } |
- previous = entry; |
- entry = next; |
- } |
- return null; |
- } |
- |
- /* patch */ void clear() { |
- _elementCount = 0; |
+ void clear() { |
_nextEntry = _previousEntry = this; |
- _buckets = new List(_INITIAL_CAPACITY); |
+ _elementCount = 0; |
+ _buckets = new List(_HashMap._INITIAL_CAPACITY); |
_modificationCount = (_modificationCount + 1) & _MODIFICATION_COUNT_MASK; |
} |
@@ -804,26 +736,50 @@ patch class LinkedHashMap<K, V> { |
_modificationCount = (_modificationCount + 1) & _MODIFICATION_COUNT_MASK; |
} |
- void _resize() { |
- List oldBuckets = _buckets; |
- int oldLength = oldBuckets.length; |
- int newLength = oldLength << 1; |
- List newBuckets = new List(newLength); |
- for (int i = 0; i < oldLength; i++) { |
- _HashMapEntry entry = oldBuckets[i]; |
- while (entry != null) { |
- _HashMapEntry next = entry.next; |
- int hashCode = entry.hashCode; |
- int index = hashCode & (newLength - 1); |
- entry.next = newBuckets[index]; |
- newBuckets[index] = entry; |
- entry = next; |
- } |
+ void _removeEntry(_LinkedHashMapEntry entry, |
+ _HashMapEntry previousInBucket, |
+ int bucketIndex) { |
+ var previousInChain = entry._previousEntry; |
+ var nextInChain = entry._nextEntry; |
+ previousInChain._nextEntry = nextInChain; |
+ nextInChain._previousEntry = previousInChain; |
+ if (previousInBucket == null) { |
+ _buckets[bucketIndex] = entry.next; |
+ } else { |
+ previousInBucket.next = entry.next; |
} |
- _buckets = newBuckets; |
+ } |
+ |
+ |
+ Iterable<K> get keys => new _LinkedHashMapKeyIterable<K>(this); |
+ Iterable<V> get values => new _LinkedHashMapValueIterable<V>(this); |
+} |
+ |
+class _LinkedHashMap<K, V> extends _HashMap<K, V> |
+ with _LinkedHashMapMixin<K, V> { |
+ _LinkedHashMap() { |
+ _nextEntry = _previousEntry = this; |
+ } |
+} |
+ |
+class _LinkedIdentityHashMap<K, V> extends _IdentityHashMap<K, V> |
+ with _LinkedHashMapMixin<K, V> { |
+ _LinkedIdentityHashMap() { |
+ _nextEntry = _previousEntry = this; |
} |
} |
+class _LinkedCustomHashMap<K, V> extends _CustomHashMap<K, V> |
+ with _LinkedHashMapMixin<K, V> { |
+ _LinkedCustomHashMap(bool equals(K key1, K key2), |
+ int hashCode(K key), |
+ bool isValidKey(potentialKey)) |
+ : super(equals, hashCode, isValidKey) { |
+ _nextEntry = _previousEntry = this; |
+ } |
+} |
+ |
+ |
patch class LinkedHashSet<E> extends _HashSetBase<E> { |
static const int _INITIAL_CAPACITY = 8; |
_LinkedHashTable<E> _table; |