Revision: 21389
Author:   [email protected]
Date:     Tue May 20 14:22:05 2014 UTC
Log:      ES6 Map/Set iterators/forEach improvements

This changes how Map/Set interacts with its iterators. When the
underlying table is rehashed or cleared, we create a new table (like
before) but we add a reference from the old table to the new table. We
also add an array describing how to transition the iterator from the
old table to the new table.

When Next is called on the iterator it checks if there is a newer table
that it should transition to. If there is, it updates the index based
on the previously recorded changes and finally changes itself to point
at the new table.

With these changes Map/Set no longer keeps the iterators alive. Also,
as before, the iterators keep the underlying table(s) alive but not the
actual Map/Set.

BUG=v8:1793
LOG=Y
[email protected], [email protected]

Review URL: https://codereview.chromium.org/289503002

Patch from Erik Arvidsson <[email protected]>.
http://code.google.com/p/v8/source/detail?r=21389

Deleted:
 /branches/bleeding_edge/test/mjsunit/runtime-gen/mapiteratorclose.js
 /branches/bleeding_edge/test/mjsunit/runtime-gen/setiteratorclose.js
Modified:
 /branches/bleeding_edge/src/collection.js
 /branches/bleeding_edge/src/objects-debug.cc
 /branches/bleeding_edge/src/objects-inl.h
 /branches/bleeding_edge/src/objects-printer.cc
 /branches/bleeding_edge/src/objects.cc
 /branches/bleeding_edge/src/objects.h
 /branches/bleeding_edge/src/runtime.cc
 /branches/bleeding_edge/src/runtime.h
 /branches/bleeding_edge/test/mjsunit/harmony/collections.js
 /branches/bleeding_edge/tools/generate-runtime-tests.py

=======================================
--- /branches/bleeding_edge/test/mjsunit/runtime-gen/mapiteratorclose.js Thu May 8 13:11:59 2014 UTC
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright 2014 the V8 project authors. All rights reserved.
-// AUTO-GENERATED BY tools/generate-runtime-tests.py, DO NOT MODIFY
-// Flags: --allow-natives-syntax --harmony
-var _holder = %MapCreateIterator(new Map(), 3);
-%MapIteratorClose(_holder);
=======================================
--- /branches/bleeding_edge/test/mjsunit/runtime-gen/setiteratorclose.js Thu May 8 13:11:59 2014 UTC
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright 2014 the V8 project authors. All rights reserved.
-// AUTO-GENERATED BY tools/generate-runtime-tests.py, DO NOT MODIFY
-// Flags: --allow-natives-syntax --harmony
-var _holder = %SetCreateIterator(new Set(), 2);
-%SetIteratorClose(_holder);
=======================================
--- /branches/bleeding_edge/src/collection.js   Wed May 14 08:51:10 2014 UTC
+++ /branches/bleeding_edge/src/collection.js   Tue May 20 14:22:05 2014 UTC
@@ -106,12 +106,8 @@

   var iterator = %SetCreateIterator(this, ITERATOR_KIND_VALUES);
   var entry;
-  try {
-    while (!(entry = %SetIteratorNext(iterator)).done) {
-      %_CallFunction(receiver, entry.value, entry.value, this, f);
-    }
-  } finally {
-    %SetIteratorClose(iterator);
+  while (!(entry = %SetIteratorNext(iterator)).done) {
+    %_CallFunction(receiver, entry.value, entry.value, this, f);
   }
 }

@@ -219,12 +215,8 @@

   var iterator = %MapCreateIterator(this, ITERATOR_KIND_ENTRIES);
   var entry;
-  try {
-    while (!(entry = %MapIteratorNext(iterator)).done) {
-      %_CallFunction(receiver, entry.value[1], entry.value[0], this, f);
-    }
-  } finally {
-    %MapIteratorClose(iterator);
+  while (!(entry = %MapIteratorNext(iterator)).done) {
+    %_CallFunction(receiver, entry.value[1], entry.value[0], this, f);
   }
 }

=======================================
--- /branches/bleeding_edge/src/objects-debug.cc Fri May 9 16:34:58 2014 UTC +++ /branches/bleeding_edge/src/objects-debug.cc Tue May 20 14:22:05 2014 UTC
@@ -702,13 +702,7 @@
   VerifyHeapPointer(table());
   CHECK(table()->IsOrderedHashTable() || table()->IsUndefined());
   CHECK(index()->IsSmi());
-  CHECK(count()->IsSmi());
   CHECK(kind()->IsSmi());
