| 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 | 7 |
| 8 patch class HashMap<K, V> { | 8 patch class HashMap<K, V> { |
| 9 patch factory HashMap({ bool equals(K key1, K key2), int hashCode(K key) }) { | 9 patch factory HashMap({ bool equals(K key1, K key2), int hashCode(K key) }) { |
| 10 if (hashCode == null) { | 10 if (hashCode == null) { |
| 11 if (equals == null) { | 11 if (equals == null) { |
| 12 return new _HashMapImpl<K, V>(); | 12 return new _Hash<K, V>(); |
| 13 } | 13 } |
| 14 if (identical(identical, equals)) { | 14 if (identical(identical, equals)) { |
| 15 return new _IdentityHashMap<K, V>(); | 15 return new _IdentityHashMap<K, V>(); |
| 16 } | 16 } |
| 17 hashCode = _defaultHashCode; | 17 hashCode = _defaultHashCode; |
| 18 } else if (equals == null) { | 18 } else if (equals == null) { |
| 19 equals = _defaultEquals; | 19 equals = _defaultEquals; |
| 20 } | 20 } |
| 21 return new _CustomHashMap<K, V>(equals, hashCode); | 21 return new _CustomHashMap<K, V>(equals, hashCode); |
| 22 } | 22 } |
| 23 } | 23 } |
| 24 | 24 |
| 25 class _HashMapImpl<K, V> implements HashMap<K, V> { | 25 class _Hash<K, V> implements HashMap<K, V> { |
| 26 int _length = 0; | 26 int _length = 0; |
| 27 | 27 |
| 28 // The hash map contents are divided into three parts: one part for | 28 // The hash map contents are divided into three parts: one part for |
| 29 // string keys, one for numeric keys, and one for the rest. String | 29 // string keys, one for numeric keys, and one for the rest. String |
| 30 // and numeric keys map directly to their values, but the rest of | 30 // and numeric keys map directly to their values, but the rest of |
| 31 // the entries are stored in bucket lists of the form: | 31 // the entries are stored in bucket lists of the form: |
| 32 // | 32 // |
| 33 // [key-0, value-0, key-1, value-1, ...] | 33 // [key-0, value-0, key-1, value-1, ...] |
| 34 // | 34 // |
| 35 // where all keys in the same bucket share the same hash code. | 35 // where all keys in the same bucket share the same hash code. |
| 36 var _strings; | 36 var _strings; |
| 37 var _nums; | 37 var _nums; |
| 38 var _rest; | 38 var _rest; |
| 39 | 39 |
| 40 // When iterating over the hash map, it is very convenient to have a | 40 // When iterating over the hash map, it is very convenient to have a |
| 41 // list of all the keys. We cache that on the instance and clear the | 41 // list of all the keys. We cache that on the instance and clear the |
| 42 // the cache whenever the key set changes. This is also used to | 42 // the cache whenever the key set changes. This is also used to |
| 43 // guard against concurrent modifications. | 43 // guard against concurrent modifications. |
| 44 List _keys; | 44 List _keys; |
| 45 | 45 |
| 46 _HashMapImpl(); | 46 _Hash(); |
| 47 | 47 |
| 48 int get length => _length; | 48 int get length => _length; |
| 49 bool get isEmpty => _length == 0; | 49 bool get isEmpty => _length == 0; |
| 50 bool get isNotEmpty => !isEmpty; | 50 bool get isNotEmpty => !isEmpty; |
| 51 | 51 |
| 52 Iterable<K> get keys { | 52 Iterable<K> get keys { |
| 53 return new HashMapKeyIterable<K>(this); | 53 return new HashMapKeyIterable<K>(this); |
| 54 } | 54 } |
| 55 | 55 |
| 56 Iterable<V> get values { | 56 Iterable<V> get values { |
| (...skipping 261 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 318 var table = JS('var', 'Object.create(null)'); | 318 var table = JS('var', 'Object.create(null)'); |
| 319 // Attempt to force the hash table into 'dictionary' mode by | 319 // Attempt to force the hash table into 'dictionary' mode by |
| 320 // adding a property to it and deleting it again. | 320 // adding a property to it and deleting it again. |
| 321 var temporaryKey = '<non-identifier-key>'; | 321 var temporaryKey = '<non-identifier-key>'; |
| 322 _setTableEntry(table, temporaryKey, table); | 322 _setTableEntry(table, temporaryKey, table); |
| 323 _deleteTableEntry(table, temporaryKey); | 323 _deleteTableEntry(table, temporaryKey); |
| 324 return table; | 324 return table; |
| 325 } | 325 } |
| 326 } | 326 } |
| 327 | 327 |
| 328 class _IdentityHashMap<K, V> extends _HashMapImpl<K, V> { | 328 class _IdentityHashMap<K, V> extends _Hash<K, V> { |
| 329 int _findBucketIndex(var bucket, var key) { | 329 int _findBucketIndex(var bucket, var key) { |
| 330 if (bucket == null) return -1; | 330 if (bucket == null) return -1; |
| 331 int length = JS('int', '#.length', bucket); | 331 int length = JS('int', '#.length', bucket); |
| 332 for (int i = 0; i < length; i += 2) { | 332 for (int i = 0; i < length; i += 2) { |
| 333 if (identical(JS('var', '#[#]', bucket, i), key)) return i; | 333 if (identical(JS('var', '#[#]', bucket, i), key)) return i; |
| 334 } | 334 } |
| 335 return -1; | 335 return -1; |
| 336 } | 336 } |
| 337 } | 337 } |
| 338 | 338 |
| 339 class _CustomHashMap<K, V> extends _HashMapImpl<K, V> { | 339 class _CustomHashMap<K, V> extends _Hash<K, V> { |
| 340 final _Equality<K> _equals; | 340 final _Equality<K> _equals; |
| 341 final _Hasher<K> _hashCode; | 341 final _Hasher<K> _hashCode; |
| 342 _CustomHashMap(this._equals, this._hashCode); | 342 _CustomHashMap(this._equals, this._hashCode); |
| 343 | 343 |
| 344 int _computeHashCode(var key) { | 344 int _computeHashCode(var key) { |
| 345 // We force the hash codes to be unsigned 30-bit integers to avoid | 345 // We force the hash codes to be unsigned 30-bit integers to avoid |
| 346 // issues with problematic keys like '__proto__'. Another option | 346 // issues with problematic keys like '__proto__'. Another option |
| 347 // would be to throw an exception if the hash code isn't a number. | 347 // would be to throw an exception if the hash code isn't a number. |
| 348 return JS('int', '# & 0x3ffffff', _hashCode(key)); | 348 return JS('int', '# & 0x3ffffff', _hashCode(key)); |
| 349 } | 349 } |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 409 // TODO(kasperl): For now, we have to tell the type inferrer to | 409 // TODO(kasperl): For now, we have to tell the type inferrer to |
| 410 // treat the result of doing offset + 1 as an int. Otherwise, we | 410 // treat the result of doing offset + 1 as an int. Otherwise, we |
| 411 // get unnecessary bailout code. | 411 // get unnecessary bailout code. |
| 412 _offset = JS('int', '#', offset + 1); | 412 _offset = JS('int', '#', offset + 1); |
| 413 return true; | 413 return true; |
| 414 } | 414 } |
| 415 } | 415 } |
| 416 } | 416 } |
| 417 | 417 |
| 418 patch class LinkedHashMap<K, V> { | 418 patch class LinkedHashMap<K, V> { |
| 419 patch factory LinkedHashMap({ bool equals(K key1, K key2), |
| 420 int hashCode(K key) }) { |
| 421 if (hashCode == null) { |
| 422 if (equals == null) { |
| 423 return new _LinkedHash<K, V>(); |
| 424 } |
| 425 if (identical(identical, equals)) { |
| 426 return new _LinkedIdentityHashMap<K, V>(); |
| 427 } |
| 428 hashCode = _defaultHashCode; |
| 429 } else if (equals == null) { |
| 430 equals = _defaultEquals; |
| 431 } |
| 432 return new _LinkedCustomHashMap<K, V>(equals, hashCode); |
| 433 } |
| 434 } |
| 435 |
| 436 class _LinkedHash<K, V> implements LinkedHashMap<K, V> { |
| 419 int _length = 0; | 437 int _length = 0; |
| 420 | 438 |
| 421 // The hash map contents are divided into three parts: one part for | 439 // The hash map contents are divided into three parts: one part for |
| 422 // string keys, one for numeric keys, and one for the rest. String | 440 // string keys, one for numeric keys, and one for the rest. String |
| 423 // and numeric keys map directly to their linked cells, but the rest | 441 // and numeric keys map directly to their linked cells, but the rest |
| 424 // of the entries are stored in bucket lists of the form: | 442 // of the entries are stored in bucket lists of the form: |
| 425 // | 443 // |
| 426 // [cell-0, cell-1, ...] | 444 // [cell-0, cell-1, ...] |
| 427 // | 445 // |
| 428 // where all keys in the same bucket share the same hash code. | 446 // where all keys in the same bucket share the same hash code. |
| 429 var _strings; | 447 var _strings; |
| 430 var _nums; | 448 var _nums; |
| 431 var _rest; | 449 var _rest; |
| 432 | 450 |
| 433 // The keys and values are stored in cells that are linked together | 451 // The keys and values are stored in cells that are linked together |
| 434 // to form a double linked list. | 452 // to form a double linked list. |
| 435 LinkedHashMapCell _first; | 453 LinkedHashMapCell _first; |
| 436 LinkedHashMapCell _last; | 454 LinkedHashMapCell _last; |
| 437 | 455 |
| 438 // We track the number of modifications done to the key set of the | 456 // We track the number of modifications done to the key set of the |
| 439 // hash map to be able to throw when the map is modified while being | 457 // hash map to be able to throw when the map is modified while being |
| 440 // iterated over. | 458 // iterated over. |
| 441 int _modifications = 0; | 459 int _modifications = 0; |
| 442 | 460 |
| 443 patch LinkedHashMap(); | 461 _LinkedHash(); |
| 444 | 462 |
| 445 patch int get length => _length; | 463 int get length => _length; |
| 446 patch bool get isEmpty => _length == 0; | 464 bool get isEmpty => _length == 0; |
| 447 patch bool get isNotEmpty => !isEmpty; | 465 bool get isNotEmpty => !isEmpty; |
| 448 | 466 |
| 449 | 467 |
| 450 patch Iterable<K> get keys { | 468 Iterable<K> get keys { |
| 451 return new LinkedHashMapKeyIterable<K>(this); | 469 return new LinkedHashMapKeyIterable<K>(this); |
| 452 } | 470 } |
| 453 | 471 |
| 454 patch Iterable<V> get values { | 472 Iterable<V> get values { |
| 455 return keys.map((each) => this[each]); | 473 return keys.map((each) => this[each]); |
| 456 } | 474 } |
| 457 | 475 |
| 458 patch bool containsKey(Object key) { | 476 bool containsKey(Object key) { |
| 459 if (_isStringKey(key)) { | 477 if (_isStringKey(key)) { |
| 460 var strings = _strings; | 478 var strings = _strings; |
| 461 if (strings == null) return false; | 479 if (strings == null) return false; |
| 462 LinkedHashMapCell cell = _getTableEntry(strings, key); | 480 LinkedHashMapCell cell = _getTableEntry(strings, key); |
| 463 return cell != null; | 481 return cell != null; |
| 464 } else if (_isNumericKey(key)) { | 482 } else if (_isNumericKey(key)) { |
| 465 var nums = _nums; | 483 var nums = _nums; |
| 466 if (nums == null) return false; | 484 if (nums == null) return false; |
| 467 LinkedHashMapCell cell = _getTableEntry(nums, key); | 485 LinkedHashMapCell cell = _getTableEntry(nums, key); |
| 468 return cell != null; | 486 return cell != null; |
| 469 } else { | 487 } else { |
| 470 var rest = _rest; | 488 var rest = _rest; |
| 471 if (rest == null) return false; | 489 if (rest == null) return false; |
| 472 var bucket = _getBucket(rest, key); | 490 var bucket = _getBucket(rest, key); |
| 473 return _findBucketIndex(bucket, key) >= 0; | 491 return _findBucketIndex(bucket, key) >= 0; |
| 474 } | 492 } |
| 475 } | 493 } |
| 476 | 494 |
| 477 patch bool containsValue(Object value) { | 495 bool containsValue(Object value) { |
| 478 return keys.any((each) => this[each] == value); | 496 return keys.any((each) => this[each] == value); |
| 479 } | 497 } |
| 480 | 498 |
| 481 patch void addAll(Map<K, V> other) { | 499 void addAll(Map<K, V> other) { |
| 482 other.forEach((K key, V value) { | 500 other.forEach((K key, V value) { |
| 483 this[key] = value; | 501 this[key] = value; |
| 484 }); | 502 }); |
| 485 } | 503 } |
| 486 | 504 |
| 487 patch V operator[](Object key) { | 505 V operator[](Object key) { |
| 488 if (_isStringKey(key)) { | 506 if (_isStringKey(key)) { |
| 489 var strings = _strings; | 507 var strings = _strings; |
| 490 if (strings == null) return null; | 508 if (strings == null) return null; |
| 491 LinkedHashMapCell cell = _getTableEntry(strings, key); | 509 LinkedHashMapCell cell = _getTableEntry(strings, key); |
| 492 return (cell == null) ? null : cell._value; | 510 return (cell == null) ? null : cell._value; |
| 493 } else if (_isNumericKey(key)) { | 511 } else if (_isNumericKey(key)) { |
| 494 var nums = _nums; | 512 var nums = _nums; |
| 495 if (nums == null) return null; | 513 if (nums == null) return null; |
| 496 LinkedHashMapCell cell = _getTableEntry(nums, key); | 514 LinkedHashMapCell cell = _getTableEntry(nums, key); |
| 497 return (cell == null) ? null : cell._value; | 515 return (cell == null) ? null : cell._value; |
| 498 } else { | 516 } else { |
| 499 var rest = _rest; | 517 var rest = _rest; |
| 500 if (rest == null) return null; | 518 if (rest == null) return null; |
| 501 var bucket = _getBucket(rest, key); | 519 var bucket = _getBucket(rest, key); |
| 502 int index = _findBucketIndex(bucket, key); | 520 int index = _findBucketIndex(bucket, key); |
| 503 if (index < 0) return null; | 521 if (index < 0) return null; |
| 504 LinkedHashMapCell cell = JS('var', '#[#]', bucket, index); | 522 LinkedHashMapCell cell = JS('var', '#[#]', bucket, index); |
| 505 return cell._value; | 523 return cell._value; |
| 506 } | 524 } |
| 507 } | 525 } |
| 508 | 526 |
| 509 patch void operator[]=(K key, V value) { | 527 void operator[]=(K key, V value) { |
| 510 if (_isStringKey(key)) { | 528 if (_isStringKey(key)) { |
| 511 var strings = _strings; | 529 var strings = _strings; |
| 512 if (strings == null) _strings = strings = _newHashTable(); | 530 if (strings == null) _strings = strings = _newHashTable(); |
| 513 _addHashTableEntry(strings, key, value); | 531 _addHashTableEntry(strings, key, value); |
| 514 } else if (_isNumericKey(key)) { | 532 } else if (_isNumericKey(key)) { |
| 515 var nums = _nums; | 533 var nums = _nums; |
| 516 if (nums == null) _nums = nums = _newHashTable(); | 534 if (nums == null) _nums = nums = _newHashTable(); |
| 517 _addHashTableEntry(nums, key, value); | 535 _addHashTableEntry(nums, key, value); |
| 518 } else { | 536 } else { |
| 519 var rest = _rest; | 537 var rest = _rest; |
| 520 if (rest == null) _rest = rest = _newHashTable(); | 538 if (rest == null) _rest = rest = _newHashTable(); |
| 521 var hash = _computeHashCode(key); | 539 var hash = _computeHashCode(key); |
| 522 var bucket = JS('var', '#[#]', rest, hash); | 540 var bucket = JS('var', '#[#]', rest, hash); |
| 523 if (bucket == null) { | 541 if (bucket == null) { |
| 524 LinkedHashMapCell cell = _newLinkedCell(key, value); | 542 LinkedHashMapCell cell = _newLinkedCell(key, value); |
| 525 _setTableEntry(rest, hash, JS('var', '[#]', cell)); | 543 _setTableEntry(rest, hash, JS('var', '[#]', cell)); |
| 526 } else { | 544 } else { |
| 527 int index = _findBucketIndex(bucket, key); | 545 int index = _findBucketIndex(bucket, key); |
| 528 if (index >= 0) { | 546 if (index >= 0) { |
| 529 LinkedHashMapCell cell = JS('var', '#[#]', bucket, index); | 547 LinkedHashMapCell cell = JS('var', '#[#]', bucket, index); |
| 530 cell._value = value; | 548 cell._value = value; |
| 531 } else { | 549 } else { |
| 532 LinkedHashMapCell cell = _newLinkedCell(key, value); | 550 LinkedHashMapCell cell = _newLinkedCell(key, value); |
| 533 JS('void', '#.push(#)', bucket, cell); | 551 JS('void', '#.push(#)', bucket, cell); |
| 534 } | 552 } |
| 535 } | 553 } |
| 536 } | 554 } |
| 537 } | 555 } |
| 538 | 556 |
| 539 patch V putIfAbsent(K key, V ifAbsent()) { | 557 V putIfAbsent(K key, V ifAbsent()) { |
| 540 if (containsKey(key)) return this[key]; | 558 if (containsKey(key)) return this[key]; |
| 541 V value = ifAbsent(); | 559 V value = ifAbsent(); |
| 542 this[key] = value; | 560 this[key] = value; |
| 543 return value; | 561 return value; |
| 544 } | 562 } |
| 545 | 563 |
| 546 patch V remove(Object key) { | 564 V remove(Object key) { |
| 547 if (_isStringKey(key)) { | 565 if (_isStringKey(key)) { |
| 548 return _removeHashTableEntry(_strings, key); | 566 return _removeHashTableEntry(_strings, key); |
| 549 } else if (_isNumericKey(key)) { | 567 } else if (_isNumericKey(key)) { |
| 550 return _removeHashTableEntry(_nums, key); | 568 return _removeHashTableEntry(_nums, key); |
| 551 } else { | 569 } else { |
| 552 var rest = _rest; | 570 var rest = _rest; |
| 553 if (rest == null) return null; | 571 if (rest == null) return null; |
| 554 var bucket = _getBucket(rest, key); | 572 var bucket = _getBucket(rest, key); |
| 555 int index = _findBucketIndex(bucket, key); | 573 int index = _findBucketIndex(bucket, key); |
| 556 if (index < 0) return null; | 574 if (index < 0) return null; |
| 557 // Use splice to remove the [cell] element at the index and | 575 // Use splice to remove the [cell] element at the index and |
| 558 // unlink the cell before returning its value. | 576 // unlink the cell before returning its value. |
| 559 LinkedHashMapCell cell = JS('var', '#.splice(#, 1)[0]', bucket, index); | 577 LinkedHashMapCell cell = JS('var', '#.splice(#, 1)[0]', bucket, index); |
| 560 _unlinkCell(cell); | 578 _unlinkCell(cell); |
| 561 // TODO(kasperl): Consider getting rid of the bucket list when | 579 // TODO(kasperl): Consider getting rid of the bucket list when |
| 562 // the length reaches zero. | 580 // the length reaches zero. |
| 563 return cell._value; | 581 return cell._value; |
| 564 } | 582 } |
| 565 } | 583 } |
| 566 | 584 |
| 567 patch void clear() { | 585 void clear() { |
| 568 if (_length > 0) { | 586 if (_length > 0) { |
| 569 _strings = _nums = _rest = _first = _last = null; | 587 _strings = _nums = _rest = _first = _last = null; |
| 570 _length = 0; | 588 _length = 0; |
| 571 _modified(); | 589 _modified(); |
| 572 } | 590 } |
| 573 } | 591 } |
| 574 | 592 |
| 575 patch void forEach(void action(K key, V value)) { | 593 void forEach(void action(K key, V value)) { |
| 576 LinkedHashMapCell cell = _first; | 594 LinkedHashMapCell cell = _first; |
| 577 int modifications = _modifications; | 595 int modifications = _modifications; |
| 578 while (cell != null) { | 596 while (cell != null) { |
| 579 action(cell._key, cell._value); | 597 action(cell._key, cell._value); |
| 580 if (modifications != _modifications) { | 598 if (modifications != _modifications) { |
| 581 throw new ConcurrentModificationError(this); | 599 throw new ConcurrentModificationError(this); |
| 582 } | 600 } |
| 583 cell = cell._next; | 601 cell = cell._next; |
| 584 } | 602 } |
| 585 } | 603 } |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 648 return key is String && key != '__proto__'; | 666 return key is String && key != '__proto__'; |
| 649 } | 667 } |
| 650 | 668 |
| 651 static bool _isNumericKey(var key) { | 669 static bool _isNumericKey(var key) { |
| 652 // Only treat unsigned 30-bit integers as numeric keys. This way, | 670 // Only treat unsigned 30-bit integers as numeric keys. This way, |
| 653 // we avoid converting them to strings when we use them as keys in | 671 // we avoid converting them to strings when we use them as keys in |
| 654 // the JavaScript hash table object. | 672 // the JavaScript hash table object. |
| 655 return key is num && JS('bool', '(# & 0x3ffffff) === #', key, key); | 673 return key is num && JS('bool', '(# & 0x3ffffff) === #', key, key); |
| 656 } | 674 } |
| 657 | 675 |
| 658 static int _computeHashCode(var key) { | 676 int _computeHashCode(var key) { |
| 659 // We force the hash codes to be unsigned 30-bit integers to avoid | 677 // We force the hash codes to be unsigned 30-bit integers to avoid |
| 660 // issues with problematic keys like '__proto__'. Another option | 678 // issues with problematic keys like '__proto__'. Another option |
| 661 // would be to throw an exception if the hash code isn't a number. | 679 // would be to throw an exception if the hash code isn't a number. |
| 662 return JS('int', '# & 0x3ffffff', key.hashCode); | 680 return JS('int', '# & 0x3ffffff', key.hashCode); |
| 663 } | 681 } |
| 664 | 682 |
| 665 static _getTableEntry(var table, var key) { | 683 static _getTableEntry(var table, var key) { |
| 666 return JS('var', '#[#]', table, key); | 684 return JS('var', '#[#]', table, key); |
| 667 } | 685 } |
| 668 | 686 |
| 669 static void _setTableEntry(var table, var key, var value) { | 687 static void _setTableEntry(var table, var key, var value) { |
| 670 assert(value != null); | 688 assert(value != null); |
| 671 JS('void', '#[#] = #', table, key, value); | 689 JS('void', '#[#] = #', table, key, value); |
| 672 } | 690 } |
| 673 | 691 |
| 674 static void _deleteTableEntry(var table, var key) { | 692 static void _deleteTableEntry(var table, var key) { |
| 675 JS('void', 'delete #[#]', table, key); | 693 JS('void', 'delete #[#]', table, key); |
| 676 } | 694 } |
| 677 | 695 |
| 678 static List _getBucket(var table, var key) { | 696 List _getBucket(var table, var key) { |
| 679 var hash = _computeHashCode(key); | 697 var hash = _computeHashCode(key); |
| 680 return JS('var', '#[#]', table, hash); | 698 return JS('var', '#[#]', table, hash); |
| 681 } | 699 } |
| 682 | 700 |
| 683 static int _findBucketIndex(var bucket, var key) { | 701 int _findBucketIndex(var bucket, var key) { |
| 684 if (bucket == null) return -1; | 702 if (bucket == null) return -1; |
| 685 int length = JS('int', '#.length', bucket); | 703 int length = JS('int', '#.length', bucket); |
| 686 for (int i = 0; i < length; i++) { | 704 for (int i = 0; i < length; i++) { |
| 687 LinkedHashMapCell cell = JS('var', '#[#]', bucket, i); | 705 LinkedHashMapCell cell = JS('var', '#[#]', bucket, i); |
| 688 if (cell._key == key) return i; | 706 if (cell._key == key) return i; |
| 689 } | 707 } |
| 690 return -1; | 708 return -1; |
| 691 } | 709 } |
| 692 | 710 |
| 693 static _newHashTable() { | 711 static _newHashTable() { |
| 694 // Create a new JavaScript object to be used as a hash table. Use | 712 // Create a new JavaScript object to be used as a hash table. Use |
| 695 // Object.create to avoid the properties on Object.prototype | 713 // Object.create to avoid the properties on Object.prototype |
| 696 // showing up as entries. | 714 // showing up as entries. |
| 697 var table = JS('var', 'Object.create(null)'); | 715 var table = JS('var', 'Object.create(null)'); |
| 698 // Attempt to force the hash table into 'dictionary' mode by | 716 // Attempt to force the hash table into 'dictionary' mode by |
| 699 // adding a property to it and deleting it again. | 717 // adding a property to it and deleting it again. |
| 700 var temporaryKey = '<non-identifier-key>'; | 718 var temporaryKey = '<non-identifier-key>'; |
| 701 _setTableEntry(table, temporaryKey, table); | 719 _setTableEntry(table, temporaryKey, table); |
| 702 _deleteTableEntry(table, temporaryKey); | 720 _deleteTableEntry(table, temporaryKey); |
| 703 return table; | 721 return table; |
| 704 } | 722 } |
| 723 |
| 724 String toString() => Maps.mapToString(this); |
| 725 } |
| 726 |
| 727 class _LinkedIdentityHashMap<K, V> extends _LinkedHash<K, V> { |
| 728 int _findBucketIndex(var bucket, var key) { |
| 729 if (bucket == null) return -1; |
| 730 int length = JS('int', '#.length', bucket); |
| 731 for (int i = 0; i < length; i++) { |
| 732 LinkedHashMapCell cell = JS('var', '#[#]', bucket, i); |
| 733 if (identical(cell._key, key)) return i; |
| 734 } |
| 735 return -1; |
| 736 } |
| 737 } |
| 738 |
| 739 class _LinkedCustomHashMap<K, V> extends _LinkedHash<K, V> { |
| 740 final _Equality<K> _equals; |
| 741 final _Hasher<K> _hashCode; |
| 742 _LinkedCustomHashMap(this._equals, this._hashCode); |
| 743 |
| 744 int _computeHashCode(var key) { |
| 745 // We force the hash codes to be unsigned 30-bit integers to avoid |
| 746 // issues with problematic keys like '__proto__'. Another option |
| 747 // would be to throw an exception if the hash code isn't a number. |
| 748 return JS('int', '# & 0x3ffffff', _hashCode(key)); |
| 749 } |
| 750 |
| 751 int _findBucketIndex(var bucket, var key) { |
| 752 if (bucket == null) return -1; |
| 753 int length = JS('int', '#.length', bucket); |
| 754 for (int i = 0; i < length; i++) { |
| 755 LinkedHashMapCell cell = JS('var', '#[#]', bucket, i); |
| 756 if (_equals(cell._key, key)) return i; |
| 757 } |
| 758 return -1; |
| 759 } |
| 705 } | 760 } |
| 706 | 761 |
| 707 class LinkedHashMapCell { | 762 class LinkedHashMapCell { |
| 708 final _key; | 763 final _key; |
| 709 var _value; | 764 var _value; |
| 710 | 765 |
| 711 LinkedHashMapCell _next; | 766 LinkedHashMapCell _next; |
| 712 LinkedHashMapCell _previous; | 767 LinkedHashMapCell _previous; |
| 713 | 768 |
| 714 LinkedHashMapCell(this._key, this._value); | 769 LinkedHashMapCell(this._key, this._value); |
| (...skipping 658 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1373 } else if (_cell == null) { | 1428 } else if (_cell == null) { |
| 1374 _current = null; | 1429 _current = null; |
| 1375 return false; | 1430 return false; |
| 1376 } else { | 1431 } else { |
| 1377 _current = _cell._element; | 1432 _current = _cell._element; |
| 1378 _cell = _cell._next; | 1433 _cell = _cell._next; |
| 1379 return true; | 1434 return true; |
| 1380 } | 1435 } |
| 1381 } | 1436 } |
| 1382 } | 1437 } |
| OLD | NEW |