Title: [181968] trunk/Source/_javascript_Core
Revision
181968
Author
[email protected]
Date
2015-03-25 11:37:17 -0700 (Wed, 25 Mar 2015)

Log Message

New map and set modification tests in r181922 fails
https://bugs.webkit.org/show_bug.cgi?id=143031

Reviewed and tweaked by Geoffrey Garen.

When packing Map/Set backing store, we need to decrement Map/Set iterator's m_index
to adjust for the packed backing store.

Consider the following map data.

x: deleted, o: exists
0 1 2 3 4
x x x x o

And iterator with m_index 3.

When packing the map data, map data will become,

0
o

At that time, we perfom didRemoveEntry 4 times on iterators.
times => m_index/index/result
1 => 3/0/dec
2 => 2/1/dec
3 => 1/2/nothing
4 => 1/3/nothing

After iteration, iterator's m_index becomes 1. But we expected that becomes 0.
This is because if we use decremented m_index for comparison,
while provided deletedIndex is the index in old storage, m_index is the index in partially packed storage.

In this patch, we compare against the packed index instead.
times => m_index/packedIndex/result
1 => 3/0/dec
2 => 2/0/dec
3 => 1/0/dec
4 => 0/0/nothing

So m_index becomes 0 as expected.

And according to the spec, once the iterator is closed (becomes done: true),
its internal [[Map]]/[[Set]] is set to undefined.
So after the iterator is finished, we don't revive the iterator (e.g. by clearing m_index = 0).

In this patch, we change 2 things.
1.
Compare an iterator's index against the packed index when removing an entry.

2.
If the iterator is closed (isFinished()), we don't apply adjustment to the iterator.

Patch by Yusuke Suzuki <[email protected]> on 2015-03-25

* runtime/MapData.h:
(JSC::MapDataImpl::IteratorData::finish):
(JSC::MapDataImpl::IteratorData::isFinished):
(JSC::MapDataImpl::IteratorData::didRemoveEntry):
(JSC::MapDataImpl::IteratorData::didRemoveAllEntries):
(JSC::MapDataImpl::IteratorData::startPackBackingStore):
* runtime/MapDataInlines.h:
(JSC::JSIterator>::replaceAndPackBackingStore):
* tests/stress/modify-map-during-iteration.js:
* tests/stress/modify-set-during-iteration.js:

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (181967 => 181968)


--- trunk/Source/_javascript_Core/ChangeLog	2015-03-25 18:29:19 UTC (rev 181967)
+++ trunk/Source/_javascript_Core/ChangeLog	2015-03-25 18:37:17 UTC (rev 181968)
@@ -1,3 +1,68 @@
+2015-03-25  Yusuke Suzuki  <[email protected]>
+
+        New map and set modification tests in r181922 fails
+        https://bugs.webkit.org/show_bug.cgi?id=143031
+
+        Reviewed and tweaked by Geoffrey Garen.
+
+        When packing Map/Set backing store, we need to decrement Map/Set iterator's m_index
+        to adjust for the packed backing store.
+
+        Consider the following map data.
+
+        x: deleted, o: exists
+        0 1 2 3 4
+        x x x x o
+
+        And iterator with m_index 3.
+
+        When packing the map data, map data will become,
+
+        0
+        o
+
+        At that time, we perfom didRemoveEntry 4 times on iterators.
+        times => m_index/index/result
+        1 => 3/0/dec
+        2 => 2/1/dec
+        3 => 1/2/nothing
+        4 => 1/3/nothing
+
+        After iteration, iterator's m_index becomes 1. But we expected that becomes 0.
+        This is because if we use decremented m_index for comparison,
+        while provided deletedIndex is the index in old storage, m_index is the index in partially packed storage.
+
+        In this patch, we compare against the packed index instead.
+        times => m_index/packedIndex/result
+        1 => 3/0/dec
+        2 => 2/0/dec
+        3 => 1/0/dec
+        4 => 0/0/nothing
+
+        So m_index becomes 0 as expected.
+
+        And according to the spec, once the iterator is closed (becomes done: true),
+        its internal [[Map]]/[[Set]] is set to undefined.
+        So after the iterator is finished, we don't revive the iterator (e.g. by clearing m_index = 0).
+
+        In this patch, we change 2 things.
+        1.
+        Compare an iterator's index against the packed index when removing an entry.
+
+        2.
+        If the iterator is closed (isFinished()), we don't apply adjustment to the iterator.
+
+        * runtime/MapData.h:
+        (JSC::MapDataImpl::IteratorData::finish):
+        (JSC::MapDataImpl::IteratorData::isFinished):
+        (JSC::MapDataImpl::IteratorData::didRemoveEntry):
+        (JSC::MapDataImpl::IteratorData::didRemoveAllEntries):
+        (JSC::MapDataImpl::IteratorData::startPackBackingStore):
+        * runtime/MapDataInlines.h:
+        (JSC::JSIterator>::replaceAndPackBackingStore):
+        * tests/stress/modify-map-during-iteration.js:
+        * tests/stress/modify-set-during-iteration.js:
+
 2015-03-24  Joseph Pecoraro  <[email protected]>
 
         Setter should have a single formal parameter, Getter no parameters