-  VerifyHeapPointer(next_iterator());
- CHECK(next_iterator()->IsJSSetIterator() || next_iterator()->IsUndefined());
-  VerifyHeapPointer(table());
-  CHECK(previous_iterator()->IsJSSetIterator()
-        || previous_iterator()->IsUndefined());
 }


@@ -718,13 +712,7 @@
   VerifyHeapPointer(table());
   CHECK(table()->IsOrderedHashTable() || table()->IsUndefined());
   CHECK(index()->IsSmi());
-  CHECK(count()->IsSmi());
   CHECK(kind()->IsSmi());
-  VerifyHeapPointer(next_iterator());
- CHECK(next_iterator()->IsJSMapIterator() || next_iterator()->IsUndefined());
-  VerifyHeapPointer(table());
-  CHECK(previous_iterator()->IsJSMapIterator()
-        || previous_iterator()->IsUndefined());
 }


=======================================
--- /branches/bleeding_edge/src/objects-inl.h   Mon May 19 16:07:20 2014 UTC
+++ /branches/bleeding_edge/src/objects-inl.h   Tue May 20 14:22:05 2014 UTC
@@ -5683,12 +5683,7 @@

 ORDERED_HASH_TABLE_ITERATOR_ACCESSORS(table, Object, kTableOffset)
 ORDERED_HASH_TABLE_ITERATOR_ACCESSORS(index, Smi, kIndexOffset)
-ORDERED_HASH_TABLE_ITERATOR_ACCESSORS(count, Smi, kCountOffset)
 ORDERED_HASH_TABLE_ITERATOR_ACCESSORS(kind, Smi, kKindOffset)
-ORDERED_HASH_TABLE_ITERATOR_ACCESSORS(next_iterator, Object,
-                                      kNextIteratorOffset)
-ORDERED_HASH_TABLE_ITERATOR_ACCESSORS(previous_iterator, Object,
-                                      kPreviousIteratorOffset)

 #undef ORDERED_HASH_TABLE_ITERATOR_ACCESSORS

=======================================
--- /branches/bleeding_edge/src/objects-printer.cc Wed Apr 30 12:25:18 2014 UTC +++ /branches/bleeding_edge/src/objects-printer.cc Tue May 20 14:22:05 2014 UTC
@@ -743,14 +743,8 @@
   table()->ShortPrint(out);
   PrintF(out, "\n - index = ");
   index()->ShortPrint(out);
-  PrintF(out, "\n - count = ");
-  count()->ShortPrint(out);
   PrintF(out, "\n - kind = ");
   kind()->ShortPrint(out);
-  PrintF(out, "\n - next_iterator = ");
-  next_iterator()->ShortPrint(out);
-  PrintF(out, "\n - previous_iterator = ");
-  previous_iterator()->ShortPrint(out);
   PrintF(out, "\n");
 }

=======================================
--- /branches/bleeding_edge/src/objects.cc      Tue May 20 09:21:45 2014 UTC
+++ /branches/bleeding_edge/src/objects.cc      Tue May 20 14:22:05 2014 UTC
@@ -16222,7 +16222,6 @@
   table->SetNumberOfBuckets(num_buckets);
   table->SetNumberOfElements(0);
   table->SetNumberOfDeletedElements(0);
-  table->set_iterators(isolate->heap()->undefined_value());
   return table;
 }

@@ -16230,6 +16229,8 @@
 template<class Derived, class Iterator, int entrysize>
