Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(348)

Side by Side Diff: tool/input_sdk/patch/collection_patch.dart

Issue 1987043002: Updates to collection_patch.dart (Closed) Base URL: https://github.com/dart-lang/dev_compiler@master
Patch Set: Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « tool/input_sdk/lib/collection/linked_hash_set.dart ('k') | tool/sdk_expected_errors.txt » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « tool/input_sdk/lib/collection/linked_hash_set.dart ('k') | tool/sdk_expected_errors.txt » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698