Modified: trunk/Source/_javascript_Core/runtime/MapData.h (181967 => 181968)


--- trunk/Source/_javascript_Core/runtime/MapData.h	2015-03-25 18:29:19 UTC (rev 181967)
+++ trunk/Source/_javascript_Core/runtime/MapData.h	2015-03-25 18:37:17 UTC (rev 181968)
@@ -54,15 +54,23 @@
         IteratorData(const MapDataImpl*);
         bool next(WTF::KeyValuePair<JSValue, JSValue>&);
 
-        void didRemoveEntry(int32_t index)
+        // This function is called while packing a map's backing store. The
+        // passed-in index is the new index the entry would have after packing.
+        void didRemoveEntry(int32_t packedIndex)
         {
-            if (m_index <= index)
+            if (isFinished())
                 return;
+
+            if (m_index <= packedIndex)
+                return;
+
             --m_index;
         }
 
         void didRemoveAllEntries()
         {
+            if (isFinished())
+                return;
             m_index = 0;
         }
 

Modified: trunk/Source/_javascript_Core/runtime/MapDataInlines.h (181967 => 181968)


--- trunk/Source/_javascript_Core/runtime/MapDataInlines.h	2015-03-25 18:29:19 UTC (rev 181967)
+++ trunk/Source/_javascript_Core/runtime/MapDataInlines.h	2015-03-25 18:37:17 UTC (rev 181968)
@@ -178,8 +178,8 @@
     for (int32_t i = 0; i < m_size; i++) {
         Entry& entry = m_entries[i];
         if (!entry.key()) {
-            m_iterators.forEach([i](JSIterator* iterator, JSIterator*) {
-                iterator->iteratorData()->didRemoveEntry(i);
+            m_iterators.forEach([newEnd](JSIterator* iterator, JSIterator*) {
+                iterator->iteratorData()->didRemoveEntry(newEnd);
             });
             continue;
         }

Modified: trunk/Source/_javascript_Core/tests/stress/modify-map-during-iteration.js (181967 => 181968)


--- trunk/Source/_javascript_Core/tests/stress/modify-map-during-iteration.js	2015-03-25 18:29:19 UTC (rev 181967)
+++ trunk/Source/_javascript_Core/tests/stress/modify-map-during-iteration.js	2015-03-25 18:37:17 UTC (rev 181968)
@@ -74,3 +74,24 @@
     throw new Error("unreeachable.");
 }
 
+var map = new Map();
+for (var i = 0; i < 5; ++i)
+    map.set(i, i);
+testValue(map.size, 5);
+var iter = map.keys();
+testValue(iter.next().value, 0);
+testValue(iter.next().value, 1);
+testValue(iter.next().value, 2);
+testValue(iter.next().value, 3);
+map.delete(0);
+map.delete(1);
+map.delete(2);
+map.delete(3);
+// It will cause MapData packing.
+for (var i = 5; i < 1000; ++i)
+    map.set(i, i);
+gc();
+for (var i = 4; i < 1000; ++i)
+    testValue(iter.next().value, i);
+testValue(iter.next().value, undefined);
+

Modified: trunk/Source/_javascript_Core/tests/stress/modify-set-during-iteration.js (181967 => 181968)


--- trunk/Source/_javascript_Core/tests/stress/modify-set-during-iteration.js	2015-03-25 18:29:19 UTC (rev 181967)
+++ trunk/Source/_javascript_Core/tests/stress/modify-set-during-iteration.js	2015-03-25 18:37:17 UTC (rev 181968)
@@ -70,3 +70,22 @@
     throw new Error("unreeachable.");
 }
 
+var set = new Set([0, 1, 2, 3, 4]);
+var iter = set[Symbol.iterator]();
+testValue(set.size, 5);
+testValue(iter.next().value, 0);
+testValue(iter.next().value, 1);
+testValue(iter.next().value, 2);
+testValue(iter.next().value, 3);
+set.delete(0);
+set.delete(1);
+set.delete(2);
+set.delete(3);
+// It will cause MapData packing.
+for (var i = 5; i < 1000; ++i)
+    set.add(i);
+gc();
+for (var i = 4; i < 1000; ++i)
+    testValue(iter.next().value, i);
+testValue(iter.next().value, undefined);
+
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to