Handle<Derived> OrderedHashTable<Derived, Iterator, entrysize>::EnsureGrowable(
     Handle<Derived> table) {
+  ASSERT(!table->IsObsolete());
+
   int nof = table->NumberOfElements();
   int nod = table->NumberOfDeletedElements();
   int capacity = table->Capacity();
@@ -16244,9 +16245,11 @@
 template<class Derived, class Iterator, int entrysize>
 Handle<Derived> OrderedHashTable<Derived, Iterator, entrysize>::Shrink(
     Handle<Derived> table) {
+  ASSERT(!table->IsObsolete());
+
   int nof = table->NumberOfElements();
   int capacity = table->Capacity();
-  if (nof > (capacity >> 2)) return table;
+  if (nof >= (capacity >> 2)) return table;
   return Rehash(table, capacity / 2);
 }

@@ -16254,21 +16257,15 @@
 template<class Derived, class Iterator, int entrysize>
 Handle<Derived> OrderedHashTable<Derived, Iterator, entrysize>::Clear(
     Handle<Derived> table) {
+  ASSERT(!table->IsObsolete());
+
   Handle<Derived> new_table =
       Allocate(table->GetIsolate(),
                kMinCapacity,
table->GetHeap()->InNewSpace(*table) ? NOT_TENURED : TENURED);

-  new_table->set_iterators(table->iterators());
-  table->set_iterators(table->GetHeap()->undefined_value());
-
-  DisallowHeapAllocation no_allocation;
-  for (Object* object = new_table->iterators();
-       !object->IsUndefined();
-       object = Iterator::cast(object)->next_iterator()) {
-    Iterator::cast(object)->TableCleared();
-    Iterator::cast(object)->set_table(*new_table);
-  }
+  table->SetNextTable(*new_table);
+  table->SetNumberOfDeletedElements(-1);

   return new_table;
 }
@@ -16277,6 +16274,8 @@
 template<class Derived, class Iterator, int entrysize>
 Handle<Derived> OrderedHashTable<Derived, Iterator, entrysize>::Rehash(
     Handle<Derived> table, int new_capacity) {
+  ASSERT(!table->IsObsolete());
+
   Handle<Derived> new_table =
       Allocate(table->GetIsolate(),
                new_capacity,
@@ -16285,9 +16284,15 @@
   int nod = table->NumberOfDeletedElements();
   int new_buckets = new_table->NumberOfBuckets();
   int new_entry = 0;
+  int removed_holes_index = 0;
+
   for (int old_entry = 0; old_entry < (nof + nod); ++old_entry) {
     Object* key = table->KeyAt(old_entry);
-    if (key->IsTheHole()) continue;
+    if (key->IsTheHole()) {
+      table->SetRemovedIndexAt(removed_holes_index++, old_entry);
+      continue;
+    }
+
     Object* hash = key->GetHash();
     int bucket = Smi::cast(hash)->value() & (new_buckets - 1);
     Object* chain_entry = new_table->get(kHashTableStartIndex + bucket);
@@ -16301,18 +16306,11 @@
     new_table->set(new_index + kChainOffset, chain_entry);
     ++new_entry;
   }
-  new_table->SetNumberOfElements(nof);

-  new_table->set_iterators(table->iterators());
-  table->set_iterators(table->GetHeap()->undefined_value());
+  ASSERT_EQ(nod, removed_holes_index);

-  DisallowHeapAllocation no_allocation;
-  for (Object* object = new_table->iterators();
-       !object->IsUndefined();
-       object = Iterator::cast(object)->next_iterator()) {
-    Iterator::cast(object)->TableCompacted();
-    Iterator::cast(object)->set_table(*new_table);
-  }
+  new_table->SetNumberOfElements(nof);
+  table->SetNextTable(*new_table);

   return new_table;
 }
@@ -16321,6 +16319,8 @@
 template<class Derived, class Iterator, int entrysize>
 int OrderedHashTable<Derived, Iterator, entrysize>::FindEntry(
     Handle<Object> key) {
+  ASSERT(!IsObsolete());
+
   DisallowHeapAllocation no_gc;
   ASSERT(!key->IsTheHole());
   Object* hash = key->GetHash();
@@ -16338,6 +16338,8 @@

 template<class Derived, class Iterator, int entrysize>
 int OrderedHashTable<Derived, Iterator, entrysize>::AddEntry(int hash) {
+  ASSERT(!IsObsolete());
+
   int entry = UsedCapacity();
   int bucket = HashToBucket(hash);
   int index = EntryToIndex(entry);
@@ -16351,19 +16353,14 @@

 template<class Derived, class Iterator, int entrysize>
void OrderedHashTable<Derived, Iterator, entrysize>::RemoveEntry(int entry) {
+  ASSERT(!IsObsolete());
+
   int index = EntryToIndex(entry);
   for (int i = 0; i < entrysize; ++i) {
     set_the_hole(index + i);
   }
   SetNumberOfElements(NumberOfElements() - 1);
   SetNumberOfDeletedElements(NumberOfDeletedElements() + 1);
-
-  DisallowHeapAllocation no_allocation;
-  for (Object* object = iterators();
-       !object->IsUndefined();
-       object = Iterator::cast(object)->next_iterator()) {
-    Iterator::cast(object)->EntryRemoved(entry);
-  }
 }


@@ -16483,97 +16480,69 @@


 template<class Derived, class TableType>
-void OrderedHashTableIterator<Derived, TableType>::EntryRemoved(int index) {
-  int i = this->index()->value();
-  if (index < i) {
-    set_count(Smi::FromInt(count()->value() - 1));
-  }
-  if (index == i) {
-    Seek();
-  }
-}
+Handle<JSObject> OrderedHashTableIterator<Derived, TableType>::Next(
+    Handle<Derived> iterator) {
+  Isolate* isolate = iterator->GetIsolate();
+  Factory* factory = isolate->factory();

+  Handle<Object> maybe_table(iterator->table(), isolate);
+  if (!maybe_table->IsUndefined()) {
+    iterator->Transition();

-template<class Derived, class TableType>
-void OrderedHashTableIterator<Derived, TableType>::Close() {
-  if (Closed()) return;
+    Handle<TableType> table(TableType::cast(iterator->table()), isolate);
+    int index = Smi::cast(iterator->index())->value();
+    int used_capacity = table->UsedCapacity();

-  DisallowHeapAllocation no_allocation;
+    while (index < used_capacity && table->KeyAt(index)->IsTheHole()) {
+      index++;
+    }

-  Object* undefined = GetHeap()->undefined_value();
-  TableType* table = TableType::cast(this->table());
-  Object* previous = previous_iterator();
-  Object* next = next_iterator();
+    if (index < used_capacity) {
+      int entry_index = table->EntryToIndex(index);
+      Handle<Object> value =
+          Derived::ValueForKind(iterator, entry_index);
+      iterator->set_index(Smi::FromInt(index + 1));
+      return factory->NewIteratorResultObject(value, false);
+    }

-  if (previous == undefined) {
-    ASSERT_EQ(table->iterators(), this);
-    table->set_iterators(next);
-  } else {
-    ASSERT_EQ(Derived::cast(previous)->next_iterator(), this);
-    Derived::cast(previous)->set_next_iterator(next);
+    iterator->set_table(iterator->GetHeap()->undefined_value());
   }

-  if (!next->IsUndefined()) {
-    ASSERT_EQ(Derived::cast(next)->previous_iterator(), this);
-    Derived::cast(next)->set_previous_iterator(previous);
-  }
-
-  set_previous_iterator(undefined);
-  set_next_iterator(undefined);
-  set_table(undefined);
-}
-
-
-template<class Derived, class TableType>
-void OrderedHashTableIterator<Derived, TableType>::Seek() {
-  ASSERT(!Closed());
-
-  DisallowHeapAllocation no_allocation;
-
-  int index = this->index()->value();
-
-  TableType* table = TableType::cast(this->table());
-  int used_capacity = table->UsedCapacity();
-
-  while (index < used_capacity && table->KeyAt(index)->IsTheHole()) {
-    index++;
-  }
-  set_index(Smi::FromInt(index));
+ return factory->NewIteratorResultObject(factory->undefined_value(), true);
 }


 template<class Derived, class TableType>
-void OrderedHashTableIterator<Derived, TableType>::MoveNext() {
-  ASSERT(!Closed());
-
-  set_index(Smi::FromInt(index()->value() + 1));
-  set_count(Smi::FromInt(count()->value() + 1));
-  Seek();
-}
-
+void OrderedHashTableIterator<Derived, TableType>::Transition() {
+  Isolate* isolate = GetIsolate();
+  Handle<TableType> table(TableType::cast(this->table()), isolate);
+  if (!table->IsObsolete()) return;

-template<class Derived, class TableType>
-Handle<JSObject> OrderedHashTableIterator<Derived, TableType>::Next(
-    Handle<Derived> iterator) {
-  Isolate* isolate = iterator->GetIsolate();
-  Factory* factory = isolate->factory();
+  int index = Smi::cast(this->index())->value();
+  while (table->IsObsolete()) {
+    Handle<TableType> next_table(table->NextTable(), isolate);

-  Handle<Object> object(iterator->table(), isolate);
+    if (index > 0) {
+      int nod = table->NumberOfDeletedElements();

-  if (!object->IsUndefined()) {
-    Handle<TableType> table = Handle<TableType>::cast(object);
-    int index = iterator->index()->value();
-    if (index < table->UsedCapacity()) {
-      int entry_index = table->EntryToIndex(index);
-      iterator->MoveNext();
-      Handle<Object> value = Derived::ValueForKind(iterator, entry_index);
-      return factory->NewIteratorResultObject(value, false);
-    } else {
-      iterator->Close();
+ // When we clear the table we set the number of deleted elements to -1.
+      if (nod == -1) {
+        index = 0;
+      } else {
+        int old_index = index;
+        for (int i = 0; i < nod; ++i) {
+          int removed_index = table->RemovedIndexAt(i);
+          if (removed_index >= old_index) break;
+          --index;
+        }
+      }
     }
+
+    table = next_table;
   }

- return factory->NewIteratorResultObject(factory->undefined_value(), true);
+  set_table(*table);
+  set_index(Smi::FromInt(index));
 }


@@ -16584,60 +16553,37 @@
     int kind) {
   Isolate* isolate = table->GetIsolate();

-  Handle<Object> undefined = isolate->factory()->undefined_value();
-
   Handle<Derived> new_iterator = Handle<Derived>::cast(
       isolate->factory()->NewJSObjectFromMap(map));
-  new_iterator->set_previous_iterator(*undefined);
   new_iterator->set_table(*table);
   new_iterator->set_index(Smi::FromInt(0));
-  new_iterator->set_count(Smi::FromInt(0));
   new_iterator->set_kind(Smi::FromInt(kind));
-
-  Handle<Object> old_iterator(table->iterators(), isolate);
-  if (!old_iterator->IsUndefined()) {
- Handle<Derived>::cast(old_iterator)->set_previous_iterator(*new_iterator);
-    new_iterator->set_next_iterator(*old_iterator);
-  } else {
-    new_iterator->set_next_iterator(*undefined);
-  }
-
-  table->set_iterators(*new_iterator);
-
   return new_iterator;
 }

-
-template void
-OrderedHashTableIterator<JSSetIterator, OrderedHashSet>::EntryRemoved(
-    int index);
-
-template void
-OrderedHashTableIterator<JSSetIterator, OrderedHashSet>::Close();

 template Handle<JSObject>
 OrderedHashTableIterator<JSSetIterator, OrderedHashSet>::Next(
     Handle<JSSetIterator> iterator);

+template void
+OrderedHashTableIterator<JSSetIterator, OrderedHashSet>::Transition();
+
 template Handle<JSSetIterator>
 OrderedHashTableIterator<JSSetIterator, OrderedHashSet>::CreateInternal(
-      Handle<Map> map, Handle<OrderedHashSet> table, int kind);
-
-
-template void
-OrderedHashTableIterator<JSMapIterator, OrderedHashMap>::EntryRemoved(
-    int index);
+    Handle<Map> map, Handle<OrderedHashSet> table, int kind);

-template void
-OrderedHashTableIterator<JSMapIterator, OrderedHashMap>::Close();

 template Handle<JSObject>
 OrderedHashTableIterator<JSMapIterator, OrderedHashMap>::Next(
     Handle<JSMapIterator> iterator);

+template void
+OrderedHashTableIterator<JSMapIterator, OrderedHashMap>::Transition();
+
 template Handle<JSMapIterator>
 OrderedHashTableIterator<JSMapIterator, OrderedHashMap>::CreateInternal(
-      Handle<Map> map, Handle<OrderedHashMap> table, int kind);
+    Handle<Map> map, Handle<OrderedHashMap> table, int kind);


 Handle<Object> JSSetIterator::ValueForKind(
=======================================
--- /branches/bleeding_edge/src/objects.h       Mon May 12 15:30:00 2014 UTC
+++ /branches/bleeding_edge/src/objects.h       Tue May 20 14:22:05 2014 UTC
@@ -4153,16 +4153,27 @@
 //   [0]: bucket count
 //   [1]: element count
 //   [2]: deleted element count
-//   [3]: live iterators (doubly-linked list)
-// [4..(NumberOfBuckets() - 1)]: "hash table", where each item is an offset -// into the data table (see below) where the
-//                                 first item in this bucket is stored.
-//   [4 + NumberOfBuckets()..length]: "data table", an array of length
+//   [3..(3 + NumberOfBuckets() - 1)]: "hash table", where each item is an
+// offset into the data table (see below) where the
+//                            first item in this bucket is stored.
+//   [3 + NumberOfBuckets()..length]: "data table", an array of length
// Capacity() * kEntrySize, where the first entrysize // items are handled by the derived class and the // item at kChainOffset is another entry into the // data table indicating the next entry in this hash
 //                            bucket.
+//
+// When we transition the table to a new version we obsolete it and reuse parts +// of the memory to store information how to transition an iterator to the new
+// table:
+//
+// Memory layout for obsolete table:
+//   [0]: bucket count
+//   [1]: Next newer table
+//   [2]: Number of removed holes or -1 when the table was cleared.
+// [3..(3 + NumberOfRemovedHoles() - 1)]: The indexes of the removed holes.
+//   [3 + NumberOfRemovedHoles()..length]: Not used
+//
 template<class Derived, class Iterator, int entrysize>
 class OrderedHashTable: public FixedArray {
  public:
@@ -4198,10 +4209,6 @@
   int NumberOfBuckets() {
     return Smi::cast(get(kNumberOfBucketsIndex))->value();
   }
-
-  Object* iterators() { return get(kIteratorsIndex); }
-
-  void set_iterators(Object* value) { set(kIteratorsIndex, value); }

   // Returns the index into the data table where the new entry
   // should be placed. The table is assumed to have enough space
@@ -4219,6 +4226,20 @@

   Object* KeyAt(int entry) { return get(EntryToIndex(entry)); }

+  bool IsObsolete() {
+    return !get(kNextTableIndex)->IsSmi();
+  }
+
+  // The next newer table. This is only valid if the table is obsolete.
+  Derived* NextTable() {
+    return Derived::cast(get(kNextTableIndex));
+  }
+
+  // When the table is obsolete we store the indexes of the removed holes.
+  int RemovedIndexAt(int index) {
+    return Smi::cast(get(kRemovedHolesIndex + index))->value();
+  }
+
   static const int kNotFound = -1;
   static const int kMinCapacity = 4;

@@ -4254,12 +4275,22 @@
     int bucket = HashToBucket(hash);
     return Smi::cast(get(kHashTableStartIndex + bucket))->value();
   }
+
+  void SetNextTable(Derived* next_table) {
+    set(kNextTableIndex, next_table);
+  }
+
+  void SetRemovedIndexAt(int index, int removed_index) {
+    return set(kRemovedHolesIndex + index, Smi::FromInt(removed_index));
+  }

   static const int kNumberOfBucketsIndex = 0;
   static const int kNumberOfElementsIndex = kNumberOfBucketsIndex + 1;
static const int kNumberOfDeletedElementsIndex = kNumberOfElementsIndex + 1;
-  static const int kIteratorsIndex = kNumberOfDeletedElementsIndex + 1;
-  static const int kHashTableStartIndex = kIteratorsIndex + 1;
+ static const int kHashTableStartIndex = kNumberOfDeletedElementsIndex + 1;
+
+  static const int kNextTableIndex = kNumberOfElementsIndex;
+  static const int kRemovedHolesIndex = kHashTableStartIndex;

   static const int kEntrySize = entrysize + 1;
   static const int kChainOffset = entrysize;
@@ -9956,17 +9987,15 @@
 // OrderedHashTableIterator is an iterator that iterates over the keys and
 // values of an OrderedHashTable.
 //
-// The hash table has a reference to the iterator and the iterators themselves -// have references to the [next_iterator] and [previous_iterator], thus creating
-// a double linked list.
+// The iterator has a reference to the underlying OrderedHashTable data,
+// [table], as well as the current [index] the iterator is at.
 //
-// When the hash table changes the iterators are called to update their [index] -// and [count]. The hash table calls [EntryRemoved], [TableCompacted] as well
-// as [TableCleared].
+// When the OrderedHashTable is rehashed it adds a reference from the old table +// to the new table as well as storing enough data about the changes so that the
+// iterator [index] can be adjusted accordingly.
 //
-// When an iterator is done it closes itself. It removes itself from the double
-// linked list and it sets its [table] to undefined, no longer keeping the
-// [table] alive.
+// When the [Next] result from the iterator is requested, the iterator checks if
+// there is a newer table that it needs to transition to.
 template<class Derived, class TableType>
 class OrderedHashTableIterator: public JSObject {
  public:
@@ -9976,29 +10005,17 @@
   // [index]: The index into the data table.
   DECL_ACCESSORS(index, Smi)

-  // [count]: The logical index into the data table, ignoring the holes.
-  DECL_ACCESSORS(count, Smi)
-
   // [kind]: The kind of iteration this is. One of the [Kind] enum values.
   DECL_ACCESSORS(kind, Smi)

-  // [next_iterator]: Used as a double linked list for the live iterators.
-  DECL_ACCESSORS(next_iterator, Object)
-
- // [previous_iterator]: Used as a double linked list for the live iterators.
-  DECL_ACCESSORS(previous_iterator, Object)
-
 #ifdef OBJECT_PRINT
   void OrderedHashTableIteratorPrint(FILE* out);
 #endif

   static const int kTableOffset = JSObject::kHeaderSize;
   static const int kIndexOffset = kTableOffset + kPointerSize;
-  static const int kCountOffset = kIndexOffset + kPointerSize;
-  static const int kKindOffset = kCountOffset + kPointerSize;
-  static const int kNextIteratorOffset = kKindOffset + kPointerSize;
- static const int kPreviousIteratorOffset = kNextIteratorOffset + kPointerSize;
-  static const int kSize = kPreviousIteratorOffset + kPointerSize;
+  static const int kKindOffset = kIndexOffset + kPointerSize;
+  static const int kSize = kKindOffset + kPointerSize;

   enum Kind {
     kKindKeys = 1,
@@ -10006,25 +10023,6 @@
     kKindEntries = 3
   };

-  // Called by the underlying [table] when an entry is removed.
-  void EntryRemoved(int index);
-
-  // Called by the underlying [table] when it is compacted/rehashed.
-  void TableCompacted() {
-    // All holes have been removed so index is now same as count.
-    set_index(count());
-  }
-
-  // Called by the underlying [table] when it is cleared.
-  void TableCleared() {
-    set_index(Smi::FromInt(0));
-    set_count(Smi::FromInt(0));
-  }
-
- // Removes the iterator from the double linked list and removes its reference
-  // back to the [table].
-  void Close();
-
// Returns an iterator result object: {value: any, done: boolean} and moves // the index to the next valid entry. Closes the iterator if moving past the
   // end.
@@ -10035,16 +10033,9 @@
       Handle<Map> map, Handle<TableType> table, int kind);

  private:
-  // Ensures [index] is not pointing to a hole.
-  void Seek();
-
- // Moves [index] to next valid entry. Closes the iterator if moving past the
-  // end.
-  void MoveNext();
-
-  bool Closed() {
-    return table()->IsUndefined();
-  }
+ // Transitions the iterator to the non obsolote backing store. This is a NOP
+  // if the [table] is not obsolete.
+  void Transition();

   DISALLOW_IMPLICIT_CONSTRUCTORS(OrderedHashTableIterator);
 };
@@ -10066,7 +10057,8 @@
   static inline JSSetIterator* cast(Object* obj);

   static Handle<Object> ValueForKind(
-      Handle<JSSetIterator> iterator, int entry_index);
+      Handle<JSSetIterator> iterator,
+      int entry_index);

  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(JSSetIterator);
@@ -10089,7 +10081,8 @@
   static inline JSMapIterator* cast(Object* obj);

   static Handle<Object> ValueForKind(
-      Handle<JSMapIterator> iterator, int entry_index);
+      Handle<JSMapIterator> iterator,
+      int entry_index);

  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(JSMapIterator);
=======================================
--- /branches/bleeding_edge/src/runtime.cc      Tue May 20 14:03:38 2014 UTC
+++ /branches/bleeding_edge/src/runtime.cc      Tue May 20 14:22:05 2014 UTC
@@ -1598,15 +1598,6 @@
   CONVERT_ARG_HANDLE_CHECKED(JSSetIterator, holder, 0);
   return *JSSetIterator::Next(holder);
 }
-
-
-RUNTIME_FUNCTION(Runtime_SetIteratorClose) {
-  HandleScope scope(isolate);
-  ASSERT(args.length() == 1);
-  CONVERT_ARG_HANDLE_CHECKED(JSSetIterator, holder, 0);
-  holder->Close();
-  return isolate->heap()->undefined_value();
-}


 RUNTIME_FUNCTION(Runtime_MapInitialize) {
@@ -1707,15 +1698,6 @@
   CONVERT_ARG_HANDLE_CHECKED(JSMapIterator, holder, 0);
   return *JSMapIterator::Next(holder);
 }
-
-
-RUNTIME_FUNCTION(Runtime_MapIteratorClose) {
-  HandleScope scope(isolate);
-  ASSERT(args.length() == 1);
-  CONVERT_ARG_HANDLE_CHECKED(JSMapIterator, holder, 0);
-  holder->Close();
-  return isolate->heap()->undefined_value();
-}


 static Handle<JSWeakCollection> WeakCollectionInitialize(
=======================================
--- /branches/bleeding_edge/src/runtime.h       Mon May 19 07:57:04 2014 UTC
+++ /branches/bleeding_edge/src/runtime.h       Tue May 20 14:22:05 2014 UTC
@@ -276,7 +276,6 @@
   F(SetCreateIterator, 2, 1) \
   \
   F(SetIteratorNext, 1, 1) \
-  F(SetIteratorClose, 1, 1) \
   \
   /* Harmony maps */ \
   F(MapInitialize, 1, 1) \
@@ -289,7 +288,6 @@
   F(MapCreateIterator, 2, 1) \
   \
   F(MapIteratorNext, 1, 1) \
-  F(MapIteratorClose, 1, 1) \
   \
   /* Harmony weak maps and sets */ \
   F(WeakCollectionInitialize, 1, 1) \
=======================================
--- /branches/bleeding_edge/test/mjsunit/harmony/collections.js Tue May 6 14:48:34 2014 UTC +++ /branches/bleeding_edge/test/mjsunit/harmony/collections.js Tue May 20 14:22:05 2014 UTC
@@ -852,3 +852,138 @@
   });
   assertEquals(4950, accumulated);
 })();
