Version 0.3.7.3
Merge revisions 18567, 18573, 18574, 18588 ,18594 to trunk

git-svn-id: http://dart.googlecode.com/svn/trunk@18610 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/pkg/pkg.status b/pkg/pkg.status
index 9933564..cdaed72 100644
--- a/pkg/pkg.status
+++ b/pkg/pkg.status
@@ -52,6 +52,9 @@
 intl/test/find_default_locale_browser_test: Skip
 intl/test/date_time_format_http_request_test: Skip
 
+# Fails to recognize HashMap as an instance of Map.
+analyzer-experimental/test/generated/ast_test: Fail
+
 [ $runtime == vm && $system == windows ]
 intl/test/find_default_locale_standalone_test: Fail # Issue 8110
 
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart b/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart
index e60f8c9..9f8155d 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/backend.dart
@@ -306,7 +306,7 @@
     // TODO(sgjesse): Handle field types for classes with more than one
     // constructor.
     if (ctors.length == 2) {
-      optimizedFunctions.forEach((Element field, _) {
+      optimizedFunctions.keys.toList().forEach((Element field) {
         if (identical(field.enclosingElement, cls)) {
           scheduleRecompilation(field);
         }
@@ -347,12 +347,12 @@
     // TODO(sgjesse): Take the type of the setter into account.
     if (setterSelectorsUsed.contains(setter.name)) return;
     setterSelectorsUsed.add(setter.name);
-    optimizedStaticFunctions.forEach((Element field, _) {
+    optimizedStaticFunctions.keys.toList().forEach((Element field) {
       if (field.name == setter.name) {
         scheduleRecompilation(field);
       }
     });
-    optimizedFunctions.forEach((Element field, _) {
+    optimizedFunctions.keys.toList().forEach((Element field) {
       if (field.name == setter.name) {
         scheduleRecompilation(field);
       }
@@ -622,14 +622,14 @@
   CodeEmitterTask emitter;
 
   /**
-   * The generated code as a js AST for compiled methods. 
+   * The generated code as a js AST for compiled methods.
    */
   Map<Element, js.Expression> get generatedCode {
     return compiler.enqueuer.codegen.generatedCode;
   }
 
   /**
-   * The generated code as a js AST for compiled bailout methods. 
+   * The generated code as a js AST for compiled bailout methods.
    */
   final Map<Element, js.Expression> generatedBailoutCode =
       new Map<Element, js.Expression>();
@@ -908,7 +908,7 @@
       // The backend will use a literal list to initialize the entries
       // of the map.
       if (enqueuer.isResolutionQueue) {
-        enqueuer.registerInstantiatedClass(compiler.listClass); 
+        enqueuer.registerInstantiatedClass(compiler.listClass);
         enqueuer.registerInstantiatedClass(compiler.mapLiteralClass);
       }
     }
diff --git a/sdk/lib/_internal/compiler/implementation/native_handler.dart b/sdk/lib/_internal/compiler/implementation/native_handler.dart
index 894a605..adce296 100644
--- a/sdk/lib/_internal/compiler/implementation/native_handler.dart
+++ b/sdk/lib/_internal/compiler/implementation/native_handler.dart
@@ -362,7 +362,7 @@
                                cause,
                                [String reason]) {
     Iterable matches = unusedClasses.where(predicate);
-    matches.forEach((c) => enqueueClass(c, cause));
+    matches.toList().forEach((c) => enqueueClass(c, cause));
   }
 
   onFirstNativeClass() {
diff --git a/sdk/lib/_internal/compiler/implementation/source_map_builder.dart b/sdk/lib/_internal/compiler/implementation/source_map_builder.dart
index a2cd6fc..80960d3 100644
--- a/sdk/lib/_internal/compiler/implementation/source_map_builder.dart
+++ b/sdk/lib/_internal/compiler/implementation/source_map_builder.dart
@@ -134,7 +134,6 @@
   int indexOf(List<String> list, String value, Map<String, int> map) {
     return map.putIfAbsent(value, () {
       int index = list.length;
-      map[value] = index;
       list.add(value);
       return index;
     });
diff --git a/sdk/lib/collection/collection.dart b/sdk/lib/collection/collection.dart
index b1a8353..88c1ada 100644
--- a/sdk/lib/collection/collection.dart
+++ b/sdk/lib/collection/collection.dart
@@ -9,8 +9,12 @@
 part 'arrays.dart';
 part 'collections.dart';
 part 'iterator.dart';
-part 'map.dart';
 part 'maps.dart';
 part 'queue.dart';
-part 'set.dart';
 part 'splay_tree.dart';
+part 'hash_table.dart';
+part 'hash_set.dart';
+part 'hash_map.dart';
+part 'linked_hash_table.dart';
+part 'linked_hash_set.dart';
+part 'linked_hash_map.dart';
diff --git a/sdk/lib/collection/collection_sources.gypi b/sdk/lib/collection/collection_sources.gypi
index b262c10..37d67be 100644
--- a/sdk/lib/collection/collection_sources.gypi
+++ b/sdk/lib/collection/collection_sources.gypi
@@ -7,11 +7,15 @@
   'sources': [
     'arrays.dart',
     'collections.dart',
+    'hash_map.dart',
+    'hash_set.dart',
+    'hash_table.dart',
     'iterator.dart',
-    'map.dart',
+    'linked_hash_map.dart',
+    'linked_hash_set.dart',
+    'linked_hash_table.dart',
     'maps.dart',
     'queue.dart',
-    'set.dart',
     'splay_tree.dart',
   ],
 }
diff --git a/sdk/lib/collection/hash_map.dart b/sdk/lib/collection/hash_map.dart
new file mode 100644
index 0000000..d90b4b9
--- /dev/null
+++ b/sdk/lib/collection/hash_map.dart
@@ -0,0 +1,136 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+part of dart.collection;
+
+class _HashMapTable<K, V> extends _HashTable<K> {
+  static const int _INITIAL_CAPACITY = 8;
+  static const int _VALUE_INDEX = 1;
+
+  _HashMapTable() : super(_INITIAL_CAPACITY);
+
+  int get _entrySize => 2;
+
+  V _value(int offset) => _table[offset + _VALUE_INDEX];
+  void _setValue(int offset, V value) { _table[offset + _VALUE_INDEX] = value; }
+
+  _copyEntry(List fromTable, int fromOffset, int toOffset) {
+    _table[toOffset + _VALUE_INDEX] = fromTable[fromOffset + _VALUE_INDEX];
+  }
+}
+
+class HashMap<K, V> implements Map<K, V> {
+  final _HashMapTable<K, V> _hashTable;
+
+  HashMap() : _hashTable = new _HashMapTable<K, V>() {
+    _hashTable._container = this;
+  }
+
+  factory HashMap.from(Map<K, V> other) {
+    return new HashMap<K, V>()..addAll(other);
+  }
+
+  bool containsKey(K key) {
+    return _hashTable._get(key) >= 0;
+  }
+
+  bool containsValue(V value) {
+    List table = _hashTable._table;
+    int entrySize = _hashTable._entrySize;
+    for (int offset = 0; offset < table.length; offset += entrySize) {
+      if (!_hashTable._isFree(table[offset]) &&
+          _hashTable._value(offset) == value) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  void addAll(Map<K, V> other) {
+    other.forEach((K key, V value) {
+      int offset = _hashTable._put(key);
+      _hashTable._setValue(offset, value);
+      _hashTable._checkCapacity();
+    });
+  }
+
+  V operator [](K key) {
+    int offset = _hashTable._get(key);
+    if (offset >= 0) return _hashTable._value(offset);
+    return null;
+  }
+
+  void operator []=(K key, V value) {
+    int offset = _hashTable._put(key);
+    _hashTable._setValue(offset, value);
+    _hashTable._checkCapacity();
+  }
+
+  V putIfAbsent(K key, V ifAbsent()) {
+    int offset = _hashTable._probeForAdd(_hashTable._hashCodeOf(key), key);
+    Object entry = _hashTable._table[offset];
+    if (!_hashTable._isFree(entry)) {
+      return _hashTable._value(offset);
+    }
+    int modificationCount = _hashTable._modificationCount;
+    V value = ifAbsent();
+    if (modificationCount == _hashTable._modificationCount) {
+      _hashTable._setKey(offset, key);
+      _hashTable._setValue(offset, value);
+      if (entry == null) {
+        _hashTable._entryCount++;
+        _hashTable._checkCapacity();
+      } else {
+        assert(identical(entry, _TOMBSTONE));
+        _hashTable._deletedCount--;
+      }
+      _hashTable._recordModification();
+    } else {
+      // The table might have changed, so we can't trust [offset] any more.
+      // Do another lookup before setting the value.
+      offset = _hashTable._put(key);
+      _hashTable._setValue(offset, value);
+      _hashTable._checkCapacity();
+    }
+    return value;
+  }
+
+  V remove(K key) {
+    int offset = _hashTable._remove(key);
+    if (offset < 0) return null;
+    V oldValue = _hashTable._value(offset);
+    _hashTable._setValue(offset, null);
+    _hashTable._checkCapacity();
+    return oldValue;
+  }
+
+  void clear() {
+    _hashTable._clear();
+  }
+
+  void forEach(void action (K key, V value)) {
+    int modificationCount = _hashTable._modificationCount;
+    List table = _hashTable._table;
+    int entrySize = _hashTable._entrySize;
+    for (int offset = 0; offset < table.length; offset += entrySize) {
+      Object entry = table[offset];
+      if (!_hashTable._isFree(entry)) {
+        K key = entry;
+        V value = _hashTable._value(offset);
+        action(key, value);
+        _hashTable._checkModification(modificationCount);
+      }
+    }
+  }
+
+  Iterable<K> get keys => new _HashTableKeyIterable<K>(_hashTable);
+  Iterable<V> get values =>
+      new _HashTableValueIterable<V>(_hashTable, _HashMapTable._VALUE_INDEX);
+
+  int get length => _hashTable._elementCount;
+
+  bool get isEmpty => _hashTable._elementCount == 0;
+
+  String toString() => Maps.mapToString(this);
+}
diff --git a/sdk/lib/collection/hash_set.dart b/sdk/lib/collection/hash_set.dart
new file mode 100644
index 0000000..22fb38f
--- /dev/null
+++ b/sdk/lib/collection/hash_set.dart
@@ -0,0 +1,115 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+part of dart.collection;
+
+class HashSet<E> extends Collection<E> implements Set<E> {
+  static const int _INITIAL_CAPACITY = 8;
+  final _HashTable<E> _table;
+
+  HashSet() : _table = new _HashTable(_INITIAL_CAPACITY) {
+    _table._container = this;
+  }
+
+  factory HashSet.from(Iterable<E> iterable) {
+    return new HashSet<E>()..addAll(iterable);
+  }
+
+  // Iterable.
+  Iterator<E> get iterator => new _HashTableKeyIterator<E>(_table);
+
+  bool get isEmpty => _table._elementCount == 0;
+
+  bool contains(Object object) => _table._get(object) >= 0;
+
+  // Collection.
+  void add(E element) {
+    _table._put(element);
+    _table._checkCapacity();
+  }
+
+  void addAll(Iterable<E> objects) {
+    for (E object in objects) {
+      _table._put(object);
+      _table._checkCapacity();
+    }
+  }
+
+  bool remove(Object object) {
+    int offset = _table._remove(object);
+    _table._checkCapacity();
+    return offset >= 0;
+  }
+
+  void removeAll(Iterable objectsToRemove) {
+    for (Object object in objectsToRemove) {
+      _table._remove(object);
+      _table._checkCapacity();
+    }
+  }
+
+  void retainAll(Iterable objectsToRetain) {
+    IterableMixinWorkaround.retainAll(this, objectsToRetain);
+  }
+
+  void _filterMatching(bool test(E element), bool removeMatching) {
+    int entrySize = _table._entrySize;
+    int length = _table._table.length;
+    for (int offset =  0; offset < length; offset += entrySize) {
+      Object entry = _table._table[offset];
+      if (!_table._isFree(entry)) {
+        E key = identical(entry, _NULL) ? null : entry;
+        int modificationCount = _table._modificationCount;
+        bool shouldRemove = (removeMatching == test(key));
+        _table._checkModification(modificationCount);
+        if (shouldRemove) {
+          _table._deleteEntry(offset);
+        }
+      }
+    }
+    _table._checkCapacity();
+  }
+
+  void removeMatching(bool test(E element)) {
+    _filterMatching(test, true);
+  }
+
+  void retainMatching(bool test(E element)) {
+    _filterMatching(test, false);
+  }
+
+  void clear() {
+    _table._clear();
+  }
+
+  // Set.
+  bool isSubsetOf(Collection<E> collection) {
+    Set otherSet;
+    if (collection is Set) {
+      otherSet = collection;
+    } else {
+      otherSet = collection.toSet();
+    }
+    return otherSet.containsAll(this);
+  }
+
+  bool containsAll(Collection<E> collection) {
+    for (E element in collection) {
+      if (!this.contains(element)) return false;
+    }
+    return true;
+  }
+
+  Set<E> intersection(Collection<E> other) {
+    Set<E> result = new HashSet<E>();
+    for (E element in other) {
+      if (this.contains(element)) {
+        result.add(element);
+      }
+    }
+    return result;
+  }
+
+  String toString() =>  Collections.collectionToString(this);
+}
diff --git a/sdk/lib/collection/hash_table.dart b/sdk/lib/collection/hash_table.dart
new file mode 100644
index 0000000..7ab52cb
--- /dev/null
+++ b/sdk/lib/collection/hash_table.dart
@@ -0,0 +1,404 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+part of dart.collection;
+
+class _DeadEntry {
+  const _DeadEntry();
+}
+
+class _NullKey {
+  const _NullKey();
+  int get hashCode => null.hashCode;
+}
+
+const _TOMBSTONE = const _DeadEntry();
+const _NULL = const _NullKey();
+
+class _HashTable<K> {
+  /**
+   * Table of entries with [_entrySize] slots per entry.
+   *
+   * Capacity in entries must be factor of two.
+   */
+  List _table;
+  /** Current capacity. Always equal to [:_table.length ~/ _entrySize:]. */
+  int _capacity;
+  /** Count of occupied entries, including deleted ones. */
+  int _entryCount = 0;
+  /** Count of deleted entries. */
+  int _deletedCount = 0;
+  /** Counter incremented when table is modified. */
+  int _modificationCount = 0;
+  /** If set, used as the source object for [ConcurrentModificationError]s. */
+  Object _container;
+
+  _HashTable(int initialCapacity) : _capacity = initialCapacity {
+    _table = _createTable(initialCapacity);
+  }
+
+  /** Reads key from table. Converts _NULL marker to null. */
+  Object _key(offset) {
+    assert(!_isFree(_table[offset]));
+    Object key = _table[offset];
+    if (!identical(key, _NULL)) return key;
+    return null;
+  }
+
+  /** Writes key to table. Converts null to _NULL marker. */
+  void _setKey(int offset, Object key) {
+    if (key == null) key = _NULL;
+    _table[offset] = key;
+  }
+
+  int get _elementCount => _entryCount - _deletedCount;
+
+  /** Size of each entry. */
+  int get _entrySize => 1;
+
+  void _checkModification(int expectedModificationCount) {
+    if (_modificationCount != expectedModificationCount) {
+      throw new ConcurrentModificationError(_container);
+    }
+  }
+
+  void _recordModification() {
+    // Value cycles after 2^30 modifications. If you keep hold of an
+    // iterator for that long, you might miss a modification detection,
+    // and iteration can go sour. Don't do that.
+    _modificationCount = (_modificationCount + 1) & (0x3FFFFFFF);
+  }
+
+  /**
+   * Create an empty table.
+   */
+  List _createTable(int capacity) {
+    List table = new List.fixedLength(capacity * _entrySize);
+    return table;
+  }
+
+  /** First table probe. */
+  int _firstProbe(int hashCode, int capacity) {
+    return hashCode & (capacity - 1);
+  }
+
+  /** Following table probes. */
+  int _nextProbe(int previousIndex, int probeCount, int capacity) {
+    // When capacity is a power of 2, this probing algorithm (the triangular
+    // number sequence modulo capacity) is guaranteed to hit all indices exactly
+    // once before repeating.
+    return (previousIndex + probeCount) & (capacity - 1);
+  }
+
+  /** Whether an object is a free-marker (either tombstone or free). */
+  bool _isFree(Object marker) =>
+      marker == null || identical(marker, _TOMBSTONE);
+
+  /**
+   * Look up the offset for an object in the table.
+   *
+   * Finds the offset of the object in the table, if it is there,
+   * or the first free offset for its hashCode.
+   */
+  int _probeForAdd(int hashCode, Object object) {
+    int entrySize = _entrySize;
+    int index = _firstProbe(hashCode, _capacity);
+    int firstTombstone = -1;
+    int probeCount = 0;
+    while (true) {
+      int offset = index * entrySize;
+      Object entry = _table[offset];
+      if (identical(entry, _TOMBSTONE)) {
+        if (firstTombstone < 0) firstTombstone = offset;
+      } else if (entry == null) {
+        if (firstTombstone < 0) return offset;
+        return firstTombstone;
+      } else if (identical(_NULL, entry) ? _equals(null, object)
+                                         : _equals(entry, object)) {
+        return offset;
+      }
+      // The _nextProbe is designed so that it hits
+      // every index eventually.
+      index = _nextProbe(index, ++probeCount, _capacity);
+    }
+  }
+
+  /**
+   * Look up the offset for an object in the table.
+   *
+   * If the object is in the table, its offset is returned.
+   *
+   * If the object is not in the table, Otherwise a negative value is returned.
+   */
+  int _probeForLookup(int hashCode, Object object) {
+    int entrySize = _entrySize;
+    int index = _firstProbe(hashCode, _capacity);
+    int probeCount = 0;
+    while (true) {
+      int offset = index * entrySize;
+      Object entry = _table[offset];
+      if (entry == null) {
+        return -1;
+      } else if (!identical(_TOMBSTONE, entry)) {
+        if (identical(_NULL, entry) ? _equals(null, object)
+                                    : _equals(entry, object)) {
+          return offset;
+        }
+      }
+      // The _nextProbe is designed so that it hits
+      // every index eventually.
+      index = _nextProbe(index, ++probeCount, _capacity);
+    }
+  }
+
+  // Override the following two to change equality/hashCode computations
+
+  /**
+   * Compare two object for equality.
+   *
+   * The first object is the one already in the table,
+   * and the second is the one being searched for.
+   */
+  bool _equals(Object element, Object other) {
+    return element == other;
+  }
+
+  /**
+   * Compute hash-code for an object.
+   */
+  int _hashCodeOf(Object object) => object.hashCode;
+
+  /**
+   * Ensure that the table isn't too full for its own good.
+   *
+   * Call this after adding an element.
+   */
+  int _checkCapacity() {
+    // Compute everything in multiples of entrySize to avoid division.
+    int freeCount = _capacity - _entryCount;
+    if (freeCount * 4 < _capacity ||
+        freeCount < _deletedCount) {
+      // Less than 25% free or more deleted entries than free entries.
+      _grow(_entryCount - _deletedCount);
+    }
+  }
+
+  void _grow(int contentCount) {
+    int capacity = _capacity;
+    // Don't grow to less than twice the needed capacity.
+    int minCapacity = contentCount * 2;
+    while (capacity < minCapacity) {
+      capacity *= 2;
+    }
+    // Reset to another table and add all existing elements.
+    List oldTable = _table;
+    _table = _createTable(capacity);
+    _capacity = capacity;
+    _entryCount = 0;
+    _deletedCount = 0;
+    _addAllEntries(oldTable);
+    _recordModification();
+  }
+
+  /**
+   * Copies all non-free entries from the old table to the new empty table.
+   */
+  void _addAllEntries(List oldTable) {
+    for (int i = 0; i < oldTable.length; i += _entrySize) {
+      Object object = oldTable[i];
+      if (!_isFree(object)) {
+        int toOffset = _put(object);
+        _copyEntry(oldTable, i, toOffset);
+      }
+    }
+  }
+
+  /**
+   * Copies everything but the key element from one entry to another.
+   *
+   * Called while growing the base array.
+   *
+   * Override this if any non-key fields need copying.
+   */
+  void _copyEntry(List fromTable, int fromOffset, int toOffset) {}
+
+  // The following three methods are for simple get/set/remove operations.
+  // They only affect the key of an entry. The remaining fields must be
+  // filled by the caller.
+
+  /**
+   * Returns the offset of a key in [_table], or negative if it's not there.
+   */
+  int _get(K key) {
+    return _probeForLookup(_hashCodeOf(key), key);
+  }
+
+  /**
+   * Puts the key into the table and returns its offset into [_table].
+   *
+   * If [_entrySize] is greater than 1, the caller should fill the
+   * remaining fields.
+   *
+   * Remember to call [_checkCapacity] after using this method.
+   */
+  int _put(K key) {
+    int offset = _probeForAdd(_hashCodeOf(key), key);
+    Object oldEntry = _table[offset];
+    if (oldEntry == null) {
+      _entryCount++;
+    } else if (identical(oldEntry, _TOMBSTONE)) {
+      _deletedCount--;
+    } else {
+      return offset;
+    }
+    _setKey(offset, key);
+    _recordModification();
+    return offset;
+  }
+
+  /**
+   * Removes a key from the table and returns its offset into [_table].
+   *
+   * Returns null if the key was not in the table.
+   * If [_entrySize] is greater than 1, the caller should clean up the
+   * remaining fields.
+   */
+  int _remove(K key) {
+    int offset = _probeForLookup(_hashCodeOf(key), key);
+    if (offset >= 0) {
+      _deleteEntry(offset);
+    }
+    return offset;
+  }
+
+  /** Clears the table completely, leaving it empty. */
+  void _clear() {
+    if (_elementCount == 0) return;
+    for (int i = 0; i < _table.length; i++) {
+      _table[i] = null;
+    }
+    _entryCount = _deletedCount = 0;
+    _recordModification();
+  }
+
+  /** Clears an entry in the table. */
+  void _deleteEntry(int offset) {
+    assert(!_isFree(_table[offset]));
+    _setKey(offset, _TOMBSTONE);
+    _deletedCount++;
+    _recordModification();
+  }
+}
+
+/**
+ * Generic iterable based on a [_HashTable].
+ */
+abstract class _HashTableIterable<E> extends Iterable<E> {
+  final _HashTable _hashTable;
+  _HashTableIterable(this._hashTable);
+
+  Iterator<E> get iterator;
+
+  /**
+   * Return the iterated value for a given entry.
+   */
+  E _valueAt(int offset, Object key);
+
+  int get length => _hashTable._elementCount;
+
+  bool get isEmpty => _hashTable._elementCount == 0;
+
+  void forEach(void action(E element)) {
+    int entrySize = _hashTable._entrySize;
+    List table = _hashTable._table;
+    int modificationCount = _hashTable._modificationCount;
+    for (int offset = 0; offset < table.length; offset += entrySize) {
+      Object entry = table[offset];
+      if (!_hashTable._isFree(entry)) {
+        E value = _valueAt(offset, entry);
+        action(value);
+      }
+      _hashTable._checkModification(modificationCount);
+    }
+  }
+}
+
+abstract class _HashTableIterator<E> implements Iterator<E> {
+  final _HashTable _hashTable;
+  final int _modificationCount;
+  /** Location right after last found element. */
+  int _offset = 0;
+  E _current = null;
+
+  _HashTableIterator(_HashTable hashTable)
+      : _hashTable = hashTable,
+        _modificationCount = hashTable._modificationCount;
+
+  bool moveNext() {
+    _hashTable._checkModification(_modificationCount);
+
+    List table = _hashTable._table;
+    int entrySize = _hashTable._entrySize;
+
+    while (_offset < table.length) {
+      int currentOffset = _offset;
+      Object entry = table[currentOffset];
+      _offset = currentOffset + entrySize;
+      if (!_hashTable._isFree(entry)) {
+        _current = _valueAt(currentOffset, entry);
+        return true;
+      }
+    }
+    _current = null;
+    return false;
+  }
+
+  E get current => _current;
+
+  E _valueAt(int offset, Object key);
+}
+
+class _HashTableKeyIterable<K> extends _HashTableIterable<K> {
+  _HashTableKeyIterable(_HashTable<K> hashTable) : super(hashTable);
+
+  Iterator<K> get iterator => new _HashTableKeyIterator<K>(_hashTable);
+
+  K _valueAt(int offset, Object key) {
+    if (identical(key, _NULL)) return null;
+    return key;
+  }
+
+  bool contains(Object value) => _hashTable._get(value) >= 0;
+}
+
+class _HashTableKeyIterator<K> extends _HashTableIterator<K> {
+  _HashTableKeyIterator(_HashTable hashTable) : super(hashTable);
+
+  K _valueAt(int offset, Object key) {
+    if (identical(key, _NULL)) return null;
+    return key;
+  }
+}
+
+class _HashTableValueIterable<V> extends _HashTableIterable<V> {
+  final int _entryIndex;
+
+  _HashTableValueIterable(_HashTable hashTable, this._entryIndex)
+      : super(hashTable);
+
+  Iterator<V> get iterator {
+    return new _HashTableValueIterator<V>(_hashTable, _entryIndex);
+  }
+
+  V _valueAt(int offset, Object key) => _hashTable._table[offset + _entryIndex];
+}
+
+class _HashTableValueIterator<V> extends _HashTableIterator<V> {
+  final int _entryIndex;
+
+  _HashTableValueIterator(_HashTable hashTable, this._entryIndex)
+      : super(hashTable);
+
+  V _valueAt(int offset, Object key) => _hashTable._table[offset + _entryIndex];
+}
diff --git a/sdk/lib/collection/linked_hash_map.dart b/sdk/lib/collection/linked_hash_map.dart
new file mode 100644
index 0000000..4dcbc060
--- /dev/null
+++ b/sdk/lib/collection/linked_hash_map.dart
@@ -0,0 +1,138 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+part of dart.collection;
+
+class _LinkedHashMapTable<K, V> extends _LinkedHashTable<K> {
+  static const int _INITIAL_CAPACITY = 8;
+  static const int _VALUE_INDEX = 3;
+
+  int get _entrySize => 4;
+
+  _LinkedHashMapTable() : super(_INITIAL_CAPACITY);
+
+  V _value(int offset) => _table[offset + _VALUE_INDEX];
+  void _setValue(int offset, V value) { _table[offset + _VALUE_INDEX] = value; }
+
+  _copyEntry(List oldTable, int fromOffset, int toOffset) {
+    _table[toOffset + _VALUE_INDEX] = oldTable[fromOffset + _VALUE_INDEX];
+  }
+}
+
+/**
+ * A hash-based map that iterates keys and values in key insertion order.
+ */
+class LinkedHashMap<K, V> implements Map<K, V> {
+  final _LinkedHashMapTable _hashTable;
+
+  LinkedHashMap() : _hashTable = new _LinkedHashMapTable<K, V>() {
+    _hashTable._container = this;
+  }
+
+  factory LinkedHashMap.from(Map<K, V> other) {
+    return new LinkedHashMap<K, V>()..addAll(other);
+  }
+
+  bool containsKey(K key) {
+    return _hashTable._get(key) >= 0;
+  }
+
+  bool containsValue(V value) {
+    int modificationCount = _hashTable._modificationCount;
+    for (int offset = _hashTable._next(_LinkedHashTable._HEAD_OFFSET);
+         offset != _LinkedHashTable._HEAD_OFFSET;
+         offset = _hashTable._next(offset)) {
+      if (_hashTable._value(offset) == value) {
+        return true;
+      }
+      // The == call may modify the table.
+      _hashTable._checkModification(modificationCount);
+    }
+    return false;
+  }
+
+  void addAll(Map<K, V> other) {
+    other.forEach((K key, V value) {
+      int offset = _hashTable._put(key);
+      _hashTable._setValue(offset, value);
+      _hashTable._checkCapacity();
+    });
+  }
+
+  V operator [](K key) {
+    int offset = _hashTable._get(key);
+    if (offset >= 0) return _hashTable._value(offset);
+    return null;
+  }
+
+  void operator []=(K key, V value) {
+    int offset = _hashTable._put(key);
+    _hashTable._setValue(offset, value);
+    _hashTable._checkCapacity();
+  }
+
+  V putIfAbsent(K key, V ifAbsent()) {
+    int offset = _hashTable._probeForAdd(_hashTable._hashCodeOf(key), key);
+    Object entry = _hashTable._table[offset];
+    if (!_hashTable._isFree(entry)) {
+      return _hashTable._value(offset);
+    }
+    int modificationCount = _hashTable._modificationCount;
+    V value = ifAbsent();
+    if (modificationCount == _hashTable._modificationCount) {
+      _hashTable._setKey(offset, key);
+      _hashTable._setValue(offset, value);
+      _hashTable._linkLast(offset);
+      if (entry == null) {
+        _hashTable._entryCount++;
+        _hashTable._checkCapacity();
+      } else {
+        assert(identical(entry, _TOMBSTONE));
+        _hashTable._deletedCount--;
+      }
+      _hashTable._recordModification();
+    } else {
+      // The table might have changed, so we can't trust [offset] any more.
+      // Do another lookup before setting the value.
+      offset = _hashTable._put(key);
+      _hashTable._setValue(offset, value);
+      _hashTable._checkCapacity();
+    }
+    return value;
+  }
+
+  V remove(K key) {
+    int offset = _hashTable._remove(key);
+    if (offset < 0) return null;
+    Object oldValue = _hashTable._value(offset);
+    _hashTable._setValue(offset, null);
+    _hashTable._checkCapacity();
+    return oldValue;
+  }
+
+  void clear() {
+    _hashTable._clear();
+  }
+
+  void forEach(void action (K key, V value)) {
+    int modificationCount = _hashTable._modificationCount;
+    for (int offset = _hashTable._next(_LinkedHashTable._HEAD_OFFSET);
+         offset != _LinkedHashTable._HEAD_OFFSET;
+         offset = _hashTable._next(offset)) {
+      action(_hashTable._key(offset), _hashTable._value(offset));
+      _hashTable._checkModification(modificationCount);
+    }
+  }
+
+  Iterable<K> get keys => new _LinkedHashTableKeyIterable<K>(_hashTable);
+  Iterable<V> get values =>
+      new _LinkedHashTableValueIterable<V>(_hashTable,
+                                           _LinkedHashMapTable._VALUE_INDEX);
+
+  int get length => _hashTable._elementCount;
+
+  bool get isEmpty => _hashTable._elementCount == 0;
+
+  String toString() => Maps.mapToString(this);
+}
diff --git a/sdk/lib/collection/linked_hash_set.dart b/sdk/lib/collection/linked_hash_set.dart
new file mode 100644
index 0000000..e72493a
--- /dev/null
+++ b/sdk/lib/collection/linked_hash_set.dart
@@ -0,0 +1,157 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+part of dart.collection;
+
+class LinkedHashSet<E> extends Collection<E> implements Set<E> {
+  static const int _INITIAL_CAPACITY = 8;
+  _LinkedHashTable<E> _table;
+
+  LinkedHashSet() : _table = new _LinkedHashTable(_INITIAL_CAPACITY) {
+    _table._container = this;
+  }
+
+  factory LinkedHashSet.from(Iterable<E> iterable) {
+    return new LinkedHashSet<E>()..addAll(iterable);
+  }
+
+  // Iterable.
+  Iterator<E> get iterator => new _LinkedHashTableKeyIterator<E>(_table);
+
+  void forEach(void action(E element)) {
+    int offset = _table._next(_LinkedHashTable._HEAD_OFFSET);
+    int modificationCount = _table._modificationCount;
+    while (offset != _LinkedHashTable._HEAD_OFFSET) {
+      E key = _table._key(offset);
+      action(key);
+      _table._checkModification(modificationCount);
+      offset = _table._next(offset);
+    }
+  }
+
+  bool get isEmpty => _table._elementCount == 0;
+
+  bool contains(Object object) => _table._get(object) >= 0;
+
+  E get first {
+    int firstOffset = _table._next(_LinkedHashTable._HEAD_OFFSET);
+    if (firstOffset == _LinkedHashTable._HEAD_OFFSET) {
+      throw new StateError("No elements");
+    }
+    return _table._key(firstOffset);
+  }
+
+  E get last {
+    int lastOffset = _table._prev(_LinkedHashTable._HEAD_OFFSET);
+    if (lastOffset == _LinkedHashTable._HEAD_OFFSET) {
+      throw new StateError("No elements");
+    }
+    return _table._key(lastOffset);
+  }
+
+  E get single {
+    int firstOffset = _table._next(_LinkedHashTable._HEAD_OFFSET);
+    if (firstOffset == _LinkedHashTable._HEAD_OFFSET) {
+      throw new StateError("No elements");
+    }
+    int lastOffset = _table._prev(_LinkedHashTable._HEAD_OFFSET);
+    if (lastOffset != firstOffset) {
+      throw new StateError("Too many elements");
+    }
+    return _table._key(firstOffset);
+  }
+
+  // Collection.
+  void add(E element) {
+    _table._put(element);
+    _table._checkCapacity();
+  }
+
+  void addAll(Iterable<E> objects) {
+    for (E object in objects) {
+      _table._put(object);
+      _table._checkCapacity();
+    }
+  }
+
+  bool remove(Object object) {
+    int offset = _table._remove(object);
+    if (offset >= 0) {
+      _table._checkCapacity();
+      return true;
+    }
+    return false;
+  }
+
+  void removeAll(Iterable objectsToRemove) {
+    for (Object object in objectsToRemove) {
+      _table._remove(object);
+      _table._checkCapacity();
+    }
+  }
+
+  void retainAll(Iterable objectsToRemove) {
+    IterableMixinWorkaround.retainAll(this, objectsToRemove);
+  }
+
+  void _filterMatching(bool test(E element), bool removeMatching) {
+    int entrySize = _table._entrySize;
+    int length = _table._table.length;
+    int offset = _table._next(_LinkedHashTable._HEAD_OFFSET);
+    while (offset != _LinkedHashTable._HEAD_OFFSET) {
+      E key = _table._key(offset);
+      int nextOffset = _table._next(offset);
+      int modificationCount = _table._modificationCount;
+      bool shouldRemove = (removeMatching == test(key));
+      _table._checkModification(modificationCount);
+      if (shouldRemove) {
+        _table._deleteEntry(offset);
+      }
+      offset = nextOffset;
+    }
+    _table._checkCapacity();
+  }
+
+  void removeMatching(bool test(E element)) {
+    _filterMatching(test, true);
+  }
+
+  void retainMatching(bool test(E element)) {
+    _filterMatching(test, false);
+  }
+
+  void clear() {
+    _table._clear();
+  }
+
+  // Set.
+  bool isSubsetOf(Collection<E> collection) {
+    Set otherSet;
+    if (collection is Set) {
+      otherSet = collection;
+    } else {
+      otherSet = collection.toSet();
+    }
+    return otherSet.containsAll(this);
+  }
+
+  bool containsAll(Collection<E> collection) {
+    for (E element in collection) {
+      if (!this.contains(element)) return false;
+    }
+    return true;
+  }
+
+  Set<E> intersection(Collection<E> other) {
+    Set<E> result = new LinkedHashSet<E>();
+    for (E element in other) {
+      if (this.contains(element)) {
+        result.add(element);
+      }
+    }
+    return result;
+  }
+
+  String toString() => Collections.collectionToString(this);
+}
diff --git a/sdk/lib/collection/linked_hash_table.dart b/sdk/lib/collection/linked_hash_table.dart
new file mode 100644
index 0000000..fbf6172
--- /dev/null
+++ b/sdk/lib/collection/linked_hash_table.dart
@@ -0,0 +1,165 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+part of dart.collection;
+
+/** Unique marker object for the head of a linked list of entries. */
+class _LinkedHashTableHeadMarker {
+  const _LinkedHashTableHeadMarker();
+}
+
+const _LinkedHashTableHeadMarker _HEAD_MARKER =
+    const _LinkedHashTableHeadMarker();
+
+class _LinkedHashTable<K> extends _HashTable<K> {
+  static const _NEXT_INDEX = 1;
+  static const _PREV_INDEX = 2;
+  static const _HEAD_OFFSET = 0;
+
+  _LinkedHashTable(int initialCapacity) : super(initialCapacity);
+
+  int get _entrySize => 3;
+
+  List _createTable(int capacity) {
+    List result = new List.fixedLength(capacity * _entrySize);
+    result[_HEAD_OFFSET] = _HEAD_MARKER;
+    result[_HEAD_OFFSET + _NEXT_INDEX] = _HEAD_OFFSET;
+    result[_HEAD_OFFSET + _PREV_INDEX] = _HEAD_OFFSET;
+    return result;
+  }
+
+  int _next(int offset) => _table[offset + _NEXT_INDEX];
+  void _setNext(int offset, int to) { _table[offset + _NEXT_INDEX] = to; }
+
+  int _prev(int offset) => _table[offset + _PREV_INDEX];
+  void _setPrev(int offset, int to) { _table[offset + _PREV_INDEX] = to; }
+
+  void _linkLast(int offset) {
+    // Add entry at offset at end of double-linked list.
+    int last = _prev(_HEAD_OFFSET);
+    _setNext(offset, _HEAD_OFFSET);
+    _setPrev(offset, last);
+    _setNext(last, offset);
+    _setPrev(_HEAD_OFFSET, offset);
+  }
+
+  void _unlink(int offset) {
+    assert(offset != _HEAD_OFFSET);
+    int next = _next(offset);
+    int prev = _prev(offset);
+    _setNext(offset, null);
+    _setPrev(offset, null);
+    _setNext(prev, next);
+    _setPrev(next, prev);
+  }
+
+  /**
+   * Copies all non-free entries from the old table to the new empty table.
+   */
+  void _addAllEntries(List oldTable) {
+    int offset = oldTable[_HEAD_OFFSET + _NEXT_INDEX];
+    while (offset != _HEAD_OFFSET) {
+      Object object = oldTable[offset];
+      int nextOffset = oldTable[offset + _NEXT_INDEX];
+      int toOffset = _put(object);
+      _copyEntry(oldTable, offset, toOffset);
+      offset = nextOffset;
+    }
+  }
+
+  void _clear() {
+    if (_elementCount == 0) return;
+    _setNext(_HEAD_OFFSET, _HEAD_OFFSET);
+    _setPrev(_HEAD_OFFSET, _HEAD_OFFSET);
+    for (int i = _entrySize; i < _table.length; i++) {
+      _table[i] = null;
+    }
+    _entryCount = _deletedCount = 0;
+    _recordModification();
+  }
+
+  int _put(K key) {
+    int offset = _probeForAdd(_hashCodeOf(key), key);
+    Object oldEntry = _table[offset];
+    if (identical(oldEntry, _TOMBSTONE)) {
+      _deletedCount--;
+    } else if (oldEntry == null) {
+      _entryCount++;
+    } else {
+      return offset;
+    }
+    _recordModification();
+    _setKey(offset, key);
+    _linkLast(offset);
+    return offset;
+  }
+
+  void _deleteEntry(int offset) {
+    _unlink(offset);
+    _setKey(offset, _TOMBSTONE);
+    _deletedCount++;
+    _recordModification();
+  }
+}
+
+class _LinkedHashTableKeyIterable<K> extends Iterable<K> {
+  final _LinkedHashTable<K> _table;
+  _LinkedHashTableKeyIterable(this._table);
+  Iterator<K> get iterator => new _LinkedHashTableKeyIterator<K>(_table);
+
+  bool contains(Object value) => _table._get(value) >= 0;
+
+  int get length => _table._elementCount;
+}
+
+class _LinkedHashTableKeyIterator<K> extends _LinkedHashTableIterator<K> {
+  _LinkedHashTableKeyIterator(_LinkedHashTable<K> hashTable): super(hashTable);
+
+  K _getCurrent(int offset) => _hashTable._key(offset);
+}
+
+class _LinkedHashTableValueIterable<V> extends Iterable<V> {
+  final _LinkedHashTable _hashTable;
+  final int _valueIndex;
+  _LinkedHashTableValueIterable(this._hashTable, this._valueIndex);
+  Iterator<V> get iterator =>
+      new _LinkedHashTableValueIterator<V>(_hashTable, _valueIndex);
+  int get length => _hashTable._elementCount;
+}
+
+class _LinkedHashTableValueIterator<V> extends _LinkedHashTableIterator<V> {
+  final int _valueIndex;
+
+  _LinkedHashTableValueIterator(_LinkedHashTable hashTable, this._valueIndex)
+      : super(hashTable);
+
+  V _getCurrent(int offset) => _hashTable._table[offset + _valueIndex];
+}
+
+abstract class _LinkedHashTableIterator<T> implements Iterator<T> {
+  final _LinkedHashTable _hashTable;
+  final int _modificationCount;
+  int _offset;
+  T _current;
+
+  _LinkedHashTableIterator(_LinkedHashTable table)
+      : _hashTable = table,
+        _modificationCount = table._modificationCount,
+        _offset = table._next(_LinkedHashTable._HEAD_OFFSET);
+
+  bool moveNext() {
+    _hashTable._checkModification(_modificationCount);
+    if (_offset == _LinkedHashTable._HEAD_OFFSET) {
+      _current = null;
+      return false;
+    }
+    _current = _getCurrent(_offset);
+    _offset = _hashTable._next(_offset);
+    return true;
+  }
+
+  T _getCurrent(int offset);
+
+  T get current => _current;
+}
diff --git a/sdk/lib/collection/map.dart b/sdk/lib/collection/map.dart
deleted file mode 100644
index 1beaf2b..0000000
--- a/sdk/lib/collection/map.dart
+++ /dev/null
@@ -1,474 +0,0 @@
-// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-part of dart.collection;
-
-
-/**
- * Hash map version of the [Map] interface. A [HashMap] does not
- * provide any guarantees on the order of keys and values in [keys]
- * and [values].
- */
-abstract class HashMap<K, V> extends Map<K, V> {
-  /**
-   * Creates a map with the default implementation.
-   */
-  factory HashMap() => new _HashMapImpl<K, V>();
-
-  /**
-   * Creates a [HashMap] that contains all key value pairs of [other].
-   */
-  factory HashMap.from(Map<K, V> other) => new _HashMapImpl<K, V>.from(other);
-}
-
-/**
- * Hash map version of the [Map] interface that preserves insertion
- * order.
- */
-abstract class LinkedHashMap<K, V> extends HashMap<K, V> {
-  /**
-   * Creates a map with the default implementation.
-   */
-  factory LinkedHashMap() => new _LinkedHashMapImpl<K, V>();
-
-  /**
-   * Creates a [LinkedHashMap] that contains all key value pairs of [other].
-   */
-  factory LinkedHashMap.from(Map<K, V> other)
-    => new _LinkedHashMapImpl<K, V>.from(other);
-}
-
-
-// Hash map implementation with open addressing and quadratic probing.
-class _HashMapImpl<K, V> implements HashMap<K, V> {
-
-  // The [_keys] list contains the keys inserted in the map.
-  // The [_keys] list must be a raw list because it
-  // will contain both elements of type K, and the [_DELETED_KEY] of type
-  // [_DeletedKeySentinel].
-  // The alternative of declaring the [_keys] list as of type Object
-  // does not work, because the HashSetIterator constructor would fail:
-  //  HashSetIterator(HashSet<E> set)
-  //    : _nextValidIndex = -1,
-  //      _entries = set_._backingMap._keys {
-  //    _advance();
-  //  }
-  // With K being type int, for example, it would fail because
-  // List<Object> is not assignable to type List<int> of entries.
-  List _keys;
-
-  // The values inserted in the map. For a filled entry index in this
-  // list, there is always the corresponding key in the [keys_] list
-  // at the same entry index.
-  List<V> _values;
-
-  // The load limit is the number of entries we allow until we double
-  // the size of the lists.
-  int _loadLimit;
-
-  // The current number of entries in the map. Will never be greater
-  // than [_loadLimit].
-  int _numberOfEntries;
-
-  // The current number of deleted entries in the map.
-  int _numberOfDeleted;
-
-  // The sentinel when a key is deleted from the map.
-  static const _DeletedKeySentinel _DELETED_KEY = const _DeletedKeySentinel();
-
-  // The initial capacity of a hash map.
-  static const int _INITIAL_CAPACITY = 8;  // must be power of 2
-
-  _HashMapImpl() {
-    _numberOfEntries = 0;
-    _numberOfDeleted = 0;
-    _loadLimit = _computeLoadLimit(_INITIAL_CAPACITY);
-    _keys = new List.fixedLength(_INITIAL_CAPACITY);
-    _values = new List<V>.fixedLength(_INITIAL_CAPACITY);
-  }
-
-  factory _HashMapImpl.from(Map<K, V> other) {
-    Map<K, V> result = new _HashMapImpl<K, V>();
-    other.forEach((K key, V value) { result[key] = value; });
-    return result;
-  }
-
-  static int _computeLoadLimit(int capacity) {
-    return (capacity * 3) ~/ 4;
-  }
-
-  static int _firstProbe(int hashCode, int length) {
-    return hashCode & (length - 1);
-  }
-
-  static int _nextProbe(int currentProbe, int numberOfProbes, int length) {
-    return (currentProbe + numberOfProbes) & (length - 1);
-  }
-
-  int _probeForAdding(K key) {
-    if (key == null) throw new ArgumentError(null);
-    int hash = _firstProbe(key.hashCode, _keys.length);
-    int numberOfProbes = 1;
-    int initialHash = hash;
-    // insertionIndex points to a slot where a key was deleted.
-    int insertionIndex = -1;
-    while (true) {
-      // [existingKey] can be either of type [K] or [_DeletedKeySentinel].
-      Object existingKey = _keys[hash];
-      if (existingKey == null) {
-        // We are sure the key is not already in the set.
-        // If the current slot is empty and we didn't find any
-        // insertion slot before, return this slot.
-        if (insertionIndex < 0) return hash;
-        // If we did find an insertion slot before, return it.
-        return insertionIndex;
-      } else if (existingKey == key) {
-        // The key is already in the map. Return its slot.
-        return hash;
-      } else if ((insertionIndex < 0) &&
-                 (identical(existingKey, _DELETED_KEY))) {
-        // The slot contains a deleted element. Because previous calls to this
-        // method may not have had this slot deleted, we must continue iterate
-        // to find if there is a slot with the given key.
-        insertionIndex = hash;
-      }
-
-      // We did not find an insertion slot. Look at the next one.
-      hash = _nextProbe(hash, numberOfProbes++, _keys.length);
-      // _ensureCapacity has guaranteed the following cannot happen.
-      // assert(hash != initialHash);
-    }
-  }
-
-  int _probeForLookup(K key) {
-    if (key == null) throw new ArgumentError(null);
-    int hash = _firstProbe(key.hashCode, _keys.length);
-    int numberOfProbes = 1;
-    int initialHash = hash;
-    while (true) {
-      // [existingKey] can be either of type [K] or [_DeletedKeySentinel].
-      Object existingKey = _keys[hash];
-      // If the slot does not contain anything (in particular, it does not
-      // contain a deleted key), we know the key is not in the map.
-      if (existingKey == null) return -1;
-      // The key is in the map, return its index.
-      if (existingKey == key) return hash;
-      // Go to the next probe.
-      hash = _nextProbe(hash, numberOfProbes++, _keys.length);
-      // _ensureCapacity has guaranteed the following cannot happen.
-      // assert(hash != initialHash);
-    }
-  }
-
-  void _ensureCapacity() {
-    int newNumberOfEntries = _numberOfEntries + 1;
-    // Test if adding an element will reach the load limit.
-    if (newNumberOfEntries >= _loadLimit) {
-      _grow(_keys.length * 2);
-      return;
-    }
-
-    // Make sure that we don't have poor performance when a map
-    // contains lots of deleted entries: we _grow if
-    // there are more deleted entried than free entries.
-    int capacity = _keys.length;
-    int numberOfFreeOrDeleted = capacity - newNumberOfEntries;
-    int numberOfFree = numberOfFreeOrDeleted - _numberOfDeleted;
-    // assert(numberOfFree > 0);
-    if (_numberOfDeleted > numberOfFree) {
-      _grow(_keys.length);
-    }
-  }
-
-  static bool _isPowerOfTwo(int x) {
-    return ((x & (x - 1)) == 0);
-  }
-
-  void _grow(int newCapacity) {
-    assert(_isPowerOfTwo(newCapacity));
-    int capacity = _keys.length;
-    _loadLimit = _computeLoadLimit(newCapacity);
-    List oldKeys = _keys;
-    List<V> oldValues = _values;
-    _keys = new List.fixedLength(newCapacity);
-    _values = new List<V>.fixedLength(newCapacity);
-    for (int i = 0; i < capacity; i++) {
-      // [key] can be either of type [K] or [_DeletedKeySentinel].
-      Object key = oldKeys[i];
-      // If there is no key, we don't need to deal with the current slot.
-      if (key == null || identical(key, _DELETED_KEY)) {
-        continue;
-      }
-      V value = oldValues[i];
-      // Insert the {key, value} pair in their new slot.
-      int newIndex = _probeForAdding(key);
-      _keys[newIndex] = key;
-      _values[newIndex] = value;
-    }
-    _numberOfDeleted = 0;
-  }
-
-  void clear() {
-    _numberOfEntries = 0;
-    _numberOfDeleted = 0;
-    int length = _keys.length;
-    for (int i = 0; i < length; i++) {
-      _keys[i] = null;
-      _values[i] = null;
-    }
-  }
-
-  void operator []=(K key, V value) {
-    _ensureCapacity();
-    int index = _probeForAdding(key);
-    if ((_keys[index] == null) || (identical(_keys[index], _DELETED_KEY))) {
-      _numberOfEntries++;
-    }
-    _keys[index] = key;
-    _values[index] = value;
-  }
-
-  V operator [](K key) {
-    int index = _probeForLookup(key);
-    if (index < 0) return null;
-    return _values[index];
-  }
-
-  V putIfAbsent(K key, V ifAbsent()) {
-    int index = _probeForLookup(key);
-    if (index >= 0) return _values[index];
-
-    V value = ifAbsent();
-    this[key] = value;
-    return value;
-  }
-
-  V remove(K key) {
-    int index = _probeForLookup(key);
-    if (index >= 0) {
-      _numberOfEntries--;
-      V value = _values[index];
-      _values[index] = null;
-      // Set the key to the sentinel to not break the probing chain.
-      _keys[index] = _DELETED_KEY;
-      _numberOfDeleted++;
-      return value;
-    }
-    return null;
-  }
-
-  bool get isEmpty {
-    return _numberOfEntries == 0;
-  }
-
-  int get length {
-    return _numberOfEntries;
-  }
-
-  void forEach(void f(K key, V value)) {
-    Iterator<int> it = new _HashMapImplIndexIterator(this);
-    while (it.moveNext()) {
-      f(_keys[it.current], _values[it.current]);
-    }
-  }
-
-  Iterable<K> get keys => new _HashMapImplKeyIterable<K>(this);
-
-  Iterable<V> get values => new _HashMapImplValueIterable<V>(this);
-
-  bool containsKey(K key) {
-    return (_probeForLookup(key) != -1);
-  }
-
-  bool containsValue(V value) => values.contains(value);
-
-  String toString() {
-    return Maps.mapToString(this);
-  }
-}
-
-class _HashMapImplKeyIterable<E> extends Iterable<E> {
-  final _HashMapImpl _map;
-  _HashMapImplKeyIterable(this._map);
-
-  Iterator<E> get iterator => new _HashMapImplKeyIterator<E>(_map);
-}
-
-class _HashMapImplValueIterable<E> extends Iterable<E> {
-  final _HashMapImpl _map;
-  _HashMapImplValueIterable(this._map);
-
-  Iterator<E> get iterator => new _HashMapImplValueIterator<E>(_map);
-}
-
-abstract class _HashMapImplIterator<E> implements Iterator<E> {
-  final _HashMapImpl _map;
-  int _index = -1;
-  E _current;
-
-  _HashMapImplIterator(this._map);
-
-  E _computeCurrentFromIndex(int index, List keys, List values);
-
-  bool moveNext() {
-    int length = _map._keys.length;
-    int newIndex = _index + 1;
-    while (newIndex < length) {
-      var key = _map._keys[newIndex];
-      if ((key != null) && (!identical(key, _HashMapImpl._DELETED_KEY))) {
-        _current = _computeCurrentFromIndex(newIndex, _map._keys, _map._values);
-        _index = newIndex;
-        return true;
-      }
-      newIndex++;
-    }
-    _index = length;
-    _current = null;
-    return false;
-  }
-
-  E get current => _current;
-}
-
-class _HashMapImplKeyIterator<E> extends _HashMapImplIterator<E> {
-  _HashMapImplKeyIterator(_HashMapImpl map) : super(map);
-
-  E _computeCurrentFromIndex(int index, List keys, List values) {
-    return keys[index];
-  }
-}
-
-class _HashMapImplValueIterator<E> extends _HashMapImplIterator<E> {
-  _HashMapImplValueIterator(_HashMapImpl map) : super(map);
-
-  E _computeCurrentFromIndex(int index, List keys, List values) {
-    return values[index];
-  }
-}
-
-class _HashMapImplIndexIterator extends _HashMapImplIterator<int> {
-  _HashMapImplIndexIterator(_HashMapImpl map) : super(map);
-
-  int _computeCurrentFromIndex(int index, List keys, List values) {
-    return index;
-  }
-}
-
-/**
- * A singleton sentinel used to represent when a key is deleted from the map.
- * We can't use [: const Object() :] as a sentinel because it would end up
- * canonicalized and then we cannot distinguish the deleted key from the
- * canonicalized [: Object() :].
- */
-class _DeletedKeySentinel {
-  const _DeletedKeySentinel();
-}
-
-
-/**
- * This class represents a pair of two objects, used by LinkedHashMap
- * to store a {key, value} in a list.
- */
-class _KeyValuePair<K, V> {
-  _KeyValuePair(this.key, this.value) {}
-
-  final K key;
-  V value;
-}
-
-/**
- * A LinkedHashMap is a hash map that preserves the insertion order
- * when iterating over the keys or the values. Updating the value of a
- * key does not change the order.
- */
-class _LinkedHashMapImpl<K, V> implements LinkedHashMap<K, V> {
-  DoubleLinkedQueue<_KeyValuePair<K, V>> _list;
-  HashMap<K, DoubleLinkedQueueEntry<_KeyValuePair<K, V>>> _map;
-
-  _LinkedHashMapImpl() {
-    _map = new HashMap<K, DoubleLinkedQueueEntry<_KeyValuePair<K, V>>>();
-    _list = new DoubleLinkedQueue<_KeyValuePair<K, V>>();
-  }
-
-  factory _LinkedHashMapImpl.from(Map<K, V> other) {
-    Map<K, V> result = new _LinkedHashMapImpl<K, V>();
-    other.forEach((K key, V value) { result[key] = value; });
-    return result;
-  }
-
-  void operator []=(K key, V value) {
-    if (_map.containsKey(key)) {
-      _map[key].element.value = value;
-    } else {
-      _list.addLast(new _KeyValuePair<K, V>(key, value));
-      _map[key] = _list.lastEntry();
-    }
-  }
-
-  V operator [](K key) {
-    DoubleLinkedQueueEntry<_KeyValuePair<K, V>> entry = _map[key];
-    if (entry == null) return null;
-    return entry.element.value;
-  }
-
-  V remove(K key) {
-    DoubleLinkedQueueEntry<_KeyValuePair<K, V>> entry = _map.remove(key);
-    if (entry == null) return null;
-    entry.remove();
-    return entry.element.value;
-  }
-
-  V putIfAbsent(K key, V ifAbsent()) {
-    V value = this[key];
-    if ((this[key] == null) && !(containsKey(key))) {
-      value = ifAbsent();
-      this[key] = value;
-    }
-    return value;
-  }
-
-  Iterable<K> get keys {
-    return new MappedIterable<_KeyValuePair<K, V>, K>(
-        _list, (_KeyValuePair<K, V> entry) => entry.key);
-  }
-
-
-  Iterable<V> get values {
-    return new MappedIterable<_KeyValuePair<K, V>, V>(
-        _list, (_KeyValuePair<K, V> entry) => entry.value);
-  }
-
-  void forEach(void f(K key, V value)) {
-    _list.forEach((_KeyValuePair<K, V> entry) {
-      f(entry.key, entry.value);
-    });
-  }
-
-  bool containsKey(K key) {
-    return _map.containsKey(key);
-  }
-
-  bool containsValue(V value) {
-    return _list.any((_KeyValuePair<K, V> entry) {
-      return (entry.value == value);
-    });
-  }
-
-  int get length {
-    return _map.length;
-  }
-
-  bool get isEmpty {
-    return length == 0;
-  }
-
-  void clear() {
-    _map.clear();
-    _list.clear();
-  }
-
-  String toString() {
-    return Maps.mapToString(this);
-  }
-}
diff --git a/sdk/lib/collection/set.dart b/sdk/lib/collection/set.dart
deleted file mode 100644
index 9363676..0000000
--- a/sdk/lib/collection/set.dart
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-part of dart.collection;
-
-
-
-class HashSet<E> extends Collection<E> implements Set<E> {
-  // The map backing this set. The associations in this map are all
-  // of the form element -> element. If a value is not in the map,
-  // then it is not in the set.
-  final _HashMapImpl<E, E> _backingMap;
-
-  HashSet() : _backingMap = new _HashMapImpl<E, E>();
-
-  /**
-   * Creates a [Set] that contains all elements of [other].
-   */
-  factory HashSet.from(Iterable<E> other) {
-    Set<E> set = new HashSet<E>();
-    for (final e in other) {
-      set.add(e);
-    }
-    return set;
-  }
-
-  void clear() {
-    _backingMap.clear();
-  }
-
-  void add(E value) {
-    _backingMap[value] = value;
-  }
-
-  bool remove(Object value) {
-    if (!_backingMap.containsKey(value)) return false;
-    _backingMap.remove(value);
-    return true;
-  }
-
-  bool contains(E value) {
-    return _backingMap.containsKey(value);
-  }
-
-  Set<E> intersection(Collection<E> collection) {
-    Set<E> result = new Set<E>();
-    collection.forEach((E value) {
-      if (contains(value)) result.add(value);
-    });
-    return result;
-  }
-
-  bool isSubsetOf(Collection<E> other) {
-    return new Set<E>.from(other).containsAll(this);
-  }
-
-  bool containsAll(Collection<E> collection) {
-    return collection.every((E value) {
-      return contains(value);
-    });
-  }
-
-  void forEach(void f(E element)) {
-    _backingMap.forEach((E key, E value) {
-      f(key);
-    });
-  }
-
-  bool get isEmpty {
-    return _backingMap.isEmpty;
-  }
-
-  int get length {
-    return _backingMap.length;
-  }
-
-  Iterator<E> get iterator => new _HashSetIterator<E>(this);
-
-  String toString() {
-    return Collections.collectionToString(this);
-  }
-}
-
-class _HashSetIterator<E> implements Iterator<E> {
-
-  _HashSetIterator(HashSet<E> set)
-    : _keysIterator = set._backingMap._keys.iterator;
-
-  E get current {
-    var result = _keysIterator.current;
-    if (identical(result, _HashMapImpl._DELETED_KEY)) {
-      // TODO(floitsch): improve the error reporting.
-      throw new StateError("Concurrent modification.");
-    }
-    return result;
-  }
-
-  bool moveNext() {
-    bool result;
-    do {
-      result = _keysIterator.moveNext();
-    } while (result &&
-             (_keysIterator.current == null ||
-              identical(_keysIterator.current, _HashMapImpl._DELETED_KEY)));
-    return result;
-  }
-
-  Iterator _keysIterator;
-}
diff --git a/sdk/lib/collection/splay_tree.dart b/sdk/lib/collection/splay_tree.dart
index d9eca77..31be011 100644
--- a/sdk/lib/collection/splay_tree.dart
+++ b/sdk/lib/collection/splay_tree.dart
@@ -9,12 +9,12 @@
  * and right children in the tree.
  */
 class SplayTreeNode<K, V> {
-  SplayTreeNode(K this.key, V this.value);
-
-  K key;
+  final K key;
   V value;
   SplayTreeNode<K, V> left;
   SplayTreeNode<K, V> right;
+
+  SplayTreeNode(K this.key, V this.value);
 }
 
 /**
@@ -40,10 +40,23 @@
   // Number of elements in the splay tree.
   int _count;
 
-  SplayTreeMap() {
-    _dummy = new SplayTreeNode<K, V>(null, null);
+  /**
+   * Counter incremented whenever the keys in the map changes.
+   *
+   * Used to detect concurrent modifications.
+   */
+  int _modificationCount = 0;
+  /**
+   * Counter incremented whenever the tree structure changes.
+   *
+   * Used to detect that an in-place traversal cannot use
+   * cached information that relies on the tree structure.
+   */
+  int _splayCount = 0;
+
+  SplayTreeMap() :
+    _dummy = new SplayTreeNode<K, V>(null, null),
     _count = 0;
-  }
 
   /**
    * Perform the splay operation for the given key. Moves the node with
@@ -51,9 +64,12 @@
    * key, the last node on the search path is moved to the top of the
    * tree. This is the simplified top-down splaying algorithm from:
    * "Self-adjusting Binary Search Trees" by Sleator and Tarjan.
+   *
+   * Returns the result of comparing the new root of the tree to [key].
+   * Returns -1 if the table is empty.
    */
-  void splay_(K key) {
-    if (isEmpty) return;
+  int _splay(K key) {
+    if (_root == null) return -1;
 
     // The right child of the dummy node will hold
     // the L tree of the algorithm.  The left child of the dummy node
@@ -62,11 +78,13 @@
     SplayTreeNode<K, V> left = _dummy;
     SplayTreeNode<K, V> right = _dummy;
     SplayTreeNode<K, V> current = _root;
+    int comp;
     while (true) {
-      int comp = key.compareTo(current.key);
-      if (comp < 0) {
+      comp = current.key.compareTo(key);
+      if (comp > 0) {
         if (current.left == null) break;
-        if (key.compareTo(current.left.key) < 0) {
+        comp = current.left.key.compareTo(key);
+        if (comp > 0) {
           // Rotate right.
           SplayTreeNode<K, V> tmp = current.left;
           current.left = tmp.right;
@@ -78,9 +96,10 @@
         right.left = current;
         right = current;
         current = current.left;
-      } else if (comp > 0) {
+      } else if (comp < 0) {
         if (current.right == null) break;
-        if (key.compareTo(current.right.key) > 0) {
+        comp = current.right.key.compareTo(key);
+        if (comp < 0) {
           // Rotate left.
           SplayTreeNode<K, V> tmp = current.right;
           current.right = tmp.left;
@@ -105,20 +124,22 @@
 
     _dummy.right = null;
     _dummy.left = null;
+    _splayCount++;
+    return comp;
   }
 
   V operator [](K key) {
-    if (!isEmpty) {
-      splay_(key);
-      if (_root.key.compareTo(key) == 0) return _root.value;
+    if (_root != null) {
+      int comp = _splay(key);
+      if (comp == 0) return _root.value;
     }
     return null;
   }
 
   V remove(K key) {
-    if (isEmpty) return null;
-    splay_(key);
-    if (_root.key.compareTo(key) != 0) return null;
+    if (_root == null) return null;
+    int comp = _splay(key);
+    if (comp != 0) return null;
     V value = _root.value;
 
     _count--;
@@ -129,31 +150,43 @@
       SplayTreeNode<K, V> right = _root.right;
       _root = _root.left;
       // Splay to make sure that the new root has an empty right child.
-      splay_(key);
+      _splay(key);
       // Insert the original right child as the right child of the new
       // root.
       _root.right = right;
     }
+    _modificationCount++;
     return value;
   }
 
   void operator []=(K key, V value) {
-    if (isEmpty) {
+    if (_root == null) {
       _count++;
       _root = new SplayTreeNode(key, value);
+      _modificationCount++;
       return;
     }
     // Splay on the key to move the last node on the search path for
     // the key to the root of the tree.
-    splay_(key);
-    if (_root.key.compareTo(key) == 0) {
+    int comp = _splay(key);
+    if (comp == 0) {
       _root.value = value;
       return;
     }
+    _addNewRoot(key, value, comp);
+  }
+
+  /**
+   * Adds a new root node with the given [key] or [value].
+   *
+   * The [comp] value is the result of comparing the existing root's key
+   * with key.
+   */
+  void _addNewRoot(K key, V value, int comp) {
     SplayTreeNode<K, V> node = new SplayTreeNode(key, value);
     // assert(_count >= 0);
     _count++;
-    if (key.compareTo(_root.key) > 0) {
+    if (comp < 0) {
       node.left = _root;
       node.right = _root.right;
       _root.right = null;
@@ -163,12 +196,34 @@
       _root.left = null;
     }
     _root = node;
+    _modificationCount++;
   }
 
   V putIfAbsent(K key, V ifAbsent()) {
-    if (containsKey(key)) return this[key];
+    if (_root == null) {
+      V value = ifAbsent();
+      if (_root != null) {
+        throw new ConcurrentModificationError(this);
+      }
+      _root = new SplayTreeNode(key, value);
+      _count++;
+      _modificationCount++;
+      return value;
+    }
+    int comp = _splay(key);
+    if (comp == 0) return _root.value;
+    int modificationCount = _modificationCount;
+    int splayCount = _splayCount;
     V value = ifAbsent();
-    this[key] = value;
+    if (modificationCount != _modificationCount) {
+      throw new ConcurrentModificationError(this);
+    }
+    if (splayCount != _splayCount) {
+      comp = _splay(key);
+      // Key is still not there, otherwise _modificationCount would be changed.
+      assert(comp != 0);
+    }
+    _addNewRoot(key, value, comp);
     return value;
   }
 
@@ -179,21 +234,11 @@
   }
 
   void forEach(void f(K key, V value)) {
-    List<SplayTreeNode<K, V>> list = new List<SplayTreeNode<K, V>>();
-    SplayTreeNode<K, V> current = _root;
-    while (current != null) {
-      if (current.left != null) {
-        list.add(current);
-        current = current.left;
-      } else {
-        f(current.key, current.value);
-        while (current.right == null) {
-          if (list.isEmpty) return;
-          current = list.removeLast();
-          f(current.key, current.value);
-        }
-        current = current.right;
-      }
+    Iterator<SplayTreeNode<K, V>> nodes =
+        new _SplayTreeNodeIterator<K, V>(this);
+    while (nodes.moveNext()) {
+      SplayTreeNode<K, V> node = nodes.current;
+      f(node.key, node.value);
     }
   }
 
@@ -207,11 +252,7 @@
   }
 
   bool containsKey(K key) {
-    if (!isEmpty) {
-      splay_(key);
-      if (_root.key.compareTo(key) == 0) return true;
-    }
-    return false;
+    return _splay(key) == 0;
   }
 
   bool containsValue(V value) {
@@ -219,22 +260,16 @@
     bool visit(SplayTreeNode node) {
       if (node == null) return false;
       if (node.value == value) return true;
+      // TODO(lrn): Do we want to handle the case where node.value.operator==
+      // modifies the map?
       return visit(node.left) || visit(node.right);
     }
     return visit(_root);
   }
 
-  Collection<K> get keys {
-    List<K> list = new List<K>();
-    forEach((K k, V v) { list.add(k); });
-    return list;
-  }
+  Iterable<K> get keys => new _SplayTreeKeyIterable(this);
 
-  Collection<V> get values {
-    List<V> list = new List<V>();
-    forEach((K k, V v) { list.add(v); });
-    return list;
-  }
+  Iterable<V> get values => new _SplayTreeValueIterable(this);
 
   String toString() {
     return Maps.mapToString(this);
@@ -251,7 +286,7 @@
     }
     // Maybe implement a splay-method that can splay the minimum without
     // performing comparisons.
-    splay_(node.key);
+    _splay(node.key);
     return node.key;
   }
 
@@ -266,7 +301,7 @@
     }
     // Maybe implement a splay-method that can splay the maximum without
     // performing comparisons.
-    splay_(node.key);
+    _splay(node.key);
     return node.key;
   }
 
@@ -275,17 +310,15 @@
    * [null] if no key was not found.
    */
   K lastKeyBefore(K key) {
-    splay_(key);
-    K visit(SplayTreeNode node, K ifEmpty) {
-      if (node == null) return ifEmpty;
-      if (node.key.compareTo(key) >= 0) {
-        return visit(node.left, ifEmpty);
-      }
-      if (node.key.compareTo(key) < 0) {
-        return visit(node.right, node.key);
-      }
+    if (_root == null) return null;
+    int comp = _splay(key);
+    if (comp < 0) return _root.key;
+    SplayTreeNode<K, V> node = _root.left;
+    if (node == null) return null;
+    while (node.right != null) {
+      node = node.right;
     }
-    return visit(_root, null);
+    return node.key;
   }
 
   /**
@@ -293,16 +326,138 @@
    * [null] if no key was not found.
    */
   K firstKeyAfter(K key) {
-    splay_(key);
-    K visit(SplayTreeNode node, K ifEmpty) {
-      if (node == null) return ifEmpty;
-      if (node.key.compareTo(key) > 0) {
-        return visit(node.left, node.key);
-      }
-      if (node.key.compareTo(key) <= 0) {
-        return visit(node.right, ifEmpty);
-      }
+    if (_root == null) return null;
+    int comp = _splay(key);
+    if (comp > 0) return _root.key;
+    SplayTreeNode<K, V> node = _root.right;
+    if (node == null) return null;
+    while (node.left != null) {
+      node = node.left;
     }
-    return visit(_root, null);
+    return node.key;
   }
 }
+
+abstract class _SplayTreeIterator<T> implements Iterator<T> {
+  final SplayTreeMap _map;
+  /**
+   * Worklist of nodes to visit.
+   *
+   * These nodes have been passed over on the way down in a
+   * depth-first left-to-right traversal. Visiting each node,
+   * and their right subtrees will visit the remainder of
+   * the nodes of a full traversal.
+   *
+   * Only valid as long as the original tree map isn't reordered.
+   */
+  final List<SplayTreeNode> _workList = <SplayTreeNode>[];
+
+  /**
+   * Original modification counter of [_map].
+   *
+   * Incremented on [_map] when a key is added or removed.
+   * If it changes, iteration is aborted.
+   */
+  final int _modificationCount;
+
+  /**
+   * Count of splay operations on [_map] when [_workList] was built.
+   *
+   * If the splay count on [_map] increases, [_workList] becomes invalid.
+   */
+  int _splayCount;
+
+  /** Current node. */
+  SplayTreeNode _currentNode;
+
+  _SplayTreeIterator(SplayTreeMap map)
+      : _map = map,
+        _modificationCount = map._modificationCount,
+        _splayCount = map._splayCount {
+    _findLeftMostDescendent(map._root);
+  }
+
+  T get current {
+    if (_currentNode == null) return null;
+    return _getValue(_currentNode);
+  }
+
+  void _findLeftMostDescendent(SplayTreeNode node) {
+    while (node != null) {
+      _workList.add(node);
+      node = node.left;
+    }
+  }
+
+  /**
+   * Called when the tree structure of the map has changed.
+   *
+   * This can be caused by a splay operation.
+   * If the key-set changes, iteration is aborted before getting
+   * here, so we know that the keys are the same as before, it's
+   * only the tree that has been reordered.
+   */
+  void _rebuildWorkList(SplayTreeNode currentNode) {
+    assert(!_workList.isEmpty);
+    _workList.clear();
+    if (currentNode == null) {
+      _findLeftMostDescendent(_map._root);
+    } else {
+      _map._splay(currentNode.key);
+      _findLeftMostDescendent(_map._root.right);
+      assert(!_workList.isEmpty);
+    }
+  }
+
+  bool moveNext() {
+    if (_modificationCount != _map._modificationCount) {
+      throw new ConcurrentModificationError(_map);
+    }
+    // Picks the next element in the worklist as current.
+    // Updates the worklist with the left-most path of the current node's
+    // right-hand child.
+    // If the worklist is no longer valid (after a splay), it is rebuild
+    // from scratch.
+    if (_workList.isEmpty) {
+      _currentNode = null;
+      return false;
+    }
+    if (_map._splayCount != _splayCount) {
+      _rebuildWorkList(_currentNode);
+    }
+    _currentNode = _workList.removeLast();
+    _findLeftMostDescendent(_currentNode.right);
+    return true;
+  }
+
+  T _getValue(SplayTreeNode node);
+}
+
+
+class _SplayTreeKeyIterable<K, V> extends Iterable<K> {
+  SplayTreeMap<K, V> _map;
+  _SplayTreeKeyIterable(this._map);
+  Iterator<K> get iterator => new _SplayTreeKeyIterator<K, V>(_map);
+}
+
+class _SplayTreeValueIterable<K, V> extends Iterable<V> {
+  SplayTreeMap<K, V> _map;
+  _SplayTreeValueIterable(this._map) ;
+  Iterator<V> get iterator => new _SplayTreeValueIterator<K, V>(_map);
+}
+
+class _SplayTreeKeyIterator<K, V> extends _SplayTreeIterator<K> {
+  _SplayTreeKeyIterator(SplayTreeMap<K, V> map): super(map);
+  K _getValue(SplayTreeNode node) => node.key;
+}
+
+class _SplayTreeValueIterator<K, V> extends _SplayTreeIterator<V> {
+  _SplayTreeValueIterator(SplayTreeMap<K, V> map): super(map);
+  V _getValue(SplayTreeNode node) => node.value;
+}
+
+class _SplayTreeNodeIterator<K, V>
+    extends _SplayTreeIterator<SplayTreeNode<K, V>> {
+  _SplayTreeNodeIterator(SplayTreeMap<K, V> map): super(map);
+  SplayTreeNode<K, V> _getValue(SplayTreeNode node) => node;
+}
diff --git a/sdk/lib/core/map.dart b/sdk/lib/core/map.dart
index 8b27b3e..de3807f 100644
--- a/sdk/lib/core/map.dart
+++ b/sdk/lib/core/map.dart
@@ -47,6 +47,9 @@
    * If [key] is not associated to a value, calls [ifAbsent] and
    * updates the map by mapping [key] to the value returned by
    * [ifAbsent]. Returns the value in the map.
+   *
+   * It is an error to add or remove keys from map during the call to
+   * [ifAbsent].
    */
   V putIfAbsent(K key, V ifAbsent());
 
@@ -65,6 +68,8 @@
 
   /**
    * Applies [f] to each {key, value} pair of the map.
+   *
+   * It is an error to add or remove keys from the map during iteration.
    */
   void forEach(void f(K key, V value));
 
diff --git a/sdk/lib/io/http_impl.dart b/sdk/lib/io/http_impl.dart
index 224c1e7..70c6c53 100644
--- a/sdk/lib/io/http_impl.dart
+++ b/sdk/lib/io/http_impl.dart
@@ -1917,14 +1917,15 @@
 
   void shutdown({bool force: false}) {
     if (force) _closeQueue.shutdown();
-    _openSockets.forEach((String key, Queue<_SocketConnection> connections) {
+    new Map.from(_openSockets).forEach(
+        (String key, Queue<_SocketConnection> connections) {
       while (!connections.isEmpty) {
         _SocketConnection socketConn = connections.removeFirst();
         socketConn._socket.close();
       }
     });
     if (force) {
-      _activeSockets.forEach((_SocketConnection socketConn) {
+      _activeSockets.toList().forEach((_SocketConnection socketConn) {
         socketConn._httpClientConnection._onClientShutdown();
         socketConn._close();
       });
diff --git a/tests/co19/co19-dart2dart.status b/tests/co19/co19-dart2dart.status
index 21a8cce..fc61890 100644
--- a/tests/co19/co19-dart2dart.status
+++ b/tests/co19/co19-dart2dart.status
@@ -657,3 +657,20 @@
 LibTest/core/Queue/removeLast_A02_t01: Fail # Moved collection classes from core to collection. co19 issue 371.
 LibTest/core/Set/Set.from_A01_t01: Fail # Moved collection classes from core to collection. co19 issue 371.
 Language/14_Types/4_Interface_Types_A08_t06: Fail # Moved collection classes from core to collection. co19 issue 371.
+
+# Doesn't expect null to be allowed in Set or Map keys (issue 377).
+LibTest/core/Map/containsKey_A01_t02: Fail
+LibTest/core/Map/operator_subscript_A01_t02: Fail
+LibTest/core/Map/operator_subscripted_assignment_A01_t02: Fail
+LibTest/core/Map/remove_A01_t02: Fail
+LibTest/core/Set/add_A01_t02: Fail
+LibTest/core/Set/addAll_A01_t02: Fail
+LibTest/core/Set/contains_A01_t02: Fail
+LibTest/core/Set/containsAll_A01_t02: Fail
+LibTest/core/Set/intersection_A03_t01: Fail
+LibTest/core/Set/isSubsetOf_A01_t02: Fail
+LibTest/core/Set/remove_A01_t02: Fail
+LibTest/core/Set/removeAll_A01_t02: Fail
+
+# Doesn't expect concurrent modification error (issue 376).
+LibTest/core/Map/forEach_A01_t07: Fail
diff --git a/tests/co19/co19-dart2js.status b/tests/co19/co19-dart2js.status
index a54182d..8da7c21 100644
--- a/tests/co19/co19-dart2js.status
+++ b/tests/co19/co19-dart2js.status
@@ -787,3 +787,20 @@
 Language/14_Types/5_Function_Types_A03_t06: Fail # dartbug.com/7342
 Language/14_Types/5_Function_Types_A03_t10: Fail # dartbug.com/7342
 Language/14_Types/5_Function_Types_A03_t11: Fail # dartbug.com/7342
+
+# Doesn't expect null to be allowed in Set or Map keys (issue 377).
+LibTest/core/Map/containsKey_A01_t02: Fail
+LibTest/core/Map/operator_subscript_A01_t02: Fail
+LibTest/core/Map/operator_subscripted_assignment_A01_t02: Fail
+LibTest/core/Map/remove_A01_t02: Fail
+LibTest/core/Set/add_A01_t02: Fail
+LibTest/core/Set/addAll_A01_t02: Fail
+LibTest/core/Set/contains_A01_t02: Fail
+LibTest/core/Set/containsAll_A01_t02: Fail
+LibTest/core/Set/intersection_A03_t01: Fail
+LibTest/core/Set/isSubsetOf_A01_t02: Fail
+LibTest/core/Set/remove_A01_t02: Fail
+LibTest/core/Set/removeAll_A01_t02: Fail
+
+# Doesn't expect concurrent modification error (issue 376).
+LibTest/core/Map/forEach_A01_t07: Fail
diff --git a/tests/co19/co19-runtime.status b/tests/co19/co19-runtime.status
index 09f39d0..6a312f5 100644
--- a/tests/co19/co19-runtime.status
+++ b/tests/co19/co19-runtime.status
@@ -504,6 +504,23 @@
 LibTest/core/Queue/removeLast_A02_t01: Fail # Moved collection classes from core to collection. co19 issue 371.
 LibTest/core/Set/Set.from_A01_t01: Fail # Moved collection classes from core to collection. co19 issue 371.
 
+# Doesn't expect null to be allowed in Set or Map keys (issue 377).
+LibTest/core/Map/containsKey_A01_t02: Fail
+LibTest/core/Map/operator_subscript_A01_t02: Fail
+LibTest/core/Map/operator_subscripted_assignment_A01_t02: Fail
+LibTest/core/Map/remove_A01_t02: Fail
+LibTest/core/Set/add_A01_t02: Fail
+LibTest/core/Set/addAll_A01_t02: Fail
+LibTest/core/Set/contains_A01_t02: Fail
+LibTest/core/Set/containsAll_A01_t02: Fail
+LibTest/core/Set/intersection_A03_t01: Fail
+LibTest/core/Set/isSubsetOf_A01_t02: Fail
+LibTest/core/Set/remove_A01_t02: Fail
+LibTest/core/Set/removeAll_A01_t02: Fail
+
+# Doesn't expect concurrent modification error (issue 376).
+LibTest/core/Map/forEach_A01_t07: Fail
+
 [ $compiler == none && $runtime == vm && $unchecked ]
 LibTest/core/Future/chain_A02_t05: Fail # Future is in async library. co19 issue 367
 LibTest/core/Future/transform_A02_t04: Fail # Future is in async library. co19 issue 367
diff --git a/tests/corelib/hash_map2_test.dart b/tests/corelib/hash_map2_test.dart
new file mode 100644
index 0000000..9f81c9f
--- /dev/null
+++ b/tests/corelib/hash_map2_test.dart
@@ -0,0 +1,289 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Tests of hash map behavior, with focus in iteration and concurrent
+// modification errors.
+
+library hash_map2_test;
+import 'dart:collection';
+
+testMap(Map newMap(), Map newMapFrom(Map map)) {
+  Map gen(int from, int to) {
+    Map map = new LinkedHashMap();
+    for (int i = from; i < to; i++) map[i] = i;
+    return map;
+  }
+
+  bool odd(int n) => (n & 1) == 1;
+  bool even(int n) => (n & 1) == 0;
+  void addAll(Map toMap, Map fromMap) {
+    fromMap.forEach((k, v) { toMap[k] = v; });
+  }
+
+  {  // Test growing to largish capacity.
+    Map map = newMap();
+
+    for (int i = 0; i < 256; i++) {
+      map[i] = i;
+    }
+    addAll(map, gen(256, 512));
+    addAll(map, newMapFrom(gen(512, 1000)));
+    Expect.equals(1000, map.length);
+
+    // Remove half.
+    for (int i = 0; i < 1000; i += 2) map.remove(i);
+    Expect.equals(500, map.length);
+    Expect.isFalse(map.keys.any(even));
+    Expect.isTrue(map.keys.every(odd));
+
+    // Re-add all.
+    addAll(map, gen(0, 1000));
+    Expect.equals(1000, map.length);
+  }
+
+  {  // Test having many deleted elements.
+    Map map = newMap();
+    map[0] = 0;
+    for (int i = 0; i < 1000; i++) {
+      map[i + 1] = i + 1;
+      map.remove(i);
+      Expect.equals(1, map.length);
+    }
+  }
+
+  {  // Test having many elements with same hashCode
+    Map map = newMap();
+    for (int i = 0; i < 1000; i++) {
+      map[new BadHashCode()] = 0;
+    }
+    Expect.equals(1000, map.length);
+  }
+
+  {  // Check concurrent modification
+    Map map = newMap()..[0] = 0..[1] = 1;
+
+    {  // Test adding before a moveNext.
+      Iterator iter = map.keys.iterator;
+      iter.moveNext();
+      map[1] = 9;  // Updating existing key isn't a modification.
+      iter.moveNext();
+      map[2] = 2;
+      Expect.throws(iter.moveNext, (e) => e is Error);
+    }
+
+    {  // Test adding after last element.
+      Iterator iter = map.keys.iterator;
+      Expect.equals(3, map.length);
+      iter.moveNext();
+      iter.moveNext();
+      iter.moveNext();
+      map[3] = 3;
+      Expect.throws(iter.moveNext, (e) => e is Error);
+    }
+
+    {  // Test removing during iteration.
+      Iterator iter = map.keys.iterator;
+      iter.moveNext();
+      map.remove(1000);  // Not a modification if it's not there.
+      iter.moveNext();
+      int n = iter.current;
+      map.remove(n);
+      // Removing doesn't change current.
+      Expect.equals(n, iter.current);
+      Expect.throws(iter.moveNext, (e) => e is Error);
+    }
+
+    {  // Test removing after last element.
+      Iterator iter = map.keys.iterator;
+      Expect.equals(3, map.length);
+      iter.moveNext();
+      iter.moveNext();
+      iter.moveNext();
+      int n = iter.current;
+      map.remove(n);
+      // Removing doesn't change current.
+      Expect.equals(n, iter.current);
+      Expect.throws(iter.moveNext, (e) => e is Error);
+    }
+
+    {  // Test that updating value of existing key doesn't cause concurrent
+       // modification error.
+      Iterator iter = map.keys.iterator;
+      Expect.equals(2, map.length);
+      iter.moveNext();
+      int n = iter.current;
+      map[n] = n * 2;
+      iter.moveNext();
+      Expect.equals(map[iter.current], iter.current);
+    }
+
+    {  // Test that modification during putIfAbsent is not an error.
+      map.putIfAbsent(4, () {
+        map[5] = 5;
+        map[4] = -1;
+        return 4;
+      });
+      Expect.equals(4, map[4]);
+      Expect.equals(5, map[5]);
+    }
+
+    {  // Check adding many existing keys isn't considered modification.
+      Map map2 = newMap();
+      for (var key in map.keys) {
+        map2[key] = map[key] + 1;
+      }
+      Iterator iter = map.keys.iterator;
+      addAll(map, map2);
+      // Shouldn't throw.
+      iter.moveNext();
+    }
+  }
+
+  {  // Regression test for bug in putIfAbsent where adding an element
+     // that make the table grow, can be lost.
+    Map map = newMap();
+    map.putIfAbsent("S", () => 0);
+    map.putIfAbsent("T", () => 0);
+    map.putIfAbsent("U", () => 0);
+    map.putIfAbsent("C", () => 0);
+    map.putIfAbsent("a", () => 0);
+    map.putIfAbsent("b", () => 0);
+    map.putIfAbsent("n", () => 0);
+    Expect.isTrue(map.containsKey("n"));
+  }
+
+  {  // Check that putIfAbsent works just as well as put.
+    Map map = newMap();
+    for (int i = 0; i < 128; i++) {
+      map.putIfAbsent(i, () => i);
+      Expect.isTrue(map.containsKey(i));
+      map.putIfAbsent(i >> 1, () => -1);  // Never triggers.
+    }
+    for (int i = 0; i < 128; i++) {
+      Expect.equals(i, map[i]);
+    }
+  }
+
+  {  // Check that updating existing elements is not a modification.
+     // This must be the case even if the underlying data structure is
+     // nearly full.
+     for (int i = 1; i < 128; i++) {
+        // Create maps of different sizes, some of which should be
+        // at a limit of the underlying data structure.
+        Map map = newMapFrom(gen(0, i));
+
+        // ForEach-iteration.
+        map.forEach((key, v) {
+          Expect.equals(key, map[key]);
+          map[key] = key + 1;
+          map.remove(1000);  // Removing something not there.
+          map.putIfAbsent(key, () => Expect.fail("SHOULD NOT BE ABSENT"));
+          // Doesn't cause ConcurrentModificationError.
+        });
+
+        // for-in iteration.
+        for (int key in map.keys) {
+          Expect.equals(key + 1, map[key]);
+          map[key] = map[key] + 1;
+          map.remove(1000);  // Removing something not there.
+          map.putIfAbsent(key, () => Expect.fail("SHOULD NOT BE ABSENT"));
+          // Doesn't cause ConcurrentModificationError.
+        }
+
+        // Raw iterator.
+        Iterator iter = map.keys.iterator;
+        for (int key = 0; key < i; key++) {
+          Expect.equals(key + 2, map[key]);
+          map[key] = key + 3;
+          map.remove(1000);  // Removing something not there.
+          map.putIfAbsent(key, () => Expect.fail("SHOULD NOT BE ABSENT"));
+          // Doesn't cause ConcurrentModificationError on the moveNext.
+        }
+        iter.moveNext();  // Should not throw.
+
+        // Remove a lot of elements, which can cause a re-tabulation.
+        for (int key = 1; key < i; key++) {
+          Expect.equals(key + 3, map[key]);
+          map.remove(key);
+        }
+        iter = map.keys.iterator;
+        map[0] = 2;
+        iter.moveNext();  // Should not throw.
+     }
+  }
+
+
+  {  // Check that null can be in the map.
+    Map map = newMap();
+    map[null] = 0;
+    Expect.equals(1, map.length);
+    Expect.isTrue(map.containsKey(null));
+    Expect.isNull(map.keys.first);
+    Expect.isNull(map.keys.last);
+    map[null] = 1;
+    Expect.equals(1, map.length);
+    Expect.isTrue(map.containsKey(null));
+    map.remove(null);
+    Expect.isTrue(map.isEmpty);
+    Expect.isFalse(map.containsKey(null));
+
+    // Created using map.from.
+    map = newMapFrom(new Map()..[null] = 0);
+    Expect.equals(1, map.length);
+    Expect.isTrue(map.containsKey(null));
+    Expect.isNull(map.keys.first);
+    Expect.isNull(map.keys.last);
+    map[null] = 1;
+    Expect.equals(1, map.length);
+    Expect.isTrue(map.containsKey(null));
+    map.remove(null);
+    Expect.isTrue(map.isEmpty);
+    Expect.isFalse(map.containsKey(null));
+
+    Map fromMap = new Map();
+    fromMap[1] = 0;
+    fromMap[2] = 0;
+    fromMap[3] = 0;
+    fromMap[null] = 0;
+    fromMap[4] = 0;
+    fromMap[5] = 0;
+    fromMap[6] = 0;
+    Expect.equals(7, fromMap.length);
+
+    // map that grows with null in it.
+    map = newMapFrom(fromMap);
+    Expect.equals(7, map.length);
+    for (int i = 7; i < 128; i++) {
+      map[i] = 0;
+    }
+    Expect.equals(128, map.length);
+    Expect.isTrue(map.containsKey(null));
+    map[null] = 1;
+    Expect.equals(128, map.length);
+    Expect.isTrue(map.containsKey(null));
+    map.remove(null);
+    Expect.equals(127, map.length);
+    Expect.isFalse(map.containsKey(null));
+  }
+}
+
+void main() {
+  Expect.isTrue(new HashMap<int, String>() is Map<int, String>);
+  Expect.isTrue(new LinkedHashMap<int, String>() is Map<int, String>);
+  Expect.isTrue(new HashMap<String, int>.from({}) is Map<String, int>);
+  Expect.isTrue(new LinkedHashMap<String, int>.from({}) is Map<String, int>);
+  Expect.isTrue(<String, int>{} is Map<String, int>);
+  Expect.isTrue(const <String, int>{} is Map<String, int>);
+
+  testMap(() => new HashMap(), (m) => new HashMap.from(m));
+  testMap(() => new LinkedHashMap(), (m) => new LinkedHashMap.from(m));
+}
+
+
+class BadHashCode {
+  static int idCounter = 0;
+  final int id;
+  BadHashCode() : id = idCounter++;
+  int get hashCode => 42;
+}
diff --git a/tests/corelib/hash_set_test.dart b/tests/corelib/hash_set_test.dart
new file mode 100644
index 0000000..68a0844
--- /dev/null
+++ b/tests/corelib/hash_set_test.dart
@@ -0,0 +1,206 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Tests of hash set behavior, with focus in iteration and concurrent
+// modification errors.
+
+library hash_map2_test;
+import 'dart:collection';
+
+testSet(Set newSet(), Set newSetFrom(Set from)) {
+  Set gen(int from, int to) =>
+      new Set.from(new Iterable.generate(to - from, (n) => n + from));
+
+  bool odd(int n) => (n & 1) == 1;
+  bool even(int n) => (n & 1) == 0;
+
+  {  // Test growing to largish capacity.
+    Set set = newSet();
+
+    for (int i = 0; i < 256; i++) {
+      set.add(i);
+    }
+    set.addAll(gen(256, 512));
+    set.addAll(newSetFrom(gen(512, 1000)));
+    Expect.equals(1000, set.length);
+
+    // Remove half.
+    for (int i = 0; i < 1000; i += 2) set.remove(i);
+    Expect.equals(500, set.length);
+    Expect.isFalse(set.any(even));
+    Expect.isTrue(set.every(odd));
+
+    // Re-add all.
+    set.addAll(gen(0, 1000));
+    Expect.equals(1000, set.length);
+  }
+
+  {  // Test having many deleted elements.
+    Set set = newSet();
+    set.add(0);
+    for (int i = 0; i < 1000; i++) {
+      set.add(i + 1);
+      set.remove(i);
+      Expect.equals(1, set.length);
+    }
+  }
+
+  {  // Test having many elements with same hashCode
+    Set set = newSet();
+    for (int i = 0; i < 1000; i++) {
+      set.add(new BadHashCode());
+    }
+    Expect.equals(1000, set.length);
+  }
+
+  {  // Check concurrent modification
+    Set set = newSet()..add(0)..add(1);
+
+    {  // Test adding before a moveNext.
+      Iterator iter = set.iterator;
+      iter.moveNext();
+      set.add(1);  // Updating existing key isn't a modification.
+      iter.moveNext();
+      set.add(2);
+      Expect.throws(iter.moveNext, (e) => e is Error);
+    }
+
+    {  // Test adding after last element.
+      Iterator iter = set.iterator;
+      Expect.equals(3, set.length);
+      iter.moveNext();
+      iter.moveNext();
+      iter.moveNext();
+      set.add(3);
+      Expect.throws(iter.moveNext, (e) => e is Error);
+    }
+
+    {  // Test removing during iteration.
+      Iterator iter = set.iterator;
+      iter.moveNext();
+      set.remove(1000);  // Not a modification if it's not there.
+      iter.moveNext();
+      int n = iter.current;
+      set.remove(n);
+      // Removing doesn't change current.
+      Expect.equals(n, iter.current);
+      Expect.throws(iter.moveNext, (e) => e is Error);
+    }
+
+    {  // Test removing after last element.
+      Iterator iter = set.iterator;
+      Expect.equals(3, set.length);
+      iter.moveNext();
+      iter.moveNext();
+      iter.moveNext();
+      int n = iter.current;
+      set.remove(n);
+      // Removing doesn't change current.
+      Expect.equals(n, iter.current);
+      Expect.throws(iter.moveNext, (e) => e is Error);
+    }
+
+    {  // Test that updating value doesn't cause error.
+      Iterator iter = set.iterator;
+      Expect.equals(2, set.length);
+      iter.moveNext();
+      int n = iter.current;
+      set.add(n);
+      iter.moveNext();
+      Expect.isTrue(set.contains(iter.current));
+    }
+
+    {  // Check adding many existing values isn't considered modification.
+      Set set2 = newSet();
+      for (var value in set) {
+        set2.add(value);
+      }
+      Iterator iter = set.iterator;
+      set.addAll(set2);
+      // Shouldn't throw.
+      iter.moveNext();
+    }
+  }
+
+  {  // Check that updating existing elements is not a modification.
+     // This must be the case even if the underlying data structure is
+     // nearly full.
+     for (int i = 1; i < 128; i++) {
+        // Create maps of different sizes, some of which should be
+        // at a limit of the underlying data structure.
+        Set set = newSetFrom(gen(0, i));
+        Iterator iter = set.iterator;
+        for (int j = 0; j < i; j++) {
+          set.add(j);
+        }
+        iter.moveNext();  // Should not throw.
+
+        for (int j = 1; j < i; j++) {
+          set.remove(j);
+        }
+        iter = set.iterator;
+        set.add(0);
+        iter.moveNext();  // Should not throw.
+     }
+  }
+
+  {  // Check that null can be in the set.
+    Set set = newSet();
+    set.add(null);
+    Expect.equals(1, set.length);
+    Expect.isTrue(set.contains(null));
+    Expect.isNull(set.first);
+    Expect.isNull(set.last);
+    set.add(null);
+    Expect.equals(1, set.length);
+    Expect.isTrue(set.contains(null));
+    set.remove(null);
+    Expect.isTrue(set.isEmpty);
+    Expect.isFalse(set.contains(null));
+
+    // Created using Set.from.
+    set = newSetFrom([null]);
+    Expect.equals(1, set.length);
+    Expect.isTrue(set.contains(null));
+    Expect.isNull(set.first);
+    Expect.isNull(set.last);
+    set.add(null);
+    Expect.equals(1, set.length);
+    Expect.isTrue(set.contains(null));
+    set.remove(null);
+    Expect.isTrue(set.isEmpty);
+    Expect.isFalse(set.contains(null));
+
+    // Set that grows with null in it.
+    set = newSetFrom([1, 2, 3, null, 4, 5, 6]);
+    Expect.equals(7, set.length);
+    for (int i = 7; i < 128; i++) {
+      set.add(i);
+    }
+    Expect.equals(128, set.length);
+    Expect.isTrue(set.contains(null));
+    set.add(null);
+    Expect.equals(128, set.length);
+    Expect.isTrue(set.contains(null));
+    set.remove(null);
+    Expect.equals(127, set.length);
+    Expect.isFalse(set.contains(null));
+  }
+}
+
+void main() {
+  testSet(() => new HashSet(), (m) => new HashSet.from(m));
+  testSet(() => new LinkedHashSet(), (m) => new LinkedHashSet.from(m));
+}
+
+
+class BadHashCode {
+  static int idCounter = 0;
+  final int id;
+  BadHashCode() : id = idCounter++;
+  int get hashCode => 42;
+  // operator == is identity.
+  // Can't make a bad compareTo that isn't invalid.
+  int compareTo(BadHashCode other) => id - other.id;
+}
diff --git a/tests/corelib/map_from_test.dart b/tests/corelib/map_from_test.dart
index bc47e9d..b43a45d 100644
--- a/tests/corelib/map_from_test.dart
+++ b/tests/corelib/map_from_test.dart
@@ -71,7 +71,7 @@
   var map = const { 'b': 1, 'a': 2, 'c': 3 };
   var otherMap = new LinkedHashMap.from(map);
   Expect.isTrue(otherMap is Map);
-  Expect.isTrue(otherMap is HashMap);
+  Expect.isTrue(otherMap is! HashMap);
   Expect.isTrue(otherMap is LinkedHashMap);
   var i = 1;
   for (var val in map.values) {
diff --git a/tests/standalone/standalone.status b/tests/standalone/standalone.status
index 9652300..cbaa158 100644
--- a/tests/standalone/standalone.status
+++ b/tests/standalone/standalone.status
@@ -45,6 +45,7 @@
 
 [ $runtime == vm && $system == windows ]
 io/file_system_links_test: Skip  # No links on Windows.
+io/socket_stream_close_test: Fail, Pass # Issue 8556.
 
 [ $compiler == none && $runtime == drt ]
 io/*: Skip # Don't run tests using dart:io in the browser
diff --git a/tools/VERSION b/tools/VERSION
index 57771f2..8b85f04 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -1,4 +1,4 @@
 MAJOR 0
 MINOR 3
 BUILD 7
-PATCH 2
+PATCH 3
diff --git a/utils/pub/pub.dart b/utils/pub/pub.dart
index 569c63c..00abe4c 100644
--- a/utils/pub/pub.dart
+++ b/utils/pub/pub.dart
@@ -42,7 +42,7 @@
     'uploader': new UploaderCommand(),
     'version': new VersionCommand()
   };
-  for (var command in commands.values) {
+  for (var command in commands.values.toList()) {
     for (var alias in command.aliases) {
       commands[alias] = command;
     }