Index: sdk/lib/_internal/compiler/implementation/lib/collection_patch.dart |
diff --git a/sdk/lib/_internal/compiler/implementation/lib/collection_patch.dart b/sdk/lib/_internal/compiler/implementation/lib/collection_patch.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..10a95131ae15b6596019b9511d0bb8205bdb8380 |
--- /dev/null |
+++ b/sdk/lib/_internal/compiler/implementation/lib/collection_patch.dart |
@@ -0,0 +1,315 @@ |
+// 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; |
+ |
+patch class HashMap<K, V> { |
+ // BUG(9368): We're currently unable to pass the type arguments to |
+ // the construction of the JSMap instance. |
+ patch factory HashMap() => new JSMap(); |
+} |
+ |
+class JSMap<K, V> implements HashMap<K, V> { |
+ int _length = 0; |
+ var _strings, _nums, _rest; |
ahe
2013/03/22 16:18:31
Each of these should be on their own line, and eac
|
+ List _keys; |
+ |
+ int get length => _length; |
+ bool get isEmpty => _length == 0; |
+ |
+ Iterable<K> get keys { |
+ return new JSMapKeyIterable<K>(this); |
+ } |
+ |
+ Iterable<V> get values { |
+ return keys.map((each) => this[each]); |
+ } |
+ |
+ bool containsKey(K key) { |
+ if (key is String && key != '__proto__') { |
+ var strings = _strings; |
ahe
2013/03/22 16:18:31
Would this work:
var strings = JS('JavaScriptInde
|
+ if (strings == null) return false; |
+ var probe = JS('var', '#[#]', strings, key); |
+ return JS('bool', '# != null', probe); |
ahe
2013/03/22 16:18:31
Why not simply:
return probe != null;
|
+ } else if (key is num) { |
+ var nums = _nums; |
+ if (nums == null) return false; |
+ var probe = JS('var', '#[#]', nums, key); |
+ return JS('bool', '# != null', probe); |
+ } else { |
+ var rest = _rest; |
+ if (rest == null) return false; |
+ var hash = key.hashCode; |
+ var bucket = JS('var', '#[#]', rest, hash); |
ahe
2013/03/22 16:18:31
Do you know if open addressing would be better in
|
+ if (bucket != null) { |
+ int bucketLength = JS('int', '#.length', bucket); |
ahe
2013/03/22 16:18:31
Could you tell compiler that bucket is a List and
|
+ for (int i = 0; i < bucketLength; i += 2) { |
+ if (JS('var', '#[#]', bucket, i) == key) { |
+ return true; |
+ } |
+ } |
+ } |
+ return false; |
+ } |
+ } |
+ |
+ bool containsValue(V value) { |
+ return _computeKeys().any((each) => this[each] == value); |
ahe
2013/03/22 16:18:31
This looks slow. Wouldn't this always be faster:
|
+ } |
+ |
+ V operator[](K key) { |
+ if (key is String && key != '__proto__') { |
+ var strings = _strings; |
+ if (strings == null) return null; |
+ var probe = JS('var', '#[#]', strings, key); |
+ return JS('bool', '# === #', probe, strings) ? null : probe; |
ahe
2013/03/22 16:18:31
You're using _strings as a sentinel object? Comme
|
+ } else if (key is num) { |
+ var nums = _nums; |
+ if (nums == null) return null; |
+ var probe = JS('var', '#[#]', nums, key); |
+ return JS('bool', '# === #', probe, nums) ? null : probe; |
+ } else { |
+ var rest = _rest; |
+ if (rest == null) return null; |
+ var hash = JS('num', 'Number(#)', key.hashCode); |
ahe
2013/03/22 16:18:31
Shouldn't you throw some kind of exception if hash
|
+ var bucket = JS('var', '#[#]', rest, hash); |
+ if (bucket != null) { |
+ int bucketLength = JS('int', '#.length', bucket); |
+ for (int i = 0; i < bucketLength; i += 2) { |
+ if (JS('var', '#[#]', bucket, i) == key) { |
+ return JS('var', '#[# + 1]', bucket, i); |
+ } |
+ } |
+ } |
+ return null; |
+ } |
+ } |
+ |
+ void operator[]=(K key, V value) { |
+ if (key is String && key != '__proto__') { |
+ var strings = _strings; |
+ if (strings == null) { |
+ _strings = strings = _newHashTable(); |
+ _length++; |
+ _keys = null; |
ahe
2013/03/22 16:18:31
This might be more readable:
addedNewKey = true;
|
+ } else if (JS('bool', '#[#] == null', strings, key)) { |
+ _length++; |
+ _keys = null; |
+ } |
+ if (value == null) value = strings; |
+ JS('void', '#[#] = #', strings, key, value); |
+ } else if (key is num) { |
+ var nums = _nums; |
+ if (nums == null) { |
+ _nums = nums = _newHashTable(); |
+ _length++; |
+ _keys = null; |
+ } else if (JS('bool', '#[#] == null', nums, key)) { |
+ _length++; |
+ _keys = null; |
+ } |
+ if (value == null) value = nums; |
+ JS('void', '#[#] = #', nums, key, value); |
+ } else { |
+ var rest = _rest; |
+ var hash = JS('num', 'Number(#)', key.hashCode); |
ahe
2013/03/22 16:18:31
Exception?
|
+ var bucket; |
+ if (rest == null) { |
+ _rest = rest = _newHashTable(); |
+ } else { |
+ bucket = JS('var', '#[#]', rest, hash); |
+ } |
+ if (bucket == null) { |
+ JS('void', '#[#] = [#, #]', rest, hash, key, value); |
+ _length++; |
+ _keys = null; |
+ } else { |
+ int bucketLength = JS('int', '#.length', bucket); |
+ for (int i = 0; i < bucketLength; i += 2) { |
+ if (JS('var', '#[#]', bucket, i) == key) { |
+ JS('void', '#[# + 1] = #', bucket, i, value); |
+ return; |
+ } |
+ } |
+ JS('void', '#.push(#, #)', bucket, key, value); |
+ _length++; |
+ _keys = null; |
+ } |
+ } |
+ } |
+ |
+ V remove(K key) { |
+ if (key is String && key != '__proto__') { |
+ var strings = _strings; |
+ if (strings == null) return null; |
+ var probe = JS('var', '#[#]', strings, key); |
+ if (probe == null) return null; |
+ JS('void', 'delete #[#]', strings, key); |
+ _length--; |
+ _keys = null; |
+ return JS('bool', '# === #', probe, strings) ? null : probe; |
+ } else if (key is num) { |
+ var nums = _nums; |
+ if (nums == null) return null; |
+ var probe = JS('var', '#[#]', nums, key); |
+ if (probe == null) return null; |
+ JS('void', 'delete #[#]', nums, key); |
+ _length--; |
+ _keys = null; |
+ return JS('bool', '# === #', probe, nums) ? null : probe; |
+ } else { |
+ var rest = _rest; |
+ if (rest == null) return null; |
+ var hash = JS('num', 'Number(#)', key.hashCode); |
+ var bucket = JS('var', '#[#]', rest, hash); |
+ if (bucket == null) return null; |
+ int bucketLength = JS('int', '#.length', bucket); |
+ for (int i = 0; i < bucketLength; i += 2) { |
+ if (JS('var', '#[#]', bucket, i) == key) { |
+ var value = JS('var', '#[# + 1]', bucket, i); |
+ int last = bucketLength - 2; |
+ if (i != last) { |
+ JS('void', '#[#] = #[#]', bucket, i, bucket, last); |
+ JS('void', '#[# + 1] = #[# + 1]', bucket, i, bucket, last); |
+ } |
+ JS('void', '#.length = #', bucket, last); |
+ _length--; |
+ _keys = null; |
+ return value; |
+ } |
+ } |
+ return null; |
+ } |
+ } |
+ |
+ void clear() { |
+ if (_length > 0) { |
+ _strings = _nums = _rest = _keys = null; |
+ _length = 0; |
+ } |
+ } |
+ |
+ void forEach(void f(K key, V value)) { |
+ List keys = _computeKeys(); |
+ for (int i = 0, length = keys.length; i < length; i++) { |
+ var key = keys[i]; |
+ f(key, this[key]); |
+ if (!identical(keys, _keys)) { |
+ throw new ConcurrentModificationError(this); |
ahe
2013/03/22 16:18:31
This means there are situations where a concurrent
|
+ } |
+ } |
+ } |
+ |
+ V putIfAbsent(K key, V ifAbsent()) { |
+ if (containsKey(key)) return this[key]; |
+ V value = ifAbsent(); |
+ this[key] = value; |
+ return value; |
+ } |
+ |
+ void addAll(Map<K, V> other) { |
+ other.forEach((K key, V value) { |
+ this[key] = value; |
+ }); |
+ } |
+ |
+ String toString() => Maps.mapToString(this); |
+ |
+ List _computeKeys() { |
+ if (_keys != null) return _keys; |
+ |
+ List result = new List(_length); |
+ int index = 0; |
+ var strings = _strings; |
+ if (strings != null) { |
+ var names = JS('var', 'Object.getOwnPropertyNames(#)', strings); |
ahe
2013/03/22 16:18:31
There is a comment in the emitter about an Opera b
ahe
2013/03/22 16:18:31
If the type of the JS expression is '=List' can't
|
+ int length = JS('int', '#.length', names); |
+ for (int i = 0; i < length; i++) { |
+ String key = JS('String', '#[#]', names, i); |
+ result[index++] = key; |
+ } |
+ } |
+ |
+ var nums = _nums; |
+ if (_nums != null) { |
+ var names = JS('var', 'Object.getOwnPropertyNames(#)', nums); |
ahe
2013/03/22 16:18:31
Use =List?
|
+ int length = JS('int', '#.length', names); |
+ for (int i = 0; i < length; i++) { |
+ num key = JS('num', 'Number(#[#])', names, i); |
ahe
2013/03/22 16:18:31
A comment explaining why Number is necessary would
|
+ result[index++] = key; |
+ } |
+ } |
+ |
+ var rest = _rest; |
+ if (rest != null) { |
+ var names = JS('var', 'Object.getOwnPropertyNames(#)', rest); |
ahe
2013/03/22 16:18:31
Use =List?
|
+ int length = JS('int', '#.length', names); |
+ for (int i = 0; i < length; i++) { |
+ var key = JS('String', '#[#]', names, i); |
+ var bucket = JS('var', '#[#]', rest, key); |
+ int bucketLength = JS('int', '#.length', bucket); |
+ for (int i = 0; i < bucketLength; i += 2) { |
+ var key = JS('var', '#[#]', bucket, i); |
+ result[index++] = key; |
+ } |
+ } |
+ } |
+ return _keys = result; |
+ } |
+ |
+ _newHashTable() { |
+ var table = JS('var', 'Object.create(null)'); |
+ var temporaryKey = '<non-identifier-key>'; |
+ JS('void', '#[#] = #', table, temporaryKey, table); |
+ JS('void', 'delete #[#]', table, temporaryKey); |
ahe
2013/03/22 16:18:31
Add a comment explaining the purpose of <non-ident
|
+ return table; |
+ } |
+} |
+ |
+ |
+class JSMapKeyIterable<E> extends Iterable<E> { |
+ final JSMap _map; |
+ JSMapKeyIterable(this._map); |
+ |
+ int get length => _map._length; |
+ bool get isEmpty => _map._length == 0; |
+ |
+ Iterator<E> get iterator { |
+ return new JSMapKeyIterator<E>(_map, _map._computeKeys()); |
+ } |
+ |
+ void forEach(void f(E element)) { |
+ List keys = _map._computeKeys(); |
+ for (int i = 0, length = keys.length; i < length; i++) { |
+ f(keys[i]); |
+ if (!identical(keys, _map._keys)) { |
+ throw new ConcurrentModificationError(_map); |
+ } |
+ } |
+ } |
+} |
+ |
+class JSMapKeyIterator<E> implements Iterator<E> { |
+ final JSMap _map; |
+ final List _keys; |
+ int _offset = 0; |
+ E _current; |
+ |
+ JSMapKeyIterator(this._map, this._keys); |
+ |
+ E get current => _current; |
+ |
+ bool moveNext() { |
+ if (!identical(_keys, _map._keys)) { |
+ throw new ConcurrentModificationError(_map); |
+ } else if (_offset >= _keys.length) { |
+ _current = null; |
+ return false; |
+ } else { |
+ _current = _keys[_offset++]; |
+ return true; |
+ } |
+ } |
+} |