+
+
+(function TestMapForEachAllRemovedTransition() {
+  var map = new Map;
+  map.set(0, 0);
+
+  var buffer = [];
+  map.forEach(function(v) {
+    buffer.push(v);
+    if (v === 0) {
+      for (var i = 1; i < 4; i++) {
+        map.set(i, i);
+      }
+    }
+
+    if (v === 3) {
+      for (var i = 0; i < 4; i++) {
+        map.delete(i);
+      }
+      for (var i = 4; i < 8; i++) {
+        map.set(i, i);
+      }
+    }
+  });
+
+  assertArrayEquals([0, 1, 2, 3, 4, 5, 6, 7], buffer);
+})();
+
+
+(function TestMapForEachClearTransition() {
+  var map = new Map;
+  map.set(0, 0);
+
+  var i = 0;
+  var buffer = [];
+  map.forEach(function(v) {
+    buffer.push(v);
+    if (++i < 5) {
+      for (var j = 0; j < 5; j++) {
+        map.clear();
+        map.set(i, i);
+      }
+    }
+  });
+
+  assertArrayEquals([0, 1, 2, 3, 4], buffer);
+})();
+
+
+(function TestMapForEachNestedNonTrivialTransition() {
+  var map = new Map;
+  map.set(0, 0);
+  map.set(1, 1);
+  map.set(2, 2);
+  map.set(3, 3);
+  map.delete(0);
+
+  var i = 0;
+  var buffer = [];
+  map.forEach(function(v) {
+    if (++i > 10) return;
+
+    buffer.push(v);
+
+    if (v == 3) {
+      map.delete(1);
+      for (var j = 4; j < 10; j++) {
+        map.set(j, j);
+      }
+      for (var j = 4; j < 10; j += 2) {
+        map.delete(j);
+      }
+      map.delete(2);
+
+      for (var j = 10; j < 20; j++) {
+        map.set(j, j);
+      }
+      for (var j = 10; j < 20; j += 2) {
+        map.delete(j);
+      }
+
+      map.delete(3);
+    }
+  });
+
+  assertArrayEquals([1, 2, 3, 5, 7, 9, 11, 13, 15, 17], buffer);
+})();
+
+
+(function TestMapForEachAllRemovedTransitionNoClear() {
+  var map = new Map;
+  map.set(0, 0);
+
+  var buffer = [];
+  map.forEach(function(v) {
+    buffer.push(v);
+    if (v === 0) {
+      for (var i = 1; i < 8; i++) {
+        map.set(i, i);
+      }
+    }
+
+    if (v === 4) {
+      for (var i = 0; i < 8; i++) {
+        map.delete(i);
+      }
+    }
+  });
+
+  assertArrayEquals([0, 1, 2, 3, 4], buffer);
+})();
+
+
+(function TestMapForEachNoMoreElementsAfterTransition() {
+  var map = new Map;
+  map.set(0, 0);
+
+  var buffer = [];
+  map.forEach(function(v) {
+    buffer.push(v);
+    if (v === 0) {
+      for (var i = 1; i < 16; i++) {
+        map.set(i, i);
+      }
+    }
+
+    if (v === 4) {
+      for (var i = 5; i < 16; i++) {
+        map.delete(i);
+      }
+    }
+  });
+
+  assertArrayEquals([0, 1, 2, 3, 4], buffer);
+})();
=======================================
--- /branches/bleeding_edge/tools/generate-runtime-tests.py Mon May 19 08:19:54 2014 UTC +++ /branches/bleeding_edge/tools/generate-runtime-tests.py Tue May 20 14:22:05 2014 UTC
@@ -47,8 +47,8 @@
# that the parser doesn't bit-rot. Change the values as needed when you add, # remove or change runtime functions, but make sure we don't lose our ability
 # to parse them!
-EXPECTED_FUNCTION_COUNT = 361
-EXPECTED_FUZZABLE_COUNT = 328
+EXPECTED_FUNCTION_COUNT = 359
+EXPECTED_FUZZABLE_COUNT = 326
 EXPECTED_CCTEST_COUNT = 6
 EXPECTED_UNKNOWN_COUNT = 5
 EXPECTED_BUILTINS_COUNT = 824

--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
--- You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to