Index: sdk/lib/_internal/compiler/js_lib/collection_patch.dart |
diff --git a/sdk/lib/_internal/compiler/js_lib/collection_patch.dart b/sdk/lib/_internal/compiler/js_lib/collection_patch.dart |
index 9df7c993393011693306552cfbee61454f19a616..55af330c55d4a1a489eb0bbb2bdf92ecd8808080 100644 |
--- a/sdk/lib/_internal/compiler/js_lib/collection_patch.dart |
+++ b/sdk/lib/_internal/compiler/js_lib/collection_patch.dart |
@@ -9,6 +9,8 @@ import 'dart:_js_helper' show |
JsLinkedHashMap, LinkedHashMapCell, LinkedHashMapKeyIterable, |
LinkedHashMapKeyIterator; |
+const _USE_ES6_MAPS = const bool.fromEnvironment("dart2js.use.es6.maps"); |
+ |
@patch |
class HashMap<K, V> { |
@patch |
@@ -500,7 +502,7 @@ class LinkedHashMap<K, V> { |
} else { |
if (identical(identityHashCode, hashCode) && |
identical(identical, equals)) { |
- return new _LinkedIdentityHashMap<K, V>(); |
+ return new _LinkedIdentityHashMap<K, V>.es6(); |
} |
if (equals == null) { |
equals = _defaultEquals; |
@@ -518,7 +520,7 @@ class LinkedHashMap<K, V> { |
} |
@patch |
- factory LinkedHashMap.identity() = _LinkedIdentityHashMap<K, V>; |
+ factory LinkedHashMap.identity() = _LinkedIdentityHashMap<K, V>.es6; |
// Private factory constructor called by generated code for map literals. |
@NoInline() |
@@ -544,8 +546,20 @@ class LinkedHashMap<K, V> { |
fillLiteralMap(keyValuePairs, new JsLinkedHashMap()); |
} |
-// TODO(floitsch): use ES6 Maps when available. |
class _LinkedIdentityHashMap<K, V> extends JsLinkedHashMap<K, V> { |
+ static bool get _supportsEs6Maps { |
+ return JS('returns:bool;depends:none;effects:none;throws:never;gvn:true', |
+ 'typeof Map != "undefined"'); |
+ } |
+ |
+ factory _LinkedIdentityHashMap.es6() { |
+ return (_USE_ES6_MAPS && _LinkedIdentityHashMap._supportsEs6Maps) |
+ ? new _LinkedIdentityHashMap<K, V>() |
+ : new _Es6LinkedIdentityHashMap<K, V>(); |
+ } |
+ |
+ _LinkedIdentityHashMap(); |
+ |
int internalComputeHashCode(var key) { |
// We force the hash codes to be unsigned 30-bit integers to avoid |
// issues with problematic keys like '__proto__'. Another option |
@@ -564,6 +578,166 @@ class _LinkedIdentityHashMap<K, V> extends JsLinkedHashMap<K, V> { |
} |
} |
+class _Es6LinkedIdentityHashMap<K, V> |
+ extends _LinkedIdentityHashMap<K, V> implements InternalMap { |
+ final _map; |
+ int _modifications = 0; |
+ |
+ _Es6LinkedIdentityHashMap() : _map = JS('var', 'new Map()'); |
+ |
+ int get length => JS('int', '#.size', _map); |
+ bool get isEmpty => length == 0; |
+ bool get isNotEmpty => !isEmpty; |
+ |
+ Iterable<K> get keys => new _Es6MapIterable<K>(this, true); |
+ |
+ Iterable<V> get values => |
+ new _Es6MapIterable<V>(this, false); |
+ |
+ bool containsKey(Object key) { |
+ return JS('bool', '#.has(#)', _map, key); |
+ } |
+ |
+ bool containsValue(Object value) { |
+ return values.any((each) => each == value); |
+ } |
+ |
+ void addAll(Map<K, V> other) { |
+ other.forEach((K key, V value) { |
+ this[key] = value; |
+ }); |
+ } |
+ |
+ V operator[](Object key) { |
+ return JS('var', '#.get(#)', _map, key); |
+ } |
+ |
+ void operator[]=(K key, V value) { |
+ JS('var', '#.set(#, #)', _map, key, value); |
+ _modified(); |
+ } |
+ |
+ V putIfAbsent(K key, V ifAbsent()) { |
+ if (containsKey(key)) return this[key]; |
+ V value = ifAbsent(); |
+ this[key] = value; |
+ return value; |
+ } |
+ |
+ V remove(Object key) { |
+ V value = this[key]; |
+ JS('bool', '#.delete(#)', _map, key); |
+ _modified(); |
+ return value; |
+ } |
+ |
+ void clear() { |
+ JS('void', '#.clear()', _map); |
+ _modified(); |
+ } |
+ |
+ void forEach(void action(K key, V value)) { |
+ var jsEntries = JS('var', '#.entries()', _map); |
+ int modifications = _modifications; |
+ while (true) { |
+ var next = JS('var', '#.next()', jsEntries); |
+ bool done = JS('bool', '#.done', next); |
+ if (done) break; |
+ var entry = JS('var', '#.value', next); |
+ var key = JS('var', '#[0]', entry); |
+ var value = JS('var', '#[1]', entry); |
+ action(key, value); |
+ if (modifications != _modifications) { |
+ throw new ConcurrentModificationError(this); |
+ } |
+ } |
+ } |
+ |
+ void _modified() { |
+ // 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. |
+ _modifications = (_modifications + 1) & 0x3ffffff; |
+ } |
+ |
+ String toString() => Maps.mapToString(this); |
+} |
+ |
+class _Es6MapIterable<E> extends Iterable<E> |
+ implements EfficientLength { |
+ final _map; |
+ final bool _isKeys; |
+ |
+ _Es6MapIterable(this._map, this._isKeys); |
+ |
+ int get length => _map.length; |
+ bool get isEmpty => _map.isEmpty; |
+ |
+ Iterator<E> get iterator => |
+ new _Es6MapIterator<E>(_map, _map._modifications, _isKeys); |
+ |
+ bool contains(Object element) => _map.containsKey(element); |
+ |
+ void forEach(void f(E element)) { |
+ var jsIterator; |
+ if (_isKeys) { |
+ jsIterator = JS('var', '#.keys()', _map._map); |
+ } else { |
+ jsIterator = JS('var', '#.values()', _map._map); |
+ } |
+ int modifications = _map._modifications; |
+ while (true) { |
+ var next = JS('var', '#.next()', jsIterator); |
+ bool done = JS('bool', '#.done', next); |
+ if (done) break; |
+ var value = JS('var', '#.value', next); |
+ f(value); |
+ if (modifications != _map._modifications) { |
+ throw new ConcurrentModificationError(_map); |
+ } |
+ } |
+ } |
+} |
+ |
+class _Es6MapIterator<E> implements Iterator<E> { |
+ final _map; |
+ final int _modifications; |
+ final bool _isKeys; |
+ var _jsIterator; |
+ var _next; |
+ E _current; |
+ bool _done; |
+ |
+ _Es6MapIterator(this._map, this._modifications, this._isKeys) { |
+ if (_isKeys) { |
+ _jsIterator = JS('var', '#.keys()', _map._map); |
+ } else { |
+ _jsIterator = JS('var', '#.values()', _map._map); |
+ } |
+ _done = false; |
+ } |
+ |
+ E get current => _current; |
+ |
+ bool moveNext() { |
+ if (_modifications != _map._modifications) { |
+ throw new ConcurrentModificationError(_map); |
+ } |
+ if (_done) return false; |
+ _next = JS('var', '#.next()', _jsIterator); |
+ bool done = JS('bool', '#.done', _next); |
+ if (done) { |
+ _current = null; |
+ _done = true; |
+ return false; |
+ } else { |
+ _current = JS('var', '#.value', _next); |
+ return true; |
+ } |
+ } |
+} |
+ |
// TODO(floitsch): use ES6 maps when available. |
class _LinkedCustomHashMap<K, V> extends JsLinkedHashMap<K, V> { |
final _Equality<K> _equals; |