Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 // Patch file for dart:collection classes. | 5 // Patch file for dart:collection classes. |
| 6 import 'dart:_foreign_helper' show JS; | 6 import 'dart:_foreign_helper' show JS; |
| 7 import 'dart:_js_helper' show | 7 import 'dart:_js_helper' show |
| 8 fillLiteralMap, InternalMap, NoInline, NoThrows, patch, JsLinkedHashMap; | 8 fillLiteralMap, InternalMap, NoInline, NoSideEffects, NoThrows, patch, |
| 9 JsLinkedHashMap, LinkedHashMapCell, LinkedHashMapKeyIterable, | |
| 10 LinkedHashMapKeyIterator; | |
| 11 | |
| 12 const _USE_ES6_MAPS = true; | |
| 9 | 13 |
| 10 @patch | 14 @patch |
| 11 class HashMap<K, V> { | 15 class HashMap<K, V> { |
| 12 @patch | 16 @patch |
| 13 factory HashMap({ bool equals(K key1, K key2), | 17 factory HashMap({ bool equals(K key1, K key2), |
| 14 int hashCode(K key), | 18 int hashCode(K key), |
| 15 bool isValidKey(Object potentialKey) }) { | 19 bool isValidKey(Object potentialKey) }) { |
| 16 if (isValidKey == null) { | 20 if (isValidKey == null) { |
| 17 if (hashCode == null) { | 21 if (hashCode == null) { |
| 18 if (equals == null) { | 22 if (equals == null) { |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 55 // | 59 // |
| 56 // where all keys in the same bucket share the same hash code. | 60 // where all keys in the same bucket share the same hash code. |
| 57 var _strings; | 61 var _strings; |
| 58 var _nums; | 62 var _nums; |
| 59 var _rest; | 63 var _rest; |
| 60 | 64 |
| 61 // When iterating over the hash map, it is very convenient to have a | 65 // When iterating over the hash map, it is very convenient to have a |
| 62 // list of all the keys. We cache that on the instance and clear the | 66 // list of all the keys. We cache that on the instance and clear the |
| 63 // the cache whenever the key set changes. This is also used to | 67 // the cache whenever the key set changes. This is also used to |
| 64 // guard against concurrent modifications. | 68 // guard against concurrent modifications. |
| 65 List _keys; | 69 List/*<K>*/ _keys; |
| 66 | 70 |
| 67 _HashMap(); | 71 _HashMap(); |
| 68 | 72 |
| 69 | 73 |
| 70 int get length => _length; | 74 int get length => _length; |
| 71 bool get isEmpty => _length == 0; | 75 bool get isEmpty => _length == 0; |
| 72 bool get isNotEmpty => !isEmpty; | 76 bool get isNotEmpty => !isEmpty; |
| 73 | 77 |
| 74 Iterable<K> get keys { | 78 Iterable<K> get keys { |
| 75 return new HashMapKeyIterable<K>(this); | 79 return new _HashMapKeyIterable<K, V>(this); |
| 76 } | 80 } |
| 77 | 81 |
| 78 Iterable<V> get values { | 82 Iterable<V> get values { |
| 79 return new MappedIterable<K, V>(keys, (each) => this[each]); | 83 return new MappedIterable<K, V>(keys, (each) => this[each]); |
| 80 } | 84 } |
| 81 | 85 |
| 82 bool containsKey(Object key) { | 86 bool containsKey(Object key) { |
| 83 if (_isStringKey(key)) { | 87 if (_isStringKey(key)) { |
| 84 var strings = _strings; | 88 var strings = _strings; |
| 85 return (strings == null) ? false : _hasTableEntry(strings, key); | 89 return (strings == null) ? false : _hasTableEntry(strings, key); |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 196 } | 200 } |
| 197 | 201 |
| 198 void clear() { | 202 void clear() { |
| 199 if (_length > 0) { | 203 if (_length > 0) { |
| 200 _strings = _nums = _rest = _keys = null; | 204 _strings = _nums = _rest = _keys = null; |
| 201 _length = 0; | 205 _length = 0; |
| 202 } | 206 } |
| 203 } | 207 } |
| 204 | 208 |
| 205 void forEach(void action(K key, V value)) { | 209 void forEach(void action(K key, V value)) { |
| 206 List keys = _computeKeys(); | 210 List/*<K>*/ keys = _computeKeys(); |
| 207 for (int i = 0, length = keys.length; i < length; i++) { | 211 for (int i = 0, length = keys.length; i < length; i++) { |
| 208 var key = JS('var', '#[#]', keys, i); | 212 var key = JS('var', '#[#]', keys, i); |
| 209 action(key, this[key]); | 213 action(key, this[key]); |
| 210 if (JS('bool', '# !== #', keys, _keys)) { | 214 if (JS('bool', '# !== #', keys, _keys)) { |
| 211 throw new ConcurrentModificationError(this); | 215 throw new ConcurrentModificationError(this); |
| 212 } | 216 } |
| 213 } | 217 } |
| 214 } | 218 } |
| 215 | 219 |
| 216 List _computeKeys() { | 220 List/*<K>*/ _computeKeys() { |
| 217 if (_keys != null) return _keys; | 221 if (_keys != null) return _keys; |
| 218 List result = new List(_length); | 222 List/*<K>*/ result = new List/*<K>*/(_length); |
|
Leaf
2016/05/18 20:11:54
consider var?
sra1
2016/05/18 22:10:04
Done.
| |
| 219 int index = 0; | 223 int index = 0; |
| 220 | 224 |
| 221 // Add all string keys to the list. | 225 // Add all string keys to the list. |
| 222 var strings = _strings; | 226 var strings = _strings; |
| 223 if (strings != null) { | 227 if (strings != null) { |
| 224 var names = JS('var', 'Object.getOwnPropertyNames(#)', strings); | 228 var names = JS('var', 'Object.getOwnPropertyNames(#)', strings); |
| 225 int entries = JS('int', '#.length', names); | 229 int entries = JS('int', '#.length', names); |
| 226 for (int i = 0; i < entries; i++) { | 230 for (int i = 0; i < entries; i++) { |
| 227 String key = JS('String', '#[#]', names, i); | 231 String key = JS('String', '#[#]', names, i); |
| 228 JS('void', '#[#] = #', result, index, key); | 232 JS('void', '#[#] = #', result, index, key); |
| (...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 333 | 337 |
| 334 static void _deleteTableEntry(var table, var key) { | 338 static void _deleteTableEntry(var table, var key) { |
| 335 JS('void', 'delete #[#]', table, key); | 339 JS('void', 'delete #[#]', table, key); |
| 336 } | 340 } |
| 337 | 341 |
| 338 List _getBucket(var table, var key) { | 342 List _getBucket(var table, var key) { |
| 339 var hash = _computeHashCode(key); | 343 var hash = _computeHashCode(key); |
| 340 return JS('var', '#[#]', table, hash); | 344 return JS('var', '#[#]', table, hash); |
| 341 } | 345 } |
| 342 | 346 |
| 343 int _findBucketIndex(var bucket, var key) { | 347 int _findBucketIndex(var bucket, Object key) { |
| 344 if (bucket == null) return -1; | 348 if (bucket == null) return -1; |
| 345 int length = JS('int', '#.length', bucket); | 349 int length = JS('int', '#.length', bucket); |
| 346 for (int i = 0; i < length; i += 2) { | 350 for (int i = 0; i < length; i += 2) { |
| 347 if (JS('var', '#[#]', bucket, i) == key) return i; | 351 if (JS('var', '#[#]', bucket, i) == key) return i; |
| 348 } | 352 } |
| 349 return -1; | 353 return -1; |
| 350 } | 354 } |
| 351 | 355 |
| 352 static _newHashTable() { | 356 static _newHashTable() { |
| 353 // Create a new JavaScript object to be used as a hash table. Use | 357 // Create a new JavaScript object to be used as a hash table. Use |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 364 } | 368 } |
| 365 | 369 |
| 366 class _IdentityHashMap<K, V> extends _HashMap<K, V> { | 370 class _IdentityHashMap<K, V> extends _HashMap<K, V> { |
| 367 int _computeHashCode(var key) { | 371 int _computeHashCode(var key) { |
| 368 // We force the hash codes to be unsigned 30-bit integers to avoid | 372 // We force the hash codes to be unsigned 30-bit integers to avoid |
| 369 // issues with problematic keys like '__proto__'. Another option | 373 // issues with problematic keys like '__proto__'. Another option |
| 370 // would be to throw an exception if the hash code isn't a number. | 374 // would be to throw an exception if the hash code isn't a number. |
| 371 return JS('int', '# & 0x3ffffff', identityHashCode(key)); | 375 return JS('int', '# & 0x3ffffff', identityHashCode(key)); |
| 372 } | 376 } |
| 373 | 377 |
| 374 int _findBucketIndex(var bucket, var key) { | 378 int _findBucketIndex(var bucket, Object key) { |
| 375 if (bucket == null) return -1; | 379 if (bucket == null) return -1; |
| 376 int length = JS('int', '#.length', bucket); | 380 int length = JS('int', '#.length', bucket); |
| 377 for (int i = 0; i < length; i += 2) { | 381 for (int i = 0; i < length; i += 2) { |
| 378 if (identical(JS('var', '#[#]', bucket, i), key)) return i; | 382 if (identical(JS('var', '#[#]', bucket, i), key)) return i; |
| 379 } | 383 } |
| 380 return -1; | 384 return -1; |
| 381 } | 385 } |
| 382 } | 386 } |
| 383 | 387 |
| 384 class _CustomHashMap<K, V> extends _HashMap<K, V> { | 388 class _CustomHashMap<K, V> extends _HashMap<K, V> { |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 408 return super._remove(key); | 412 return super._remove(key); |
| 409 } | 413 } |
| 410 | 414 |
| 411 int _computeHashCode(var key) { | 415 int _computeHashCode(var key) { |
| 412 // We force the hash codes to be unsigned 30-bit integers to avoid | 416 // We force the hash codes to be unsigned 30-bit integers to avoid |
| 413 // issues with problematic keys like '__proto__'. Another option | 417 // issues with problematic keys like '__proto__'. Another option |
| 414 // would be to throw an exception if the hash code isn't a number. | 418 // would be to throw an exception if the hash code isn't a number. |
| 415 return JS('int', '# & 0x3ffffff', _hashCode(key)); | 419 return JS('int', '# & 0x3ffffff', _hashCode(key)); |
| 416 } | 420 } |
| 417 | 421 |
| 418 int _findBucketIndex(var bucket, var key) { | 422 int _findBucketIndex(var bucket, Object key) { |
| 419 if (bucket == null) return -1; | 423 if (bucket == null) return -1; |
| 420 int length = JS('int', '#.length', bucket); | 424 int length = JS('int', '#.length', bucket); |
| 421 for (int i = 0; i < length; i += 2) { | 425 for (int i = 0; i < length; i += 2) { |
| 422 if (_equals(JS('var', '#[#]', bucket, i), key)) return i; | 426 if (_equals(JS('var', '#[#]', bucket, i), key)) return i; |
| 423 } | 427 } |
| 424 return -1; | 428 return -1; |
| 425 } | 429 } |
| 426 | 430 |
| 427 String toString() => Maps.mapToString(this); | 431 String toString() => Maps.mapToString(this); |
| 428 } | 432 } |
| 429 | 433 |
| 430 class HashMapKeyIterable<E> extends IterableBase<E> | 434 class _HashMapKeyIterable<K, V> extends Iterable<K> implements EfficientLength { |
|
Leaf
2016/05/18 20:11:53
If you get rid of V in the iterator, I don't think
sra1
2016/05/18 22:10:04
Done.
| |
| 431 implements EfficientLength { | 435 final _HashMap/*<K, V>*/ _map; |
| 432 final _map; | 436 _HashMapKeyIterable(this._map); |
| 433 HashMapKeyIterable(this._map); | |
| 434 | 437 |
| 435 int get length => _map._length; | 438 int get length => _map._length; |
| 436 bool get isEmpty => _map._length == 0; | 439 bool get isEmpty => _map._length == 0; |
| 437 | 440 |
| 438 Iterator<E> get iterator { | 441 Iterator<K> get iterator { |
| 439 return new HashMapKeyIterator<E>(_map, _map._computeKeys()); | 442 return new _HashMapKeyIterator<K, V>(_map, _map._computeKeys()); |
| 440 } | 443 } |
| 441 | 444 |
| 442 bool contains(Object element) { | 445 bool contains(Object element) { |
| 443 return _map.containsKey(element); | 446 return _map.containsKey(element); |
| 444 } | 447 } |
| 445 | 448 |
| 446 void forEach(void f(E element)) { | 449 void forEach(void f(K element)) { |
| 447 List keys = _map._computeKeys(); | 450 List/*<K>*/ keys = _map._computeKeys(); |
| 448 for (int i = 0, length = JS('int', '#.length', keys); i < length; i++) { | 451 for (int i = 0, length = JS('int', '#.length', keys); i < length; i++) { |
| 449 f(JS('var', '#[#]', keys, i)); | 452 f(JS('var', '#[#]', keys, i)); |
| 450 if (JS('bool', '# !== #', keys, _map._keys)) { | 453 if (JS('bool', '# !== #', keys, _map._keys)) { |
| 451 throw new ConcurrentModificationError(_map); | 454 throw new ConcurrentModificationError(_map); |
| 452 } | 455 } |
| 453 } | 456 } |
| 454 } | 457 } |
| 455 } | 458 } |
| 456 | 459 |
| 457 class HashMapKeyIterator<E> implements Iterator<E> { | 460 class _HashMapKeyIterator<K, V> implements Iterator<K> { |
|
Leaf
2016/05/18 20:11:53
Why do you need V? It never appears in the interf
sra1
2016/05/18 22:10:04
Done.
| |
| 458 final _map; | 461 final _HashMap/*<K, V>*/ _map; |
| 459 final List _keys; | 462 final List/*<K>*/ _keys; |
| 460 int _offset = 0; | 463 int _offset = 0; |
| 461 E _current; | 464 K _current; |
| 462 | 465 |
| 463 HashMapKeyIterator(this._map, this._keys); | 466 _HashMapKeyIterator(this._map, this._keys); |
| 464 | 467 |
| 465 E get current => _current; | 468 K get current => _current; |
| 466 | 469 |
| 467 bool moveNext() { | 470 bool moveNext() { |
| 468 var keys = _keys; | 471 var keys = _keys; |
| 469 int offset = _offset; | 472 int offset = _offset; |
| 470 if (JS('bool', '# !== #', keys, _map._keys)) { | 473 if (JS('bool', '# !== #', keys, _map._keys)) { |
| 471 throw new ConcurrentModificationError(_map); | 474 throw new ConcurrentModificationError(_map); |
| 472 } else if (offset >= JS('int', '#.length', keys)) { | 475 } else if (offset >= JS('int', '#.length', keys)) { |
| 473 _current = null; | 476 _current = null; |
| 474 return false; | 477 return false; |
| 475 } else { | 478 } else { |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 491 bool isValidKey(Object potentialKey) }) { | 494 bool isValidKey(Object potentialKey) }) { |
| 492 if (isValidKey == null) { | 495 if (isValidKey == null) { |
| 493 if (hashCode == null) { | 496 if (hashCode == null) { |
| 494 if (equals == null) { | 497 if (equals == null) { |
| 495 return new JsLinkedHashMap<K, V>.es6(); | 498 return new JsLinkedHashMap<K, V>.es6(); |
| 496 } | 499 } |
| 497 hashCode = _defaultHashCode; | 500 hashCode = _defaultHashCode; |
| 498 } else { | 501 } else { |
| 499 if (identical(identityHashCode, hashCode) && | 502 if (identical(identityHashCode, hashCode) && |
| 500 identical(identical, equals)) { | 503 identical(identical, equals)) { |
| 501 return new _LinkedIdentityHashMap<K, V>(); | 504 return new _LinkedIdentityHashMap<K, V>.es6(); |
| 502 } | 505 } |
| 503 if (equals == null) { | 506 if (equals == null) { |
| 504 equals = _defaultEquals; | 507 equals = _defaultEquals; |
| 505 } | 508 } |
| 506 } | 509 } |
| 507 } else { | 510 } else { |
| 508 if (hashCode == null) { | 511 if (hashCode == null) { |
| 509 hashCode = _defaultHashCode; | 512 hashCode = _defaultHashCode; |
| 510 } | 513 } |
| 511 if (equals == null) { | 514 if (equals == null) { |
| 512 equals = _defaultEquals; | 515 equals = _defaultEquals; |
| 513 } | 516 } |
| 514 } | 517 } |
| 515 return new _LinkedCustomHashMap<K, V>(equals, hashCode, isValidKey); | 518 return new _LinkedCustomHashMap<K, V>(equals, hashCode, isValidKey); |
| 516 } | 519 } |
| 517 | 520 |
| 518 @patch | 521 @patch |
| 519 factory LinkedHashMap.identity() = _LinkedIdentityHashMap<K, V>; | 522 factory LinkedHashMap.identity() = _LinkedIdentityHashMap<K, V>.es6; |
| 523 } | |
| 520 | 524 |
| 521 // Private factory constructor called by generated code for map literals. | 525 class _LinkedIdentityHashMap<K, V> extends JsLinkedHashMap<K, V> { |
| 522 @NoInline() | 526 static bool get _supportsEs6Maps { |
| 523 factory LinkedHashMap._literal(List keyValuePairs) { | 527 return true; |
| 524 return fillLiteralMap(keyValuePairs, new _LinkedHashMap<K, V>()); | |
| 525 } | 528 } |
| 526 | 529 |
| 527 // Private factory constructor called by generated code for map literals. | 530 factory _LinkedIdentityHashMap.es6() { |
| 528 @NoThrows() @NoInline() | 531 return (_USE_ES6_MAPS && _LinkedIdentityHashMap._supportsEs6Maps) |
| 529 factory LinkedHashMap._empty() { | 532 ? new _Es6LinkedIdentityHashMap<K, V>() |
| 530 return new _LinkedHashMap<K, V>(); | 533 : new _LinkedIdentityHashMap<K, V>(); |
| 534 } | |
| 535 | |
| 536 _LinkedIdentityHashMap(); | |
| 537 | |
| 538 int internalComputeHashCode(var key) { | |
| 539 // We force the hash codes to be unsigned 30-bit integers to avoid | |
| 540 // issues with problematic keys like '__proto__'. Another option | |
| 541 // would be to throw an exception if the hash code isn't a number. | |
| 542 return JS('int', '# & 0x3ffffff', identityHashCode(key)); | |
| 543 } | |
| 544 | |
| 545 int internalFindBucketIndex(var bucket, var key) { | |
| 546 if (bucket == null) return -1; | |
| 547 int length = JS('int', '#.length', bucket); | |
| 548 for (int i = 0; i < length; i++) { | |
| 549 LinkedHashMapCell/*<K, V>*/ cell = JS('var', '#[#]', bucket, i); | |
| 550 if (identical(cell.hashMapCellKey, key)) return i; | |
| 551 } | |
| 552 return -1; | |
| 531 } | 553 } |
| 532 } | 554 } |
| 533 | 555 |
| 534 class _LinkedHashMap<K, V> implements LinkedHashMap<K, V>, InternalMap { | 556 class _Es6LinkedIdentityHashMap<K, V> |
| 535 int _length = 0; | 557 extends _LinkedIdentityHashMap<K, V> implements InternalMap { |
| 536 | 558 final _map; |
| 537 // The hash map contents are divided into three parts: one part for | |
| 538 // string keys, one for numeric keys, and one for the rest. String | |
| 539 // and numeric keys map directly to their linked cells, but the rest | |
| 540 // of the entries are stored in bucket lists of the form: | |
| 541 // | |
| 542 // [cell-0, cell-1, ...] | |
| 543 // | |
| 544 // where all keys in the same bucket share the same hash code. | |
| 545 var _strings; | |
| 546 var _nums; | |
| 547 var _rest; | |
| 548 | |
| 549 // The keys and values are stored in cells that are linked together | |
| 550 // to form a double linked list. | |
| 551 LinkedHashMapCell _first; | |
| 552 LinkedHashMapCell _last; | |
| 553 | |
| 554 // We track the number of modifications done to the key set of the | |
| 555 // hash map to be able to throw when the map is modified while being | |
| 556 // iterated over. | |
| 557 int _modifications = 0; | 559 int _modifications = 0; |
| 558 | 560 |
| 559 _LinkedHashMap(); | 561 _Es6LinkedIdentityHashMap() : _map = JS('var', 'new Map()'); |
| 560 | 562 |
| 561 | 563 int get length => JS('int', '#.size', _map); |
| 562 int get length => _length; | 564 bool get isEmpty => length == 0; |
| 563 bool get isEmpty => _length == 0; | |
| 564 bool get isNotEmpty => !isEmpty; | 565 bool get isNotEmpty => !isEmpty; |
| 565 | 566 |
| 566 Iterable<K> get keys { | 567 Iterable<K> get keys => new _Es6MapIterable<K>(this, true); |
| 567 return new LinkedHashMapKeyIterable<K>(this); | |
| 568 } | |
| 569 | 568 |
| 570 Iterable<V> get values { | 569 Iterable<V> get values => |
| 571 return new MappedIterable<K, V>(keys, (each) => this[each]); | 570 new _Es6MapIterable<V>(this, false); |
| 572 } | |
| 573 | 571 |
| 574 bool containsKey(Object key) { | 572 bool containsKey(Object key) { |
| 575 if (_isStringKey(key)) { | 573 return JS('bool', '#.has(#)', _map, key); |
| 576 var strings = _strings; | |
| 577 if (strings == null) return false; | |
| 578 LinkedHashMapCell cell = _getTableEntry(strings, key); | |
| 579 return cell != null; | |
| 580 } else if (_isNumericKey(key)) { | |
| 581 var nums = _nums; | |
| 582 if (nums == null) return false; | |
| 583 LinkedHashMapCell cell = _getTableEntry(nums, key); | |
| 584 return cell != null; | |
| 585 } else { | |
| 586 return _containsKey(key); | |
| 587 } | |
| 588 } | |
| 589 | |
| 590 bool _containsKey(Object key) { | |
| 591 var rest = _rest; | |
| 592 if (rest == null) return false; | |
| 593 var bucket = _getBucket(rest, key); | |
| 594 return _findBucketIndex(bucket, key) >= 0; | |
| 595 } | 574 } |
| 596 | 575 |
| 597 bool containsValue(Object value) { | 576 bool containsValue(Object value) { |
| 598 return keys.any((each) => this[each] == value); | 577 return values.any((each) => each == value); |
| 599 } | 578 } |
| 600 | 579 |
| 601 void addAll(Map<K, V> other) { | 580 void addAll(Map<K, V> other) { |
| 602 other.forEach((K key, V value) { | 581 other.forEach((K key, V value) { |
| 603 this[key] = value; | 582 this[key] = value; |
| 604 }); | 583 }); |
| 605 } | 584 } |
| 606 | 585 |
| 607 V operator[](Object key) { | 586 V operator[](Object key) { |
| 608 if (_isStringKey(key)) { | 587 return JS('var', '#.get(#)', _map, key); |
| 609 var strings = _strings; | |
| 610 if (strings == null) return null; | |
| 611 LinkedHashMapCell cell = _getTableEntry(strings, key); | |
| 612 return (cell == null) ? null : cell._value; | |
| 613 } else if (_isNumericKey(key)) { | |
| 614 var nums = _nums; | |
| 615 if (nums == null) return null; | |
| 616 LinkedHashMapCell cell = _getTableEntry(nums, key); | |
| 617 return (cell == null) ? null : cell._value; | |
| 618 } else { | |
| 619 return _get(key); | |
| 620 } | |
| 621 } | |
| 622 | |
| 623 V _get(Object key) { | |
| 624 var rest = _rest; | |
| 625 if (rest == null) return null; | |
| 626 var bucket = _getBucket(rest, key); | |
| 627 int index = _findBucketIndex(bucket, key); | |
| 628 if (index < 0) return null; | |
| 629 LinkedHashMapCell cell = JS('var', '#[#]', bucket, index); | |
| 630 return cell._value; | |
| 631 } | 588 } |
| 632 | 589 |
| 633 void operator[]=(K key, V value) { | 590 void operator[]=(K key, V value) { |
| 634 if (_isStringKey(key)) { | 591 JS('var', '#.set(#, #)', _map, key, value); |
| 635 var strings = _strings; | 592 _modified(); |
| 636 if (strings == null) _strings = strings = _newHashTable(); | |
| 637 _addHashTableEntry(strings, key, value); | |
| 638 } else if (_isNumericKey(key)) { | |
| 639 var nums = _nums; | |
| 640 if (nums == null) _nums = nums = _newHashTable(); | |
| 641 _addHashTableEntry(nums, key, value); | |
| 642 } else { | |
| 643 _set(key, value); | |
| 644 } | |
| 645 } | |
| 646 | |
| 647 void _set(K key, V value) { | |
| 648 var rest = _rest; | |
| 649 if (rest == null) _rest = rest = _newHashTable(); | |
| 650 var hash = _computeHashCode(key); | |
| 651 var bucket = JS('var', '#[#]', rest, hash); | |
| 652 if (bucket == null) { | |
| 653 LinkedHashMapCell cell = _newLinkedCell(key, value); | |
| 654 _setTableEntry(rest, hash, JS('var', '[#]', cell)); | |
| 655 } else { | |
| 656 int index = _findBucketIndex(bucket, key); | |
| 657 if (index >= 0) { | |
| 658 LinkedHashMapCell cell = JS('var', '#[#]', bucket, index); | |
| 659 cell._value = value; | |
| 660 } else { | |
| 661 LinkedHashMapCell cell = _newLinkedCell(key, value); | |
| 662 JS('void', '#.push(#)', bucket, cell); | |
| 663 } | |
| 664 } | |
| 665 } | 593 } |
| 666 | 594 |
| 667 V putIfAbsent(K key, V ifAbsent()) { | 595 V putIfAbsent(K key, V ifAbsent()) { |
| 668 if (containsKey(key)) return this[key]; | 596 if (containsKey(key)) return this[key]; |
| 669 V value = ifAbsent(); | 597 V value = ifAbsent(); |
| 670 this[key] = value; | 598 this[key] = value; |
| 671 return value; | 599 return value; |
| 672 } | 600 } |
| 673 | 601 |
| 674 V remove(Object key) { | 602 V remove(Object key) { |
| 675 if (_isStringKey(key)) { | 603 V value = this[key]; |
| 676 return _removeHashTableEntry(_strings, key); | 604 JS('bool', '#.delete(#)', _map, key); |
| 677 } else if (_isNumericKey(key)) { | 605 _modified(); |
| 678 return _removeHashTableEntry(_nums, key); | 606 return value; |
| 679 } else { | 607 } |
| 680 return _remove(key); | 608 |
| 609 void clear() { | |
| 610 JS('void', '#.clear()', _map); | |
| 611 _modified(); | |
| 612 } | |
| 613 | |
| 614 void forEach(void action(K key, V value)) { | |
| 615 var jsEntries = JS('var', '#.entries()', _map); | |
| 616 int modifications = _modifications; | |
| 617 while (true) { | |
| 618 var next = JS('var', '#.next()', jsEntries); | |
| 619 bool done = JS('bool', '#.done', next); | |
| 620 if (done) break; | |
| 621 var entry = JS('var', '#.value', next); | |
| 622 var key = JS('var', '#[0]', entry); | |
| 623 var value = JS('var', '#[1]', entry); | |
| 624 action(key, value); | |
| 625 if (modifications != _modifications) { | |
| 626 throw new ConcurrentModificationError(this); | |
| 627 } | |
| 681 } | 628 } |
| 682 } | 629 } |
| 683 | 630 |
| 684 V _remove(Object key) { | |
| 685 var rest = _rest; | |
| 686 if (rest == null) return null; | |
| 687 var bucket = _getBucket(rest, key); | |
| 688 int index = _findBucketIndex(bucket, key); | |
| 689 if (index < 0) return null; | |
| 690 // Use splice to remove the [cell] element at the index and | |
| 691 // unlink the cell before returning its value. | |
| 692 LinkedHashMapCell cell = JS('var', '#.splice(#, 1)[0]', bucket, index); | |
| 693 _unlinkCell(cell); | |
| 694 // TODO(kasperl): Consider getting rid of the bucket list when | |
| 695 // the length reaches zero. | |
| 696 return cell._value; | |
| 697 } | |
| 698 | |
| 699 void clear() { | |
| 700 if (_length > 0) { | |
| 701 _strings = _nums = _rest = _first = _last = null; | |
| 702 _length = 0; | |
| 703 _modified(); | |
| 704 } | |
| 705 } | |
| 706 | |
| 707 void forEach(void action(K key, V value)) { | |
| 708 LinkedHashMapCell cell = _first; | |
| 709 int modifications = _modifications; | |
| 710 while (cell != null) { | |
| 711 action(cell._key, cell._value); | |
| 712 if (modifications != _modifications) { | |
| 713 throw new ConcurrentModificationError(this); | |
| 714 } | |
| 715 cell = cell._next; | |
| 716 } | |
| 717 } | |
| 718 | |
| 719 void _addHashTableEntry(var table, K key, V value) { | |
| 720 LinkedHashMapCell cell = _getTableEntry(table, key); | |
| 721 if (cell == null) { | |
| 722 _setTableEntry(table, key, _newLinkedCell(key, value)); | |
| 723 } else { | |
| 724 cell._value = value; | |
| 725 } | |
| 726 } | |
| 727 | |
| 728 V _removeHashTableEntry(var table, Object key) { | |
| 729 if (table == null) return null; | |
| 730 LinkedHashMapCell cell = _getTableEntry(table, key); | |
| 731 if (cell == null) return null; | |
| 732 _unlinkCell(cell); | |
| 733 _deleteTableEntry(table, key); | |
| 734 return cell._value; | |
| 735 } | |
| 736 | |
| 737 void _modified() { | 631 void _modified() { |
| 738 // Value cycles after 2^30 modifications. If you keep hold of an | 632 // Value cycles after 2^30 modifications so that modification counts are |
| 739 // iterator for that long, you might miss a modification | 633 // always unboxed (Smi) values. Modification detection will be missed if you |
| 740 // detection, and iteration can go sour. Don't do that. | 634 // make exactly some multiple of 2^30 modifications between advances of an |
| 635 // iterator. | |
| 741 _modifications = (_modifications + 1) & 0x3ffffff; | 636 _modifications = (_modifications + 1) & 0x3ffffff; |
| 742 } | 637 } |
| 743 | 638 |
| 744 // Create a new cell and link it in as the last one in the list. | |
| 745 LinkedHashMapCell _newLinkedCell(K key, V value) { | |
| 746 LinkedHashMapCell cell = new LinkedHashMapCell(key, value); | |
| 747 if (_first == null) { | |
| 748 _first = _last = cell; | |
| 749 } else { | |
| 750 LinkedHashMapCell last = _last; | |
| 751 cell._previous = last; | |
| 752 _last = last._next = cell; | |
| 753 } | |
| 754 _length++; | |
| 755 _modified(); | |
| 756 return cell; | |
| 757 } | |
| 758 | |
| 759 // Unlink the given cell from the linked list of cells. | |
| 760 void _unlinkCell(LinkedHashMapCell cell) { | |
| 761 LinkedHashMapCell previous = cell._previous; | |
| 762 LinkedHashMapCell next = cell._next; | |
| 763 if (previous == null) { | |
| 764 assert(cell == _first); | |
| 765 _first = next; | |
| 766 } else { | |
| 767 previous._next = next; | |
| 768 } | |
| 769 if (next == null) { | |
| 770 assert(cell == _last); | |
| 771 _last = previous; | |
| 772 } else { | |
| 773 next._previous = previous; | |
| 774 } | |
| 775 _length--; | |
| 776 _modified(); | |
| 777 } | |
| 778 | |
| 779 static bool _isStringKey(var key) { | |
| 780 return key is String && key != '__proto__'; | |
| 781 } | |
| 782 | |
| 783 static bool _isNumericKey(var key) { | |
| 784 // Only treat unsigned 30-bit integers as numeric keys. This way, | |
| 785 // we avoid converting them to strings when we use them as keys in | |
| 786 // the JavaScript hash table object. | |
| 787 return key is num && JS('bool', '(# & 0x3ffffff) === #', key, key); | |
| 788 } | |
| 789 | |
| 790 int _computeHashCode(var key) { | |
| 791 // We force the hash codes to be unsigned 30-bit integers to avoid | |
| 792 // issues with problematic keys like '__proto__'. Another option | |
| 793 // would be to throw an exception if the hash code isn't a number. | |
| 794 return JS('int', '# & 0x3ffffff', key.hashCode); | |
| 795 } | |
| 796 | |
| 797 static _getTableEntry(var table, var key) { | |
| 798 return JS('var', '#[#]', table, key); | |
| 799 } | |
| 800 | |
| 801 static void _setTableEntry(var table, var key, var value) { | |
| 802 assert(value != null); | |
| 803 JS('void', '#[#] = #', table, key, value); | |
| 804 } | |
| 805 | |
| 806 static void _deleteTableEntry(var table, var key) { | |
| 807 JS('void', 'delete #[#]', table, key); | |
| 808 } | |
| 809 | |
| 810 List _getBucket(var table, var key) { | |
| 811 var hash = _computeHashCode(key); | |
| 812 return JS('var', '#[#]', table, hash); | |
| 813 } | |
| 814 | |
| 815 int _findBucketIndex(var bucket, var key) { | |
| 816 if (bucket == null) return -1; | |
| 817 int length = JS('int', '#.length', bucket); | |
| 818 for (int i = 0; i < length; i++) { | |
| 819 LinkedHashMapCell cell = JS('var', '#[#]', bucket, i); | |
| 820 if (cell._key == key) return i; | |
| 821 } | |
| 822 return -1; | |
| 823 } | |
| 824 | |
| 825 static _newHashTable() { | |
| 826 // Create a new JavaScript object to be used as a hash table. Use | |
| 827 // Object.create to avoid the properties on Object.prototype | |
| 828 // showing up as entries. | |
| 829 var table = JS('var', 'Object.create(null)'); | |
| 830 // Attempt to force the hash table into 'dictionary' mode by | |
| 831 // adding a property to it and deleting it again. | |
| 832 var temporaryKey = '<non-identifier-key>'; | |
| 833 _setTableEntry(table, temporaryKey, table); | |
| 834 _deleteTableEntry(table, temporaryKey); | |
| 835 return table; | |
| 836 } | |
| 837 | |
| 838 String toString() => Maps.mapToString(this); | 639 String toString() => Maps.mapToString(this); |
| 839 } | 640 } |
| 840 | 641 |
| 841 class _LinkedIdentityHashMap<K, V> extends _LinkedHashMap<K, V> { | 642 class _Es6MapIterable<E> extends Iterable<E> |
| 842 int _computeHashCode(var key) { | 643 implements EfficientLength { |
| 843 // We force the hash codes to be unsigned 30-bit integers to avoid | 644 final _map; |
| 844 // issues with problematic keys like '__proto__'. Another option | 645 final bool _isKeys; |
| 845 // would be to throw an exception if the hash code isn't a number. | |
| 846 return JS('int', '# & 0x3ffffff', identityHashCode(key)); | |
| 847 } | |
| 848 | 646 |
| 849 int _findBucketIndex(var bucket, var key) { | 647 _Es6MapIterable(this._map, this._isKeys); |
| 850 if (bucket == null) return -1; | 648 |
| 851 int length = JS('int', '#.length', bucket); | 649 int get length => _map.length; |
| 852 for (int i = 0; i < length; i++) { | 650 bool get isEmpty => _map.isEmpty; |
| 853 LinkedHashMapCell cell = JS('var', '#[#]', bucket, i); | 651 |
| 854 if (identical(cell._key, key)) return i; | 652 Iterator<E> get iterator => |
| 653 new _Es6MapIterator<E>(_map, _map._modifications, _isKeys); | |
| 654 | |
| 655 bool contains(Object element) => _map.containsKey(element); | |
| 656 | |
| 657 void forEach(void f(E element)) { | |
| 658 var jsIterator; | |
| 659 if (_isKeys) { | |
| 660 jsIterator = JS('var', '#.keys()', _map._map); | |
| 661 } else { | |
| 662 jsIterator = JS('var', '#.values()', _map._map); | |
| 855 } | 663 } |
| 856 return -1; | 664 int modifications = _map._modifications; |
| 665 while (true) { | |
| 666 var next = JS('var', '#.next()', jsIterator); | |
| 667 bool done = JS('bool', '#.done', next); | |
| 668 if (done) break; | |
| 669 var value = JS('var', '#.value', next); | |
| 670 f(value); | |
| 671 if (modifications != _map._modifications) { | |
| 672 throw new ConcurrentModificationError(_map); | |
| 673 } | |
| 674 } | |
| 857 } | 675 } |
| 858 } | 676 } |
| 859 | 677 |
| 860 class _LinkedCustomHashMap<K, V> extends _LinkedHashMap<K, V> { | 678 class _Es6MapIterator<E> implements Iterator<E> { |
| 679 final _map; | |
| 680 final int _modifications; | |
| 681 final bool _isKeys; | |
| 682 var _jsIterator; | |
| 683 var _next; | |
| 684 E _current; | |
| 685 bool _done; | |
| 686 | |
| 687 _Es6MapIterator(this._map, this._modifications, this._isKeys) { | |
| 688 if (_isKeys) { | |
| 689 _jsIterator = JS('var', '#.keys()', _map._map); | |
| 690 } else { | |
| 691 _jsIterator = JS('var', '#.values()', _map._map); | |
| 692 } | |
| 693 _done = false; | |
| 694 } | |
| 695 | |
| 696 E get current => _current; | |
| 697 | |
| 698 bool moveNext() { | |
| 699 if (_modifications != _map._modifications) { | |
| 700 throw new ConcurrentModificationError(_map); | |
| 701 } | |
| 702 if (_done) return false; | |
| 703 _next = JS('var', '#.next()', _jsIterator); | |
| 704 bool done = JS('bool', '#.done', _next); | |
| 705 if (done) { | |
| 706 _current = null; | |
| 707 _done = true; | |
| 708 return false; | |
| 709 } else { | |
| 710 _current = JS('var', '#.value', _next); | |
| 711 return true; | |
| 712 } | |
| 713 } | |
| 714 } | |
| 715 | |
| 716 // TODO(floitsch): use ES6 maps when available. | |
| 717 class _LinkedCustomHashMap<K, V> extends JsLinkedHashMap<K, V> { | |
| 861 final _Equality<K> _equals; | 718 final _Equality<K> _equals; |
| 862 final _Hasher<K> _hashCode; | 719 final _Hasher<K> _hashCode; |
| 863 final _Predicate<Object> _validKey; | 720 final _Predicate<Object> _validKey; |
| 864 _LinkedCustomHashMap(this._equals, this._hashCode, | 721 _LinkedCustomHashMap(this._equals, this._hashCode, |
| 865 bool validKey(Object potentialKey)) | 722 bool validKey(Object potentialKey)) |
| 866 : _validKey = (validKey != null) ? validKey : ((v) => v is K); | 723 : _validKey = (validKey != null) ? validKey : ((v) => v is K); |
| 867 | 724 |
| 868 V operator[](Object key) { | 725 V operator[](Object key) { |
| 869 if (!_validKey(key)) return null; | 726 if (!_validKey(key)) return null; |
| 870 return super._get(key); | 727 return super.internalGet(key); |
| 871 } | 728 } |
| 872 | 729 |
| 873 void operator[]=(K key, V value) { | 730 void operator[]=(K key, V value) { |
| 874 super._set(key, value); | 731 super.internalSet(key, value); |
| 875 } | 732 } |
| 876 | 733 |
| 877 bool containsKey(Object key) { | 734 bool containsKey(Object key) { |
| 878 if (!_validKey(key)) return false; | 735 if (!_validKey(key)) return false; |
| 879 return super._containsKey(key); | 736 return super.internalContainsKey(key); |
| 880 } | 737 } |
| 881 | 738 |
| 882 V remove(Object key) { | 739 V remove(Object key) { |
| 883 if (!_validKey(key)) return null; | 740 if (!_validKey(key)) return null; |
| 884 return super._remove(key); | 741 return super.internalRemove(key); |
| 885 } | 742 } |
| 886 | 743 |
| 887 int _computeHashCode(var key) { | 744 int internalComputeHashCode(var key) { |
| 888 // We force the hash codes to be unsigned 30-bit integers to avoid | 745 // We force the hash codes to be unsigned 30-bit integers to avoid |
| 889 // issues with problematic keys like '__proto__'. Another option | 746 // issues with problematic keys like '__proto__'. Another option |
| 890 // would be to throw an exception if the hash code isn't a number. | 747 // would be to throw an exception if the hash code isn't a number. |
| 891 return JS('int', '# & 0x3ffffff', _hashCode(key)); | 748 return JS('int', '# & 0x3ffffff', _hashCode(key)); |
| 892 } | 749 } |
| 893 | 750 |
| 894 int _findBucketIndex(var bucket, var key) { | 751 int internalFindBucketIndex(var bucket, var key) { |
| 895 if (bucket == null) return -1; | 752 if (bucket == null) return -1; |
| 896 int length = JS('int', '#.length', bucket); | 753 int length = JS('int', '#.length', bucket); |
| 897 for (int i = 0; i < length; i++) { | 754 for (int i = 0; i < length; i++) { |
| 898 LinkedHashMapCell cell = JS('var', '#[#]', bucket, i); | 755 LinkedHashMapCell/*<K, V>*/ cell = JS('var', '#[#]', bucket, i); |
| 899 if (_equals(cell._key, key)) return i; | 756 if (_equals(cell.hashMapCellKey, key)) return i; |
| 900 } | 757 } |
| 901 return -1; | 758 return -1; |
| 902 } | 759 } |
| 903 } | 760 } |
| 904 | 761 |
| 905 class LinkedHashMapCell { | |
| 906 final _key; | |
| 907 var _value; | |
| 908 | |
| 909 LinkedHashMapCell _next; | |
| 910 LinkedHashMapCell _previous; | |
| 911 | |
| 912 LinkedHashMapCell(this._key, this._value); | |
| 913 } | |
| 914 | |
| 915 class LinkedHashMapKeyIterable<E> extends IterableBase<E> | |
| 916 implements EfficientLength { | |
| 917 final _map; | |
| 918 LinkedHashMapKeyIterable(this._map); | |
| 919 | |
| 920 int get length => _map._length; | |
| 921 bool get isEmpty => _map._length == 0; | |
| 922 | |
| 923 Iterator<E> get iterator { | |
| 924 return new LinkedHashMapKeyIterator<E>(_map, _map._modifications); | |
| 925 } | |
| 926 | |
| 927 bool contains(Object element) { | |
| 928 return _map.containsKey(element); | |
| 929 } | |
| 930 | |
| 931 void forEach(void f(E element)) { | |
| 932 LinkedHashMapCell cell = _map._first; | |
| 933 int modifications = _map._modifications; | |
| 934 while (cell != null) { | |
| 935 f(cell._key); | |
| 936 if (modifications != _map._modifications) { | |
| 937 throw new ConcurrentModificationError(_map); | |
| 938 } | |
| 939 cell = cell._next; | |
| 940 } | |
| 941 } | |
| 942 } | |
| 943 | |
| 944 class LinkedHashMapKeyIterator<E> implements Iterator<E> { | |
| 945 final _map; | |
| 946 final int _modifications; | |
| 947 LinkedHashMapCell _cell; | |
| 948 E _current; | |
| 949 | |
| 950 LinkedHashMapKeyIterator(this._map, this._modifications) { | |
| 951 _cell = _map._first; | |
| 952 } | |
| 953 | |
| 954 E get current => _current; | |
| 955 | |
| 956 bool moveNext() { | |
| 957 if (_modifications != _map._modifications) { | |
| 958 throw new ConcurrentModificationError(_map); | |
| 959 } else if (_cell == null) { | |
| 960 _current = null; | |
| 961 return false; | |
| 962 } else { | |
| 963 _current = _cell._key; | |
| 964 _cell = _cell._next; | |
| 965 return true; | |
| 966 } | |
| 967 } | |
| 968 } | |
| 969 | |
| 970 @patch | 762 @patch |
| 971 class HashSet<E> { | 763 class HashSet<E> { |
| 972 @patch | 764 @patch |
| 973 factory HashSet({ bool equals(E e1, E e2), | 765 factory HashSet({ bool equals(E e1, E e2), |
| 974 int hashCode(E e), | 766 int hashCode(E e), |
| 975 bool isValidKey(Object potentialKey) }) { | 767 bool isValidKey(Object potentialKey) }) { |
| 976 if (isValidKey == null) { | 768 if (isValidKey == null) { |
| 977 if (hashCode == null) { | 769 if (hashCode == null) { |
| 978 if (equals == null) { | 770 if (equals == null) { |
| 979 return new _HashSet<E>(); | 771 return new _HashSet<E>(); |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1014 // | 806 // |
| 1015 // [element-0, element-1, element-2, ...] | 807 // [element-0, element-1, element-2, ...] |
| 1016 // | 808 // |
| 1017 // where all elements in the same bucket share the same hash code. | 809 // where all elements in the same bucket share the same hash code. |
| 1018 var _strings; | 810 var _strings; |
| 1019 var _nums; | 811 var _nums; |
| 1020 var _rest; | 812 var _rest; |
| 1021 | 813 |
| 1022 // When iterating over the hash set, it is very convenient to have a | 814 // When iterating over the hash set, it is very convenient to have a |
| 1023 // list of all the elements. We cache that on the instance and clear | 815 // list of all the elements. We cache that on the instance and clear |
| 1024 // the the cache whenever the set changes. This is also used to | 816 // the cache whenever the set changes. This is also used to |
| 1025 // guard against concurrent modifications. | 817 // guard against concurrent modifications. |
| 1026 List _elements; | 818 List/*<E>*/ _elements; |
| 1027 | 819 |
| 1028 _HashSet(); | 820 _HashSet(); |
| 1029 | 821 |
| 1030 Set<E> _newSet() => new _HashSet<E>(); | 822 Set<E> _newSet() => new _HashSet<E>(); |
| 1031 | 823 |
| 1032 // Iterable. | 824 // Iterable. |
| 1033 Iterator<E> get iterator { | 825 Iterator<E> get iterator { |
| 1034 return new HashSetIterator<E>(this, _computeElements()); | 826 return new _HashSetIterator<E>(this, _computeElements()); |
| 1035 } | 827 } |
| 1036 | 828 |
| 1037 int get length => _length; | 829 int get length => _length; |
| 1038 bool get isEmpty => _length == 0; | 830 bool get isEmpty => _length == 0; |
| 1039 bool get isNotEmpty => !isEmpty; | 831 bool get isNotEmpty => !isEmpty; |
| 1040 | 832 |
| 1041 bool contains(Object object) { | 833 bool contains(Object object) { |
| 1042 if (_isStringElement(object)) { | 834 if (_isStringElement(object)) { |
| 1043 var strings = _strings; | 835 var strings = _strings; |
| 1044 return (strings == null) ? false : _hasTableEntry(strings, object); | 836 return (strings == null) ? false : _hasTableEntry(strings, object); |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1137 return true; | 929 return true; |
| 1138 } | 930 } |
| 1139 | 931 |
| 1140 void clear() { | 932 void clear() { |
| 1141 if (_length > 0) { | 933 if (_length > 0) { |
| 1142 _strings = _nums = _rest = _elements = null; | 934 _strings = _nums = _rest = _elements = null; |
| 1143 _length = 0; | 935 _length = 0; |
| 1144 } | 936 } |
| 1145 } | 937 } |
| 1146 | 938 |
| 1147 List _computeElements() { | 939 List/*<E>*/ _computeElements() { |
| 1148 if (_elements != null) return _elements; | 940 if (_elements != null) return _elements; |
| 1149 List result = new List(_length); | 941 List/*<E>*/ result = new List/*<E>*/(_length); |
| 1150 int index = 0; | 942 int index = 0; |
| 1151 | 943 |
| 1152 // Add all string elements to the list. | 944 // Add all string elements to the list. |
| 1153 var strings = _strings; | 945 var strings = _strings; |
| 1154 if (strings != null) { | 946 if (strings != null) { |
| 1155 var names = JS('var', 'Object.getOwnPropertyNames(#)', strings); | 947 var names = JS('var', 'Object.getOwnPropertyNames(#)', strings); |
| 1156 int entries = JS('int', '#.length', names); | 948 int entries = JS('int', '#.length', names); |
| 1157 for (int i = 0; i < entries; i++) { | 949 for (int i = 0; i < entries; i++) { |
| 1158 String element = JS('String', '#[#]', names, i); | 950 String element = JS('String', '#[#]', names, i); |
| 1159 JS('void', '#[#] = #', result, index, element); | 951 JS('void', '#[#] = #', result, index, element); |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1243 | 1035 |
| 1244 static void _setTableEntry(var table, var key, var value) { | 1036 static void _setTableEntry(var table, var key, var value) { |
| 1245 assert(value != null); | 1037 assert(value != null); |
| 1246 JS('void', '#[#] = #', table, key, value); | 1038 JS('void', '#[#] = #', table, key, value); |
| 1247 } | 1039 } |
| 1248 | 1040 |
| 1249 static void _deleteTableEntry(var table, var key) { | 1041 static void _deleteTableEntry(var table, var key) { |
| 1250 JS('void', 'delete #[#]', table, key); | 1042 JS('void', 'delete #[#]', table, key); |
| 1251 } | 1043 } |
| 1252 | 1044 |
| 1253 List _getBucket(var table, var element) { | 1045 List/*<E>*/ _getBucket(var table, var element) { |
| 1254 var hash = _computeHashCode(element); | 1046 var hash = _computeHashCode(element); |
| 1255 return JS('var', '#[#]', table, hash); | 1047 return JS('var', '#[#]', table, hash); |
| 1256 } | 1048 } |
| 1257 | 1049 |
| 1258 int _findBucketIndex(var bucket, var element) { | 1050 int _findBucketIndex(var bucket, var element) { |
| 1259 if (bucket == null) return -1; | 1051 if (bucket == null) return -1; |
| 1260 int length = JS('int', '#.length', bucket); | 1052 int length = JS('int', '#.length', bucket); |
| 1261 for (int i = 0; i < length; i++) { | 1053 for (int i = 0; i < length; i++) { |
| 1262 if (JS('var', '#[#]', bucket, i) == element) return i; | 1054 if (JS('var', '#[#]', bucket, i) == element) return i; |
| 1263 } | 1055 } |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1336 if (!_validKey(object)) return null; | 1128 if (!_validKey(object)) return null; |
| 1337 return super._lookup(object); | 1129 return super._lookup(object); |
| 1338 } | 1130 } |
| 1339 | 1131 |
| 1340 bool remove(Object object) { | 1132 bool remove(Object object) { |
| 1341 if (!_validKey(object)) return false; | 1133 if (!_validKey(object)) return false; |
| 1342 return super._remove(object); | 1134 return super._remove(object); |
| 1343 } | 1135 } |
| 1344 } | 1136 } |
| 1345 | 1137 |
| 1346 // TODO(kasperl): Share this code with HashMapKeyIterator<E>? | 1138 // TODO(kasperl): Share this code with _HashMapKeyIterator<E>? |
| 1347 class HashSetIterator<E> implements Iterator<E> { | 1139 class _HashSetIterator<E> implements Iterator<E> { |
| 1348 final _set; | 1140 final _set; |
| 1349 final List _elements; | 1141 final List/*<E>*/ _elements; |
| 1350 int _offset = 0; | 1142 int _offset = 0; |
| 1351 E _current; | 1143 E _current; |
| 1352 | 1144 |
| 1353 HashSetIterator(this._set, this._elements); | 1145 _HashSetIterator(this._set, this._elements); |
| 1354 | 1146 |
| 1355 E get current => _current; | 1147 E get current => _current; |
| 1356 | 1148 |
| 1357 bool moveNext() { | 1149 bool moveNext() { |
| 1358 var elements = _elements; | 1150 var elements = _elements; |
| 1359 int offset = _offset; | 1151 int offset = _offset; |
| 1360 if (JS('bool', '# !== #', elements, _set._elements)) { | 1152 if (JS('bool', '# !== #', elements, _set._elements)) { |
| 1361 throw new ConcurrentModificationError(_set); | 1153 throw new ConcurrentModificationError(_set); |
| 1362 } else if (offset >= JS('int', '#.length', elements)) { | 1154 } else if (offset >= JS('int', '#.length', elements)) { |
| 1363 _current = null; | 1155 _current = null; |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1420 // | 1212 // |
| 1421 // [cell-0, cell-1, ...] | 1213 // [cell-0, cell-1, ...] |
| 1422 // | 1214 // |
| 1423 // where all elements in the same bucket share the same hash code. | 1215 // where all elements in the same bucket share the same hash code. |
| 1424 var _strings; | 1216 var _strings; |
| 1425 var _nums; | 1217 var _nums; |
| 1426 var _rest; | 1218 var _rest; |
| 1427 | 1219 |
| 1428 // The elements are stored in cells that are linked together | 1220 // The elements are stored in cells that are linked together |
| 1429 // to form a double linked list. | 1221 // to form a double linked list. |
| 1430 LinkedHashSetCell _first; | 1222 _LinkedHashSetCell/*<E>*/ _first; |
| 1431 LinkedHashSetCell _last; | 1223 _LinkedHashSetCell/*<E>*/ _last; |
| 1432 | 1224 |
| 1433 // We track the number of modifications done to the element set to | 1225 // We track the number of modifications done to the element set to |
| 1434 // be able to throw when the set is modified while being iterated | 1226 // be able to throw when the set is modified while being iterated |
| 1435 // over. | 1227 // over. |
| 1436 int _modifications = 0; | 1228 int _modifications = 0; |
| 1437 | 1229 |
| 1438 _LinkedHashSet(); | 1230 _LinkedHashSet(); |
| 1439 | 1231 |
| 1440 Set<E> _newSet() => new _LinkedHashSet<E>(); | 1232 Set<E> _newSet() => new _LinkedHashSet<E>(); |
| 1441 | 1233 |
| 1442 void _unsupported(String operation) { | 1234 void _unsupported(String operation) { |
| 1443 throw 'LinkedHashSet: unsupported $operation'; | 1235 throw 'LinkedHashSet: unsupported $operation'; |
| 1444 } | 1236 } |
| 1445 | 1237 |
| 1446 // Iterable. | 1238 // Iterable. |
| 1447 Iterator<E> get iterator { | 1239 Iterator<E> get iterator { |
| 1448 return new LinkedHashSetIterator(this, _modifications); | 1240 return new _LinkedHashSetIterator(this, _modifications); |
| 1449 } | 1241 } |
| 1450 | 1242 |
| 1451 int get length => _length; | 1243 int get length => _length; |
| 1452 bool get isEmpty => _length == 0; | 1244 bool get isEmpty => _length == 0; |
| 1453 bool get isNotEmpty => !isEmpty; | 1245 bool get isNotEmpty => !isEmpty; |
| 1454 | 1246 |
| 1455 bool contains(Object object) { | 1247 bool contains(Object object) { |
| 1456 if (_isStringElement(object)) { | 1248 if (_isStringElement(object)) { |
| 1457 var strings = _strings; | 1249 var strings = _strings; |
| 1458 if (strings == null) return false; | 1250 if (strings == null) return false; |
| 1459 LinkedHashSetCell cell = _getTableEntry(strings, object); | 1251 _LinkedHashSetCell/*<E>*/ cell = _getTableEntry(strings, object); |
| 1460 return cell != null; | 1252 return cell != null; |
| 1461 } else if (_isNumericElement(object)) { | 1253 } else if (_isNumericElement(object)) { |
| 1462 var nums = _nums; | 1254 var nums = _nums; |
| 1463 if (nums == null) return false; | 1255 if (nums == null) return false; |
| 1464 LinkedHashSetCell cell = _getTableEntry(nums, object); | 1256 _LinkedHashSetCell/*<E>*/ cell = _getTableEntry(nums, object); |
| 1465 return cell != null; | 1257 return cell != null; |
| 1466 } else { | 1258 } else { |
| 1467 return _contains(object); | 1259 return _contains(object); |
| 1468 } | 1260 } |
| 1469 } | 1261 } |
| 1470 | 1262 |
| 1471 bool _contains(Object object) { | 1263 bool _contains(Object object) { |
| 1472 var rest = _rest; | 1264 var rest = _rest; |
| 1473 if (rest == null) return false; | 1265 if (rest == null) return false; |
| 1474 var bucket = _getBucket(rest, object); | 1266 var bucket = _getBucket(rest, object); |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 1486 E _lookup(Object object) { | 1278 E _lookup(Object object) { |
| 1487 var rest = _rest; | 1279 var rest = _rest; |
| 1488 if (rest == null) return null; | 1280 if (rest == null) return null; |
| 1489 var bucket = _getBucket(rest, object); | 1281 var bucket = _getBucket(rest, object); |
| 1490 var index = _findBucketIndex(bucket, object); | 1282 var index = _findBucketIndex(bucket, object); |
| 1491 if (index < 0) return null; | 1283 if (index < 0) return null; |
| 1492 return bucket[index]._element; | 1284 return bucket[index]._element; |
| 1493 } | 1285 } |
| 1494 | 1286 |
| 1495 void forEach(void action(E element)) { | 1287 void forEach(void action(E element)) { |
| 1496 LinkedHashSetCell cell = _first; | 1288 _LinkedHashSetCell/*<E>*/ cell = _first; |
| 1497 int modifications = _modifications; | 1289 int modifications = _modifications; |
| 1498 while (cell != null) { | 1290 while (cell != null) { |
| 1499 action(cell._element); | 1291 action(cell._element); |
| 1500 if (modifications != _modifications) { | 1292 if (modifications != _modifications) { |
| 1501 throw new ConcurrentModificationError(this); | 1293 throw new ConcurrentModificationError(this); |
| 1502 } | 1294 } |
| 1503 cell = cell._next; | 1295 cell = cell._next; |
| 1504 } | 1296 } |
| 1505 } | 1297 } |
| 1506 | 1298 |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 1528 return _add(element); | 1320 return _add(element); |
| 1529 } | 1321 } |
| 1530 } | 1322 } |
| 1531 | 1323 |
| 1532 bool _add(E element) { | 1324 bool _add(E element) { |
| 1533 var rest = _rest; | 1325 var rest = _rest; |
| 1534 if (rest == null) _rest = rest = _newHashTable(); | 1326 if (rest == null) _rest = rest = _newHashTable(); |
| 1535 var hash = _computeHashCode(element); | 1327 var hash = _computeHashCode(element); |
| 1536 var bucket = JS('var', '#[#]', rest, hash); | 1328 var bucket = JS('var', '#[#]', rest, hash); |
| 1537 if (bucket == null) { | 1329 if (bucket == null) { |
| 1538 LinkedHashSetCell cell = _newLinkedCell(element); | 1330 _LinkedHashSetCell/*<E>*/ cell = _newLinkedCell(element); |
| 1539 _setTableEntry(rest, hash, JS('var', '[#]', cell)); | 1331 _setTableEntry(rest, hash, JS('var', '[#]', cell)); |
| 1540 } else { | 1332 } else { |
| 1541 int index = _findBucketIndex(bucket, element); | 1333 int index = _findBucketIndex(bucket, element); |
| 1542 if (index >= 0) return false; | 1334 if (index >= 0) return false; |
| 1543 LinkedHashSetCell cell = _newLinkedCell(element); | 1335 _LinkedHashSetCell/*<E>*/ cell = _newLinkedCell(element); |
| 1544 JS('void', '#.push(#)', bucket, cell); | 1336 JS('void', '#.push(#)', bucket, cell); |
| 1545 } | 1337 } |
| 1546 return true; | 1338 return true; |
| 1547 } | 1339 } |
| 1548 | 1340 |
| 1549 bool remove(Object object) { | 1341 bool remove(Object object) { |
| 1550 if (_isStringElement(object)) { | 1342 if (_isStringElement(object)) { |
| 1551 return _removeHashTableEntry(_strings, object); | 1343 return _removeHashTableEntry(_strings, object); |
| 1552 } else if (_isNumericElement(object)) { | 1344 } else if (_isNumericElement(object)) { |
| 1553 return _removeHashTableEntry(_nums, object); | 1345 return _removeHashTableEntry(_nums, object); |
| 1554 } else { | 1346 } else { |
| 1555 return _remove(object); | 1347 return _remove(object); |
| 1556 } | 1348 } |
| 1557 } | 1349 } |
| 1558 | 1350 |
| 1559 bool _remove(Object object) { | 1351 bool _remove(Object object) { |
| 1560 var rest = _rest; | 1352 var rest = _rest; |
| 1561 if (rest == null) return false; | 1353 if (rest == null) return false; |
| 1562 var bucket = _getBucket(rest, object); | 1354 var bucket = _getBucket(rest, object); |
| 1563 int index = _findBucketIndex(bucket, object); | 1355 int index = _findBucketIndex(bucket, object); |
| 1564 if (index < 0) return false; | 1356 if (index < 0) return false; |
| 1565 // Use splice to remove the [cell] element at the index and | 1357 // Use splice to remove the [cell] element at the index and |
| 1566 // unlink it. | 1358 // unlink it. |
| 1567 LinkedHashSetCell cell = JS('var', '#.splice(#, 1)[0]', bucket, index); | 1359 _LinkedHashSetCell/*<E>*/ cell = JS('var', '#.splice(#, 1)[0]', bucket, inde x); |
| 1568 _unlinkCell(cell); | 1360 _unlinkCell(cell); |
| 1569 return true; | 1361 return true; |
| 1570 } | 1362 } |
| 1571 | 1363 |
| 1572 void removeWhere(bool test(E element)) { | 1364 void removeWhere(bool test(E element)) { |
| 1573 _filterWhere(test, true); | 1365 _filterWhere(test, true); |
| 1574 } | 1366 } |
| 1575 | 1367 |
| 1576 void retainWhere(bool test(E element)) { | 1368 void retainWhere(bool test(E element)) { |
| 1577 _filterWhere(test, false); | 1369 _filterWhere(test, false); |
| 1578 } | 1370 } |
| 1579 | 1371 |
| 1580 void _filterWhere(bool test(E element), bool removeMatching) { | 1372 void _filterWhere(bool test(E element), bool removeMatching) { |
| 1581 LinkedHashSetCell cell = _first; | 1373 _LinkedHashSetCell/*<E>*/ cell = _first; |
| 1582 while (cell != null) { | 1374 while (cell != null) { |
| 1583 E element = cell._element; | 1375 E element = cell._element; |
| 1584 LinkedHashSetCell next = cell._next; | 1376 _LinkedHashSetCell/*<E>*/ next = cell._next; |
| 1585 int modifications = _modifications; | 1377 int modifications = _modifications; |
| 1586 bool shouldRemove = (removeMatching == test(element)); | 1378 bool shouldRemove = (removeMatching == test(element)); |
| 1587 if (modifications != _modifications) { | 1379 if (modifications != _modifications) { |
| 1588 throw new ConcurrentModificationError(this); | 1380 throw new ConcurrentModificationError(this); |
| 1589 } | 1381 } |
| 1590 if (shouldRemove) remove(element); | 1382 if (shouldRemove) remove(element); |
| 1591 cell = next; | 1383 cell = next; |
| 1592 } | 1384 } |
| 1593 } | 1385 } |
| 1594 | 1386 |
| 1595 void clear() { | 1387 void clear() { |
| 1596 if (_length > 0) { | 1388 if (_length > 0) { |
| 1597 _strings = _nums = _rest = _first = _last = null; | 1389 _strings = _nums = _rest = _first = _last = null; |
| 1598 _length = 0; | 1390 _length = 0; |
| 1599 _modified(); | 1391 _modified(); |
| 1600 } | 1392 } |
| 1601 } | 1393 } |
| 1602 | 1394 |
| 1603 bool _addHashTableEntry(var table, E element) { | 1395 bool _addHashTableEntry(var table, E element) { |
| 1604 LinkedHashSetCell cell = _getTableEntry(table, element); | 1396 _LinkedHashSetCell/*<E>*/ cell = _getTableEntry(table, element); |
| 1605 if (cell != null) return false; | 1397 if (cell != null) return false; |
| 1606 _setTableEntry(table, element, _newLinkedCell(element)); | 1398 _setTableEntry(table, element, _newLinkedCell(element)); |
| 1607 return true; | 1399 return true; |
| 1608 } | 1400 } |
| 1609 | 1401 |
| 1610 bool _removeHashTableEntry(var table, Object element) { | 1402 bool _removeHashTableEntry(var table, Object element) { |
| 1611 if (table == null) return false; | 1403 if (table == null) return false; |
| 1612 LinkedHashSetCell cell = _getTableEntry(table, element); | 1404 _LinkedHashSetCell/*<E>*/ cell = _getTableEntry(table, element); |
| 1613 if (cell == null) return false; | 1405 if (cell == null) return false; |
| 1614 _unlinkCell(cell); | 1406 _unlinkCell(cell); |
| 1615 _deleteTableEntry(table, element); | 1407 _deleteTableEntry(table, element); |
| 1616 return true; | 1408 return true; |
| 1617 } | 1409 } |
| 1618 | 1410 |
| 1619 void _modified() { | 1411 void _modified() { |
| 1620 // Value cycles after 2^30 modifications. If you keep hold of an | 1412 // Value cycles after 2^30 modifications. If you keep hold of an |
| 1621 // iterator for that long, you might miss a modification | 1413 // iterator for that long, you might miss a modification |
| 1622 // detection, and iteration can go sour. Don't do that. | 1414 // detection, and iteration can go sour. Don't do that. |
| 1623 _modifications = (_modifications + 1) & 0x3ffffff; | 1415 _modifications = (_modifications + 1) & 0x3ffffff; |
| 1624 } | 1416 } |
| 1625 | 1417 |
| 1626 // Create a new cell and link it in as the last one in the list. | 1418 // Create a new cell and link it in as the last one in the list. |
| 1627 LinkedHashSetCell _newLinkedCell(E element) { | 1419 _LinkedHashSetCell/*<E>*/ _newLinkedCell(E element) { |
| 1628 LinkedHashSetCell cell = new LinkedHashSetCell(element); | 1420 _LinkedHashSetCell/*<E>*/ cell = new _LinkedHashSetCell/*<E>*/(element); |
| 1629 if (_first == null) { | 1421 if (_first == null) { |
| 1630 _first = _last = cell; | 1422 _first = _last = cell; |
| 1631 } else { | 1423 } else { |
| 1632 LinkedHashSetCell last = _last; | 1424 _LinkedHashSetCell/*<E>*/ last = _last; |
| 1633 cell._previous = last; | 1425 cell._previous = last; |
| 1634 _last = last._next = cell; | 1426 _last = last._next = cell; |
| 1635 } | 1427 } |
| 1636 _length++; | 1428 _length++; |
| 1637 _modified(); | 1429 _modified(); |
| 1638 return cell; | 1430 return cell; |
| 1639 } | 1431 } |
| 1640 | 1432 |
| 1641 // Unlink the given cell from the linked list of cells. | 1433 // Unlink the given cell from the linked list of cells. |
| 1642 void _unlinkCell(LinkedHashSetCell cell) { | 1434 void _unlinkCell(_LinkedHashSetCell/*<E>*/ cell) { |
| 1643 LinkedHashSetCell previous = cell._previous; | 1435 _LinkedHashSetCell/*<E>*/ previous = cell._previous; |
| 1644 LinkedHashSetCell next = cell._next; | 1436 _LinkedHashSetCell/*<E>*/ next = cell._next; |
| 1645 if (previous == null) { | 1437 if (previous == null) { |
| 1646 assert(cell == _first); | 1438 assert(cell == _first); |
| 1647 _first = next; | 1439 _first = next; |
| 1648 } else { | 1440 } else { |
| 1649 previous._next = next; | 1441 previous._next = next; |
| 1650 } | 1442 } |
| 1651 if (next == null) { | 1443 if (next == null) { |
| 1652 assert(cell == _last); | 1444 assert(cell == _last); |
| 1653 _last = previous; | 1445 _last = previous; |
| 1654 } else { | 1446 } else { |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 1671 } | 1463 } |
| 1672 | 1464 |
| 1673 int _computeHashCode(var element) { | 1465 int _computeHashCode(var element) { |
| 1674 // We force the hash codes to be unsigned 30-bit integers to avoid | 1466 // We force the hash codes to be unsigned 30-bit integers to avoid |
| 1675 // issues with problematic elements like '__proto__'. Another | 1467 // issues with problematic elements like '__proto__'. Another |
| 1676 // option would be to throw an exception if the hash code isn't a | 1468 // option would be to throw an exception if the hash code isn't a |
| 1677 // number. | 1469 // number. |
| 1678 return JS('int', '# & 0x3ffffff', element.hashCode); | 1470 return JS('int', '# & 0x3ffffff', element.hashCode); |
| 1679 } | 1471 } |
| 1680 | 1472 |
| 1681 static _getTableEntry(var table, var key) { | 1473 _LinkedHashSetCell/*<E>*/ _getTableEntry(var table, var key) { |
| 1682 return JS('var', '#[#]', table, key); | 1474 return JS('var', '#[#]', table, key); |
| 1683 } | 1475 } |
| 1684 | 1476 |
| 1685 static void _setTableEntry(var table, var key, var value) { | 1477 static void _setTableEntry(var table, var key, var value) { |
| 1686 assert(value != null); | 1478 assert(value != null); |
| 1687 JS('void', '#[#] = #', table, key, value); | 1479 JS('void', '#[#] = #', table, key, value); |
| 1688 } | 1480 } |
| 1689 | 1481 |
| 1690 static void _deleteTableEntry(var table, var key) { | 1482 static void _deleteTableEntry(var table, var key) { |
| 1691 JS('void', 'delete #[#]', table, key); | 1483 JS('void', 'delete #[#]', table, key); |
| 1692 } | 1484 } |
| 1693 | 1485 |
| 1694 List _getBucket(var table, var element) { | 1486 List/*<_LinkedHashSetCell<E>>*/ _getBucket(var table, var element) { |
| 1695 var hash = _computeHashCode(element); | 1487 var hash = _computeHashCode(element); |
| 1696 return JS('var', '#[#]', table, hash); | 1488 return JS('var', '#[#]', table, hash); |
| 1697 } | 1489 } |
| 1698 | 1490 |
| 1699 int _findBucketIndex(var bucket, var element) { | 1491 int _findBucketIndex(var bucket, var element) { |
| 1700 if (bucket == null) return -1; | 1492 if (bucket == null) return -1; |
| 1701 int length = JS('int', '#.length', bucket); | 1493 int length = JS('int', '#.length', bucket); |
| 1702 for (int i = 0; i < length; i++) { | 1494 for (int i = 0; i < length; i++) { |
| 1703 LinkedHashSetCell cell = JS('var', '#[#]', bucket, i); | 1495 _LinkedHashSetCell/*<E>*/ cell = JS('var', '#[#]', bucket, i); |
| 1704 if (cell._element == element) return i; | 1496 if (cell._element == element) return i; |
| 1705 } | 1497 } |
| 1706 return -1; | 1498 return -1; |
| 1707 } | 1499 } |
| 1708 | 1500 |
| 1709 static _newHashTable() { | 1501 static _newHashTable() { |
| 1710 // Create a new JavaScript object to be used as a hash table. Use | 1502 // Create a new JavaScript object to be used as a hash table. Use |
| 1711 // Object.create to avoid the properties on Object.prototype | 1503 // Object.create to avoid the properties on Object.prototype |
| 1712 // showing up as entries. | 1504 // showing up as entries. |
| 1713 var table = JS('var', 'Object.create(null)'); | 1505 var table = JS('var', 'Object.create(null)'); |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 1727 // We force the hash codes to be unsigned 30-bit integers to avoid | 1519 // We force the hash codes to be unsigned 30-bit integers to avoid |
| 1728 // issues with problematic keys like '__proto__'. Another option | 1520 // issues with problematic keys like '__proto__'. Another option |
| 1729 // would be to throw an exception if the hash code isn't a number. | 1521 // would be to throw an exception if the hash code isn't a number. |
| 1730 return JS('int', '# & 0x3ffffff', identityHashCode(key)); | 1522 return JS('int', '# & 0x3ffffff', identityHashCode(key)); |
| 1731 } | 1523 } |
| 1732 | 1524 |
| 1733 int _findBucketIndex(var bucket, var element) { | 1525 int _findBucketIndex(var bucket, var element) { |
| 1734 if (bucket == null) return -1; | 1526 if (bucket == null) return -1; |
| 1735 int length = JS('int', '#.length', bucket); | 1527 int length = JS('int', '#.length', bucket); |
| 1736 for (int i = 0; i < length; i++) { | 1528 for (int i = 0; i < length; i++) { |
| 1737 LinkedHashSetCell cell = JS('var', '#[#]', bucket, i); | 1529 _LinkedHashSetCell/*<E>*/ cell = JS('var', '#[#]', bucket, i); |
| 1738 if (identical(cell._element, element)) return i; | 1530 if (identical(cell._element, element)) return i; |
| 1739 } | 1531 } |
| 1740 return -1; | 1532 return -1; |
| 1741 } | 1533 } |
| 1742 } | 1534 } |
| 1743 | 1535 |
| 1744 class _LinkedCustomHashSet<E> extends _LinkedHashSet<E> { | 1536 class _LinkedCustomHashSet<E> extends _LinkedHashSet<E> { |
| 1745 _Equality<E> _equality; | 1537 _Equality<E> _equality; |
| 1746 _Hasher<E> _hasher; | 1538 _Hasher<E> _hasher; |
| 1747 _Predicate<Object> _validKey; | 1539 _Predicate<Object> _validKey; |
| 1748 _LinkedCustomHashSet(this._equality, this._hasher, | 1540 _LinkedCustomHashSet(this._equality, this._hasher, |
| 1749 bool validKey(Object potentialKey)) | 1541 bool validKey(Object potentialKey)) |
| 1750 : _validKey = (validKey != null) ? validKey : ((x) => x is E); | 1542 : _validKey = (validKey != null) ? validKey : ((x) => x is E); |
| 1751 | 1543 |
| 1752 Set<E> _newSet() => | 1544 Set<E> _newSet() => |
| 1753 new _LinkedCustomHashSet<E>(_equality, _hasher, _validKey); | 1545 new _LinkedCustomHashSet<E>(_equality, _hasher, _validKey); |
| 1754 | 1546 |
| 1755 int _findBucketIndex(var bucket, var element) { | 1547 int _findBucketIndex(var bucket, var element) { |
| 1756 if (bucket == null) return -1; | 1548 if (bucket == null) return -1; |
| 1757 int length = JS('int', '#.length', bucket); | 1549 int length = JS('int', '#.length', bucket); |
| 1758 for (int i = 0; i < length; i++) { | 1550 for (int i = 0; i < length; i++) { |
| 1759 LinkedHashSetCell cell = JS('var', '#[#]', bucket, i); | 1551 _LinkedHashSetCell/*<E>*/ cell = JS('var', '#[#]', bucket, i); |
| 1760 if (_equality(cell._element, element)) return i; | 1552 if (_equality(cell._element, element)) return i; |
| 1761 } | 1553 } |
| 1762 return -1; | 1554 return -1; |
| 1763 } | 1555 } |
| 1764 | 1556 |
| 1765 int _computeHashCode(var element) { | 1557 int _computeHashCode(var element) { |
| 1766 // We force the hash codes to be unsigned 30-bit integers to avoid | 1558 // We force the hash codes to be unsigned 30-bit integers to avoid |
| 1767 // issues with problematic elements like '__proto__'. Another | 1559 // issues with problematic elements like '__proto__'. Another |
| 1768 // option would be to throw an exception if the hash code isn't a | 1560 // option would be to throw an exception if the hash code isn't a |
| 1769 // number. | 1561 // number. |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 1796 | 1588 |
| 1797 void removeAll(Iterable<Object> elements) { | 1589 void removeAll(Iterable<Object> elements) { |
| 1798 for (Object element in elements) { | 1590 for (Object element in elements) { |
| 1799 if (_validKey(element)) { | 1591 if (_validKey(element)) { |
| 1800 super._remove(element); | 1592 super._remove(element); |
| 1801 } | 1593 } |
| 1802 } | 1594 } |
| 1803 } | 1595 } |
| 1804 } | 1596 } |
| 1805 | 1597 |
| 1806 class LinkedHashSetCell { | 1598 class _LinkedHashSetCell<E> { |
| 1807 final _element; | 1599 E _element; |
| 1808 | 1600 |
| 1809 LinkedHashSetCell _next; | 1601 _LinkedHashSetCell<E> _next; |
| 1810 LinkedHashSetCell _previous; | 1602 _LinkedHashSetCell<E> _previous; |
| 1811 | 1603 |
| 1812 LinkedHashSetCell(this._element); | 1604 _LinkedHashSetCell(this._element); |
| 1813 } | 1605 } |
| 1814 | 1606 |
| 1815 // TODO(kasperl): Share this code with LinkedHashMapKeyIterator<E>? | 1607 // TODO(kasperl): Share this code with LinkedHashMapKeyIterator<E>? |
| 1816 class LinkedHashSetIterator<E> implements Iterator<E> { | 1608 class _LinkedHashSetIterator<E> implements Iterator<E> { |
| 1817 final _set; | 1609 final _set; |
| 1818 final int _modifications; | 1610 final int _modifications; |
| 1819 LinkedHashSetCell _cell; | 1611 _LinkedHashSetCell _cell; |
| 1820 E _current; | 1612 E _current; |
| 1821 | 1613 |
| 1822 LinkedHashSetIterator(this._set, this._modifications) { | 1614 _LinkedHashSetIterator(this._set, this._modifications) { |
| 1823 _cell = _set._first; | 1615 _cell = _set._first; |
| 1824 } | 1616 } |
| 1825 | 1617 |
| 1826 E get current => _current; | 1618 E get current => _current; |
| 1827 | 1619 |
| 1828 bool moveNext() { | 1620 bool moveNext() { |
| 1829 if (_modifications != _set._modifications) { | 1621 if (_modifications != _set._modifications) { |
| 1830 throw new ConcurrentModificationError(_set); | 1622 throw new ConcurrentModificationError(_set); |
| 1831 } else if (_cell == null) { | 1623 } else if (_cell == null) { |
| 1832 _current = null; | 1624 _current = null; |
| 1833 return false; | 1625 return false; |
| 1834 } else { | 1626 } else { |
| 1835 _current = _cell._element; | 1627 _current = _cell._element; |
| 1836 _cell = _cell._next; | 1628 _cell = _cell._next; |
| 1837 return true; | 1629 return true; |
| 1838 } | 1630 } |
| 1839 } | 1631 } |
| 1840 } | 1632 } |
| OLD | NEW |