Chromium Code Reviews| 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..dcef50665806042b1c54e63839a7d06188819d7a 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,164 @@ class _LinkedIdentityHashMap<K, V> extends JsLinkedHashMap<K, V> { |
| } |
| } |
| +class _Es6LinkedIdentityHashMap<K, V> |
| + implements LinkedHashMap<K, V>, 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; |
| + _modified(); |
| + }); |
| + } |
| + |
| + 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; |
| + _modified(); |
| + 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. If you keep hold of an |
| + // iterator for that long, you might miss a modification |
| + // detection, and iteration can go sour. Don't do that. |
|
sra1
2015/06/18 17:01:01
I would not say "go sour". It is ominous and nons
Harry Terkelsen
2015/06/19 01:08:12
Done. I also changed the comment that I copied thi
|
| + _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; |
| + |
| + _Es6MapIterator(this._map, this._modifications, this._isKeys) { |
| + if (_isKeys) { |
| + _jsIterator = JS('var', '#.keys()', _map._map); |
| + } else { |
| + _jsIterator = JS('var', '#.values()', _map._map); |
| + } |
| + _next = JS('var', '#.next()', _jsIterator); |
|
sra1
2015/06/18 17:01:01
Can you make it that you only call #.next() when t
|
| + } |
| + |
| + E get current => _current; |
| + |
| + bool moveNext() { |
| + if (_modifications != _map._modifications) { |
| + throw new ConcurrentModificationError(_map); |
| + } |
| + bool done = JS('bool', '#.done', _next); |
| + if (done) { |
| + _current = null; |
| + return false; |
| + } else { |
| + _current = JS('var', '#.value', _next); |
| + _next = JS('var', '#.next()', _jsIterator); |
| + return true; |
| + } |
| + } |
| +} |
| + |
| // TODO(floitsch): use ES6 maps when available. |
| class _LinkedCustomHashMap<K, V> extends JsLinkedHashMap<K, V> { |
| final _Equality<K> _equals; |