<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[181922] trunk/Source</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/181922">181922</a></dd>
<dt>Author</dt> <dd>ggaren@apple.com</dd>
<dt>Date</dt> <dd>2015-03-24 17:19:05 -0700 (Tue, 24 Mar 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>REGRESSION (<a href="http://trac.webkit.org/projects/webkit/changeset/181458">r181458</a>): Heap use-after-free in JSSetIterator destructor
https://bugs.webkit.org/show_bug.cgi?id=142696

Reviewed and tweaked by Geoffrey Garen.

Source/JavaScriptCore:

Before <a href="http://trac.webkit.org/projects/webkit/changeset/142556">r142556</a>, JSSetIterator::destroy was not defined.
So accidentally MapData::const_iterator in JSSet was never destroyed.
But it had non trivial destructor, decrementing MapData-&gt;m_iteratorCount.

After <a href="http://trac.webkit.org/projects/webkit/changeset/142556">r142556</a>, JSSetIterator::destroy works.
It correctly destruct MapData::const_iterator and m_iteratorCount partially works.
But JSSetIterator::~JSSetIterator requires owned JSSet since it mutates MapData-&gt;m_iteratorCount.

It is guaranteed that JSSet is live since JSSetIterator has a reference to JSSet
and marks it in visitChildren (WriteBarrier&lt;Unknown&gt;).
However, the order of destructions is not guaranteed in GC-ed system.

Consider the following case,
allocate JSSet and subsequently allocate JSSetIterator.
And they resides in the separated MarkedBlock, &lt;1&gt; and &lt;2&gt;.

JSSet&lt;1&gt; &lt;- JSSetIterator&lt;2&gt;

And after that, when performing GC, Marker decides that the above 2 objects are not marked.
And Marker also decides MarkedBlocks &lt;1&gt; and &lt;2&gt; can be sweeped.

First Sweeper sweep &lt;1&gt;, destruct JSSet&lt;1&gt; and free MarkedBlock&lt;1&gt;.
Second Sweeper sweep &lt;2&gt;, attempt to destruct JSSetIterator&lt;2&gt;.
However, JSSetIterator&lt;2&gt;'s destructor,
JSSetIterator::~JSSetIterator requires live JSSet&lt;1&gt;, it causes use-after-free.

In this patch, we introduce WeakGCMap into JSMap/JSSet to track live iterators.
When packing the removed elements in JSSet/JSMap, we apply the change to all live
iterators tracked by WeakGCMap.

WeakGCMap can only track JSCell since they are managed by GC.
So we drop JSSet/JSMap C++ style iterators. Instead of C++ style iterator, this patch
introduces JS style iterator signatures into C++ class IteratorData.
If we need to iterate over JSMap/JSSet, use JSSetIterator/JSMapIterator instead of using
IteratorData directly.

Patch by Yusuke Suzuki &lt;utatane.tea@gmail.com&gt; on 2015-03-24

* runtime/JSMap.cpp:
(JSC::JSMap::destroy):
* runtime/JSMap.h:
(JSC::JSMap::JSMap):
(JSC::JSMap::begin): Deleted.
(JSC::JSMap::end): Deleted.
* runtime/JSMapIterator.cpp:
(JSC::JSMapIterator::destroy):
* runtime/JSMapIterator.h:
(JSC::JSMapIterator::next):
(JSC::JSMapIterator::nextKeyValue):
(JSC::JSMapIterator::iteratorData):
(JSC::JSMapIterator::JSMapIterator):
* runtime/JSSet.cpp:
(JSC::JSSet::destroy):
* runtime/JSSet.h:
(JSC::JSSet::JSSet):
(JSC::JSSet::begin): Deleted.
(JSC::JSSet::end): Deleted.
* runtime/JSSetIterator.cpp:
(JSC::JSSetIterator::destroy):
* runtime/JSSetIterator.h:
(JSC::JSSetIterator::next):
(JSC::JSSetIterator::iteratorData):
(JSC::JSSetIterator::JSSetIterator):
* runtime/MapData.h:
(JSC::MapDataImpl::IteratorData::finish):
(JSC::MapDataImpl::IteratorData::isFinished):
(JSC::MapDataImpl::shouldPack):
(JSC::JSIterator&gt;::MapDataImpl):
(JSC::JSIterator&gt;::KeyType::KeyType):
(JSC::JSIterator&gt;::IteratorData::IteratorData):
(JSC::JSIterator&gt;::IteratorData::next):
(JSC::JSIterator&gt;::IteratorData::ensureSlot):
(JSC::JSIterator&gt;::IteratorData::applyMapDataPatch):
(JSC::JSIterator&gt;::IteratorData::refreshCursor):
(JSC::MapDataImpl::const_iterator::key): Deleted.
(JSC::MapDataImpl::const_iterator::value): Deleted.
(JSC::MapDataImpl::const_iterator::operator++): Deleted.
(JSC::MapDataImpl::const_iterator::finish): Deleted.
(JSC::MapDataImpl::const_iterator::atEnd): Deleted.
(JSC::MapDataImpl::begin): Deleted.
(JSC::MapDataImpl::end): Deleted.
(JSC::MapDataImpl&lt;Entry&gt;::MapDataImpl): Deleted.
(JSC::MapDataImpl&lt;Entry&gt;::clear): Deleted.
(JSC::MapDataImpl&lt;Entry&gt;::KeyType::KeyType): Deleted.
(JSC::MapDataImpl&lt;Entry&gt;::const_iterator::internalIncrement): Deleted.
(JSC::MapDataImpl&lt;Entry&gt;::const_iterator::ensureSlot): Deleted.
(JSC::MapDataImpl&lt;Entry&gt;::const_iterator::const_iterator): Deleted.
(JSC::MapDataImpl&lt;Entry&gt;::const_iterator::~const_iterator): Deleted.
(JSC::MapDataImpl&lt;Entry&gt;::const_iterator::operator): Deleted.
(JSC::=): Deleted.
* runtime/MapDataInlines.h:
(JSC::JSIterator&gt;::clear):
(JSC::JSIterator&gt;::find):
(JSC::JSIterator&gt;::contains):
(JSC::JSIterator&gt;::add):
(JSC::JSIterator&gt;::set):
(JSC::JSIterator&gt;::get):
(JSC::JSIterator&gt;::remove):
(JSC::JSIterator&gt;::replaceAndPackBackingStore):
(JSC::JSIterator&gt;::replaceBackingStore):
(JSC::JSIterator&gt;::ensureSpaceForAppend):
(JSC::JSIterator&gt;::visitChildren):
(JSC::JSIterator&gt;::copyBackingStore):
(JSC::JSIterator&gt;::applyMapDataPatch):
(JSC::MapDataImpl&lt;Entry&gt;::find): Deleted.
(JSC::MapDataImpl&lt;Entry&gt;::contains): Deleted.
(JSC::MapDataImpl&lt;Entry&gt;::add): Deleted.
(JSC::MapDataImpl&lt;Entry&gt;::set): Deleted.
(JSC::MapDataImpl&lt;Entry&gt;::get): Deleted.
(JSC::MapDataImpl&lt;Entry&gt;::remove): Deleted.
(JSC::MapDataImpl&lt;Entry&gt;::replaceAndPackBackingStore): Deleted.
(JSC::MapDataImpl&lt;Entry&gt;::replaceBackingStore): Deleted.
(JSC::MapDataImpl&lt;Entry&gt;::ensureSpaceForAppend): Deleted.
(JSC::MapDataImpl&lt;Entry&gt;::visitChildren): Deleted.
(JSC::MapDataImpl&lt;Entry&gt;::copyBackingStore): Deleted.
* runtime/MapPrototype.cpp:
(JSC::mapProtoFuncForEach):
* runtime/SetPrototype.cpp:
(JSC::setProtoFuncForEach):
* runtime/WeakGCMap.h:
(JSC::WeakGCMap::forEach):
* tests/stress/modify-map-during-iteration.js: Added.
(testValue):
(identityPairs):
(.set if):
(var):
(set map):
* tests/stress/modify-set-during-iteration.js: Added.
(testValue):
(set forEach):
(set delete):

Source/WebCore:

Use JSSetIterator/JSMapIterator to iterate over JSSet and JSMap.

Patch by Yusuke Suzuki &lt;utatane.tea@gmail.com&gt; on 2015-03-24

* ForwardingHeaders/runtime/JSMapIterator.h: Added.
* ForwardingHeaders/runtime/JSSetIterator.h: Added.
* bindings/js/SerializedScriptValue.cpp:
(WebCore::CloneSerializer::serialize):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreJavaScriptCorexcodeprojprojectpbxproj">trunk/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeJSMapcpp">trunk/Source/JavaScriptCore/runtime/JSMap.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeJSMaph">trunk/Source/JavaScriptCore/runtime/JSMap.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeJSMapIteratorcpp">trunk/Source/JavaScriptCore/runtime/JSMapIterator.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeJSMapIteratorh">trunk/Source/JavaScriptCore/runtime/JSMapIterator.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeJSSetcpp">trunk/Source/JavaScriptCore/runtime/JSSet.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeJSSeth">trunk/Source/JavaScriptCore/runtime/JSSet.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeJSSetIteratorcpp">trunk/Source/JavaScriptCore/runtime/JSSetIterator.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeJSSetIteratorh">trunk/Source/JavaScriptCore/runtime/JSSetIterator.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeMapDatah">trunk/Source/JavaScriptCore/runtime/MapData.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeMapDataInlinesh">trunk/Source/JavaScriptCore/runtime/MapDataInlines.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeMapPrototypecpp">trunk/Source/JavaScriptCore/runtime/MapPrototype.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeSetPrototypecpp">trunk/Source/JavaScriptCore/runtime/SetPrototype.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeWeakGCMaph">trunk/Source/JavaScriptCore/runtime/WeakGCMap.h</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCorebindingsjsSerializedScriptValuecpp">trunk/Source/WebCore/bindings/js/SerializedScriptValue.cpp</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoretestsstressmodifymapduringiterationjs">trunk/Source/JavaScriptCore/tests/stress/modify-map-during-iteration.js</a></li>
<li><a href="#trunkSourceJavaScriptCoretestsstressmodifysetduringiterationjs">trunk/Source/JavaScriptCore/tests/stress/modify-set-during-iteration.js</a></li>
<li><a href="#trunkSourceWebCoreForwardingHeadersruntimeJSMapIteratorh">trunk/Source/WebCore/ForwardingHeaders/runtime/JSMapIterator.h</a></li>
<li><a href="#trunkSourceWebCoreForwardingHeadersruntimeJSSetIteratorh">trunk/Source/WebCore/ForwardingHeaders/runtime/JSSetIterator.h</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (181921 => 181922)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2015-03-24 23:54:04 UTC (rev 181921)
+++ trunk/Source/JavaScriptCore/ChangeLog        2015-03-25 00:19:05 UTC (rev 181922)
</span><span class="lines">@@ -1,3 +1,140 @@
</span><ins>+2015-03-24  Yusuke Suzuki  &lt;utatane.tea@gmail.com&gt;
+
+        REGRESSION (r181458): Heap use-after-free in JSSetIterator destructor
+        https://bugs.webkit.org/show_bug.cgi?id=142696
+
+        Reviewed and tweaked by Geoffrey Garen.
+
+        Before r142556, JSSetIterator::destroy was not defined.
+        So accidentally MapData::const_iterator in JSSet was never destroyed.
+        But it had non trivial destructor, decrementing MapData-&gt;m_iteratorCount.
+
+        After r142556, JSSetIterator::destroy works.
+        It correctly destruct MapData::const_iterator and m_iteratorCount partially works.
+        But JSSetIterator::~JSSetIterator requires owned JSSet since it mutates MapData-&gt;m_iteratorCount.
+
+        It is guaranteed that JSSet is live since JSSetIterator has a reference to JSSet
+        and marks it in visitChildren (WriteBarrier&lt;Unknown&gt;).
+        However, the order of destructions is not guaranteed in GC-ed system.
+
+        Consider the following case,
+        allocate JSSet and subsequently allocate JSSetIterator.
+        And they resides in the separated MarkedBlock, &lt;1&gt; and &lt;2&gt;.
+
+        JSSet&lt;1&gt; &lt;- JSSetIterator&lt;2&gt;
+
+        And after that, when performing GC, Marker decides that the above 2 objects are not marked.
+        And Marker also decides MarkedBlocks &lt;1&gt; and &lt;2&gt; can be sweeped.
+
+        First Sweeper sweep &lt;1&gt;, destruct JSSet&lt;1&gt; and free MarkedBlock&lt;1&gt;.
+        Second Sweeper sweep &lt;2&gt;, attempt to destruct JSSetIterator&lt;2&gt;.
+        However, JSSetIterator&lt;2&gt;'s destructor,
+        JSSetIterator::~JSSetIterator requires live JSSet&lt;1&gt;, it causes use-after-free.
+
+        In this patch, we introduce WeakGCMap into JSMap/JSSet to track live iterators.
+        When packing the removed elements in JSSet/JSMap, we apply the change to all live
+        iterators tracked by WeakGCMap.
+
+        WeakGCMap can only track JSCell since they are managed by GC.
+        So we drop JSSet/JSMap C++ style iterators. Instead of C++ style iterator, this patch
+        introduces JS style iterator signatures into C++ class IteratorData.
+        If we need to iterate over JSMap/JSSet, use JSSetIterator/JSMapIterator instead of using
+        IteratorData directly.
+
+        * runtime/JSMap.cpp:
+        (JSC::JSMap::destroy):
+        * runtime/JSMap.h:
+        (JSC::JSMap::JSMap):
+        (JSC::JSMap::begin): Deleted.
+        (JSC::JSMap::end): Deleted.
+        * runtime/JSMapIterator.cpp:
+        (JSC::JSMapIterator::destroy):
+        * runtime/JSMapIterator.h:
+        (JSC::JSMapIterator::next):
+        (JSC::JSMapIterator::nextKeyValue):
+        (JSC::JSMapIterator::iteratorData):
+        (JSC::JSMapIterator::JSMapIterator):
+        * runtime/JSSet.cpp:
+        (JSC::JSSet::destroy):
+        * runtime/JSSet.h:
+        (JSC::JSSet::JSSet):
+        (JSC::JSSet::begin): Deleted.
+        (JSC::JSSet::end): Deleted.
+        * runtime/JSSetIterator.cpp:
+        (JSC::JSSetIterator::destroy):
+        * runtime/JSSetIterator.h:
+        (JSC::JSSetIterator::next):
+        (JSC::JSSetIterator::iteratorData):
+        (JSC::JSSetIterator::JSSetIterator):
+        * runtime/MapData.h:
+        (JSC::MapDataImpl::IteratorData::finish):
+        (JSC::MapDataImpl::IteratorData::isFinished):
+        (JSC::MapDataImpl::shouldPack):
+        (JSC::JSIterator&gt;::MapDataImpl):
+        (JSC::JSIterator&gt;::KeyType::KeyType):
+        (JSC::JSIterator&gt;::IteratorData::IteratorData):
+        (JSC::JSIterator&gt;::IteratorData::next):
+        (JSC::JSIterator&gt;::IteratorData::ensureSlot):
+        (JSC::JSIterator&gt;::IteratorData::applyMapDataPatch):
+        (JSC::JSIterator&gt;::IteratorData::refreshCursor):
+        (JSC::MapDataImpl::const_iterator::key): Deleted.
+        (JSC::MapDataImpl::const_iterator::value): Deleted.
+        (JSC::MapDataImpl::const_iterator::operator++): Deleted.
+        (JSC::MapDataImpl::const_iterator::finish): Deleted.
+        (JSC::MapDataImpl::const_iterator::atEnd): Deleted.
+        (JSC::MapDataImpl::begin): Deleted.
+        (JSC::MapDataImpl::end): Deleted.
+        (JSC::MapDataImpl&lt;Entry&gt;::MapDataImpl): Deleted.
+        (JSC::MapDataImpl&lt;Entry&gt;::clear): Deleted.
+        (JSC::MapDataImpl&lt;Entry&gt;::KeyType::KeyType): Deleted.
+        (JSC::MapDataImpl&lt;Entry&gt;::const_iterator::internalIncrement): Deleted.
+        (JSC::MapDataImpl&lt;Entry&gt;::const_iterator::ensureSlot): Deleted.
+        (JSC::MapDataImpl&lt;Entry&gt;::const_iterator::const_iterator): Deleted.
+        (JSC::MapDataImpl&lt;Entry&gt;::const_iterator::~const_iterator): Deleted.
+        (JSC::MapDataImpl&lt;Entry&gt;::const_iterator::operator): Deleted.
+        (JSC::=): Deleted.
+        * runtime/MapDataInlines.h:
+        (JSC::JSIterator&gt;::clear):
+        (JSC::JSIterator&gt;::find):
+        (JSC::JSIterator&gt;::contains):
+        (JSC::JSIterator&gt;::add):
+        (JSC::JSIterator&gt;::set):
+        (JSC::JSIterator&gt;::get):
+        (JSC::JSIterator&gt;::remove):
+        (JSC::JSIterator&gt;::replaceAndPackBackingStore):
+        (JSC::JSIterator&gt;::replaceBackingStore):
+        (JSC::JSIterator&gt;::ensureSpaceForAppend):
+        (JSC::JSIterator&gt;::visitChildren):
+        (JSC::JSIterator&gt;::copyBackingStore):
+        (JSC::JSIterator&gt;::applyMapDataPatch):
+        (JSC::MapDataImpl&lt;Entry&gt;::find): Deleted.
+        (JSC::MapDataImpl&lt;Entry&gt;::contains): Deleted.
+        (JSC::MapDataImpl&lt;Entry&gt;::add): Deleted.
+        (JSC::MapDataImpl&lt;Entry&gt;::set): Deleted.
+        (JSC::MapDataImpl&lt;Entry&gt;::get): Deleted.
+        (JSC::MapDataImpl&lt;Entry&gt;::remove): Deleted.
+        (JSC::MapDataImpl&lt;Entry&gt;::replaceAndPackBackingStore): Deleted.
+        (JSC::MapDataImpl&lt;Entry&gt;::replaceBackingStore): Deleted.
+        (JSC::MapDataImpl&lt;Entry&gt;::ensureSpaceForAppend): Deleted.
+        (JSC::MapDataImpl&lt;Entry&gt;::visitChildren): Deleted.
+        (JSC::MapDataImpl&lt;Entry&gt;::copyBackingStore): Deleted.
+        * runtime/MapPrototype.cpp:
+        (JSC::mapProtoFuncForEach):
+        * runtime/SetPrototype.cpp:
+        (JSC::setProtoFuncForEach):
+        * runtime/WeakGCMap.h:
+        (JSC::WeakGCMap::forEach):
+        * tests/stress/modify-map-during-iteration.js: Added.
+        (testValue):
+        (identityPairs):
+        (.set if):
+        (var):
+        (set map):
+        * tests/stress/modify-set-during-iteration.js: Added.
+        (testValue):
+        (set forEach):
+        (set delete):
+
</ins><span class="cx"> 2015-03-24  Mark Lam  &lt;mark.lam@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         The ExecutionTimeLimit test should use its own JSGlobalContextRef.
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreJavaScriptCorexcodeprojprojectpbxproj"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj (181921 => 181922)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj        2015-03-24 23:54:04 UTC (rev 181921)
+++ trunk/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj        2015-03-25 00:19:05 UTC (rev 181922)
</span><span class="lines">@@ -1305,7 +1305,7 @@
</span><span class="cx">                 A74DEF93182D991400522C22 /* MapIteratorPrototype.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A74DEF8D182D991400522C22 /* MapIteratorPrototype.cpp */; };
</span><span class="cx">                 A74DEF94182D991400522C22 /* MapIteratorPrototype.h in Headers */ = {isa = PBXBuildFile; fileRef = A74DEF8E182D991400522C22 /* MapIteratorPrototype.h */; };
</span><span class="cx">                 A74DEF95182D991400522C22 /* JSMapIterator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A74DEF8F182D991400522C22 /* JSMapIterator.cpp */; };
</span><del>-                A74DEF96182D991400522C22 /* JSMapIterator.h in Headers */ = {isa = PBXBuildFile; fileRef = A74DEF90182D991400522C22 /* JSMapIterator.h */; };
</del><ins>+                A74DEF96182D991400522C22 /* JSMapIterator.h in Headers */ = {isa = PBXBuildFile; fileRef = A74DEF90182D991400522C22 /* JSMapIterator.h */; settings = {ATTRIBUTES = (Private, ); }; };
</ins><span class="cx">                 A75706DE118A2BCF0057F88F /* JITArithmetic32_64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A75706DD118A2BCF0057F88F /* JITArithmetic32_64.cpp */; };
</span><span class="cx">                 A75EE9B218AAB7E200AAD043 /* BuiltinNames.h in Headers */ = {isa = PBXBuildFile; fileRef = A75EE9B018AAB7E200AAD043 /* BuiltinNames.h */; };
</span><span class="cx">                 A76140CD182982CB00750624 /* ArgumentsIteratorConstructor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A76140C7182982CB00750624 /* ArgumentsIteratorConstructor.cpp */; };
</span><span class="lines">@@ -1352,7 +1352,7 @@
</span><span class="cx">                 A790DD6D182F499700588807 /* SetIteratorPrototype.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A790DD67182F499700588807 /* SetIteratorPrototype.cpp */; };
</span><span class="cx">                 A790DD6E182F499700588807 /* SetIteratorPrototype.h in Headers */ = {isa = PBXBuildFile; fileRef = A790DD68182F499700588807 /* SetIteratorPrototype.h */; };
</span><span class="cx">                 A790DD6F182F499700588807 /* JSSetIterator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A790DD69182F499700588807 /* JSSetIterator.cpp */; };
</span><del>-                A790DD70182F499700588807 /* JSSetIterator.h in Headers */ = {isa = PBXBuildFile; fileRef = A790DD6A182F499700588807 /* JSSetIterator.h */; };
</del><ins>+                A790DD70182F499700588807 /* JSSetIterator.h in Headers */ = {isa = PBXBuildFile; fileRef = A790DD6A182F499700588807 /* JSSetIterator.h */; settings = {ATTRIBUTES = (Private, ); }; };
</ins><span class="cx">                 A7986D5717A0BB1E00A95DD0 /* DFGEdgeUsesStructure.h in Headers */ = {isa = PBXBuildFile; fileRef = A7986D5617A0BB1E00A95DD0 /* DFGEdgeUsesStructure.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="cx">                 A7A4AE0817973B26005612B1 /* MacroAssemblerX86Common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A7A4AE0717973B26005612B1 /* MacroAssemblerX86Common.cpp */; };
</span><span class="cx">                 A7A4AE1017973B4D005612B1 /* JITStubsX86Common.h in Headers */ = {isa = PBXBuildFile; fileRef = A7A4AE0C17973B4D005612B1 /* JITStubsX86Common.h */; };
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeJSMapcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/JSMap.cpp (181921 => 181922)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/JSMap.cpp        2015-03-24 23:54:04 UTC (rev 181921)
+++ trunk/Source/JavaScriptCore/runtime/JSMap.cpp        2015-03-25 00:19:05 UTC (rev 181922)
</span><span class="lines">@@ -27,6 +27,7 @@
</span><span class="cx"> #include &quot;JSMap.h&quot;
</span><span class="cx"> 
</span><span class="cx"> #include &quot;JSCJSValueInlines.h&quot;
</span><ins>+#include &quot;JSMapIterator.h&quot;
</ins><span class="cx"> #include &quot;MapDataInlines.h&quot;
</span><span class="cx"> #include &quot;SlotVisitorInlines.h&quot;
</span><span class="cx"> #include &quot;StructureInlines.h&quot;
</span><span class="lines">@@ -37,7 +38,8 @@
</span><span class="cx"> 
</span><span class="cx"> void JSMap::destroy(JSCell* cell)
</span><span class="cx"> {
</span><del>-    jsCast&lt;JSMap*&gt;(cell)-&gt;~JSMap();
</del><ins>+    JSMap* thisObject = jsCast&lt;JSMap*&gt;(cell);
+    thisObject-&gt;JSMap::~JSMap();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void JSMap::visitChildren(JSCell* cell, SlotVisitor&amp; visitor)
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeJSMaph"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/JSMap.h (181921 => 181922)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/JSMap.h        2015-03-24 23:54:04 UTC (rev 181921)
+++ trunk/Source/JavaScriptCore/runtime/JSMap.h        2015-03-25 00:19:05 UTC (rev 181922)
</span><span class="lines">@@ -32,10 +32,14 @@
</span><span class="cx"> 
</span><span class="cx"> namespace JSC {
</span><span class="cx"> 
</span><ins>+class JSMapIterator;
+
</ins><span class="cx"> class JSMap : public JSDestructibleObject {
</span><span class="cx"> public:
</span><span class="cx">     typedef JSDestructibleObject Base;
</span><span class="cx"> 
</span><ins>+    friend class JSMapIterator;
+
</ins><span class="cx">     // Our marking functions expect Entry to maintain this layout, and have all
</span><span class="cx">     // fields be WriteBarrier&lt;Unknown&gt;
</span><span class="cx">     class Entry {
</span><span class="lines">@@ -82,7 +86,7 @@
</span><span class="cx">         }
</span><span class="cx">     };
</span><span class="cx"> 
</span><del>-    typedef MapDataImpl&lt;Entry&gt; MapData;
</del><ins>+    typedef MapDataImpl&lt;Entry, JSMapIterator&gt; MapData;
</ins><span class="cx"> 
</span><span class="cx">     DECLARE_EXPORT_INFO;
</span><span class="cx"> 
</span><span class="lines">@@ -103,16 +107,6 @@
</span><span class="cx">         return create(exec-&gt;vm(), structure);
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    typedef MapData::const_iterator const_iterator;
-
-    const_iterator begin() const
-    {
-        return m_mapData.begin();
-    }
-    const_iterator end() const
-    {
-        return m_mapData.end();
-    }
</del><span class="cx">     bool has(ExecState*, JSValue);
</span><span class="cx">     size_t size(ExecState*);
</span><span class="cx">     JSValue get(ExecState*, JSValue);
</span><span class="lines">@@ -123,7 +117,7 @@
</span><span class="cx"> private:
</span><span class="cx">     JSMap(VM&amp; vm, Structure* structure)
</span><span class="cx">         : Base(vm, structure)
</span><del>-        , m_mapData()
</del><ins>+        , m_mapData(vm)
</ins><span class="cx">     {
</span><span class="cx">     }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeJSMapIteratorcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/JSMapIterator.cpp (181921 => 181922)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/JSMapIterator.cpp        2015-03-24 23:54:04 UTC (rev 181921)
+++ trunk/Source/JavaScriptCore/runtime/JSMapIterator.cpp        2015-03-25 00:19:05 UTC (rev 181922)
</span><span class="lines">@@ -29,6 +29,7 @@
</span><span class="cx"> #include &quot;JSCJSValueInlines.h&quot;
</span><span class="cx"> #include &quot;JSCellInlines.h&quot;
</span><span class="cx"> #include &quot;JSMap.h&quot;
</span><ins>+#include &quot;MapDataInlines.h&quot;
</ins><span class="cx"> #include &quot;SlotVisitorInlines.h&quot;
</span><span class="cx"> #include &quot;StructureInlines.h&quot;
</span><span class="cx"> 
</span><span class="lines">@@ -44,7 +45,8 @@
</span><span class="cx"> 
</span><span class="cx"> void JSMapIterator::destroy(JSCell* cell)
</span><span class="cx"> {
</span><del>-    jsCast&lt;JSMapIterator*&gt;(cell)-&gt;~JSMapIterator();
</del><ins>+    JSMapIterator* thisObject = jsCast&lt;JSMapIterator*&gt;(cell);
+    thisObject-&gt;JSMapIterator::~JSMapIterator();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void JSMapIterator::visitChildren(JSCell* cell, SlotVisitor&amp; visitor)
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeJSMapIteratorh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/JSMapIterator.h (181921 => 181922)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/JSMapIterator.h        2015-03-24 23:54:04 UTC (rev 181921)
+++ trunk/Source/JavaScriptCore/runtime/JSMapIterator.h        2015-03-25 00:19:05 UTC (rev 181922)
</span><span class="lines">@@ -57,19 +57,30 @@
</span><span class="cx"> 
</span><span class="cx">     bool next(CallFrame* callFrame, JSValue&amp; value)
</span><span class="cx">     {
</span><del>-        if (!m_iterator.ensureSlot())
</del><ins>+        WTF::KeyValuePair&lt;JSValue, JSValue&gt; pair;
+        if (!m_iterator.next(pair))
</ins><span class="cx">             return false;
</span><span class="cx"> 
</span><span class="cx">         if (m_kind == MapIterateValue)
</span><del>-            value = m_iterator.value();
</del><ins>+            value = pair.value;
</ins><span class="cx">         else if (m_kind == MapIterateKey)
</span><del>-            value = m_iterator.key();
</del><ins>+            value = pair.key;
</ins><span class="cx">         else
</span><del>-            value = createPair(callFrame, m_iterator.key(), m_iterator.value());
-        ++m_iterator;
</del><ins>+            value = createPair(callFrame, pair.key, pair.value);
</ins><span class="cx">         return true;
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    bool nextKeyValue(JSValue&amp; key, JSValue&amp; value)
+    {
+        WTF::KeyValuePair&lt;JSValue, JSValue&gt; pair;
+        if (!m_iterator.next(pair))
+            return false;
+
+        key = pair.key;
+        value = pair.value;
+        return true;
+    }
+
</ins><span class="cx">     void finish()
</span><span class="cx">     {
</span><span class="cx">         m_iterator.finish();
</span><span class="lines">@@ -79,21 +90,26 @@
</span><span class="cx">     JSValue iteratedValue() const { return m_map.get(); }
</span><span class="cx">     JSMapIterator* clone(ExecState*);
</span><span class="cx"> 
</span><ins>+    JSMap::MapData::IteratorData* iteratorData()
+    {
+        return &amp;m_iterator;
+    }
+
</ins><span class="cx"> private:
</span><span class="cx">     JSMapIterator(VM&amp; vm, Structure* structure, JSMap* iteratedObject, MapIterationKind kind)
</span><span class="cx">         : Base(vm, structure)
</span><del>-        , m_iterator(iteratedObject-&gt;begin())
</del><ins>+        , m_iterator(iteratedObject-&gt;m_mapData.createIteratorData(this))
</ins><span class="cx">         , m_kind(kind)
</span><span class="cx">     {
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     static void destroy(JSCell*);
</span><del>-    void finishCreation(VM&amp;, JSMap*);
</del><ins>+    JS_EXPORT_PRIVATE void finishCreation(VM&amp;, JSMap*);
</ins><span class="cx">     JSValue createPair(CallFrame*, JSValue, JSValue);
</span><span class="cx">     static void visitChildren(JSCell*, SlotVisitor&amp;);
</span><span class="cx"> 
</span><span class="cx">     WriteBarrier&lt;JSMap&gt; m_map;
</span><del>-    JSMap::const_iterator m_iterator;
</del><ins>+    JSMap::MapData::IteratorData m_iterator;
</ins><span class="cx">     MapIterationKind m_kind;
</span><span class="cx"> };
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeJSSetcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/JSSet.cpp (181921 => 181922)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/JSSet.cpp        2015-03-24 23:54:04 UTC (rev 181921)
+++ trunk/Source/JavaScriptCore/runtime/JSSet.cpp        2015-03-25 00:19:05 UTC (rev 181922)
</span><span class="lines">@@ -27,6 +27,7 @@
</span><span class="cx"> #include &quot;JSSet.h&quot;
</span><span class="cx"> 
</span><span class="cx"> #include &quot;JSCJSValueInlines.h&quot;
</span><ins>+#include &quot;JSSetIterator.h&quot;
</ins><span class="cx"> #include &quot;MapDataInlines.h&quot;
</span><span class="cx"> #include &quot;SlotVisitorInlines.h&quot;
</span><span class="cx"> #include &quot;StructureInlines.h&quot;
</span><span class="lines">@@ -37,7 +38,8 @@
</span><span class="cx"> 
</span><span class="cx"> void JSSet::destroy(JSCell* cell)
</span><span class="cx"> {
</span><del>-    jsCast&lt;JSSet*&gt;(cell)-&gt;~JSSet();
</del><ins>+    JSSet* thisObject = jsCast&lt;JSSet*&gt;(cell);
+    thisObject-&gt;JSSet::~JSSet();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void JSSet::visitChildren(JSCell* cell, SlotVisitor&amp; visitor)
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeJSSeth"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/JSSet.h (181921 => 181922)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/JSSet.h        2015-03-24 23:54:04 UTC (rev 181921)
+++ trunk/Source/JavaScriptCore/runtime/JSSet.h        2015-03-25 00:19:05 UTC (rev 181922)
</span><span class="lines">@@ -32,10 +32,14 @@
</span><span class="cx"> 
</span><span class="cx"> namespace JSC {
</span><span class="cx"> 
</span><ins>+class JSSetIterator;
+
</ins><span class="cx"> class JSSet : public JSDestructibleObject {
</span><span class="cx"> public:
</span><span class="cx">     typedef JSDestructibleObject Base;
</span><span class="cx"> 
</span><ins>+    friend class JSSetIterator;
+
</ins><span class="cx">     // Our marking functions expect Entry to maintain this layout, and have all
</span><span class="cx">     // fields be WriteBarrier&lt;Unknown&gt;
</span><span class="cx">     class Entry {
</span><span class="lines">@@ -78,7 +82,7 @@
</span><span class="cx">         }
</span><span class="cx">     };
</span><span class="cx"> 
</span><del>-    typedef MapDataImpl&lt;Entry&gt; SetData;
</del><ins>+    typedef MapDataImpl&lt;Entry, JSSetIterator&gt; SetData;
</ins><span class="cx"> 
</span><span class="cx">     DECLARE_EXPORT_INFO;
</span><span class="cx"> 
</span><span class="lines">@@ -99,16 +103,6 @@
</span><span class="cx">         return create(exec-&gt;vm(), structure);
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    typedef SetData::const_iterator const_iterator;
-
-    const_iterator begin() const
-    {
-        return m_setData.begin();
-    }
-    const_iterator end() const
-    {
-        return m_setData.end();
-    }
</del><span class="cx">     bool has(ExecState*, JSValue);
</span><span class="cx">     size_t size(ExecState*);
</span><span class="cx">     JS_EXPORT_PRIVATE void add(ExecState*, JSValue);
</span><span class="lines">@@ -118,7 +112,7 @@
</span><span class="cx"> private:
</span><span class="cx">     JSSet(VM&amp; vm, Structure* structure)
</span><span class="cx">         : Base(vm, structure)
</span><del>-        , m_setData()
</del><ins>+        , m_setData(vm)
</ins><span class="cx">     {
</span><span class="cx">     }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeJSSetIteratorcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/JSSetIterator.cpp (181921 => 181922)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/JSSetIterator.cpp        2015-03-24 23:54:04 UTC (rev 181921)
+++ trunk/Source/JavaScriptCore/runtime/JSSetIterator.cpp        2015-03-25 00:19:05 UTC (rev 181922)
</span><span class="lines">@@ -29,6 +29,7 @@
</span><span class="cx"> #include &quot;JSCJSValueInlines.h&quot;
</span><span class="cx"> #include &quot;JSCellInlines.h&quot;
</span><span class="cx"> #include &quot;JSSet.h&quot;
</span><ins>+#include &quot;MapDataInlines.h&quot;
</ins><span class="cx"> #include &quot;SlotVisitorInlines.h&quot;
</span><span class="cx"> #include &quot;StructureInlines.h&quot;
</span><span class="cx"> 
</span><span class="lines">@@ -52,7 +53,8 @@
</span><span class="cx"> 
</span><span class="cx"> void JSSetIterator::destroy(JSCell* cell)
</span><span class="cx"> {
</span><del>-    jsCast&lt;JSSetIterator*&gt;(cell)-&gt;~JSSetIterator();
</del><ins>+    JSSetIterator* thisObject = jsCast&lt;JSSetIterator*&gt;(cell);
+    thisObject-&gt;JSSetIterator::~JSSetIterator();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> JSValue JSSetIterator::createPair(CallFrame* callFrame, JSValue key, JSValue value)
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeJSSetIteratorh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/JSSetIterator.h (181921 => 181922)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/JSSetIterator.h        2015-03-24 23:54:04 UTC (rev 181921)
+++ trunk/Source/JavaScriptCore/runtime/JSSetIterator.h        2015-03-25 00:19:05 UTC (rev 181922)
</span><span class="lines">@@ -58,13 +58,13 @@
</span><span class="cx"> 
</span><span class="cx">     bool next(CallFrame* callFrame, JSValue&amp; value)
</span><span class="cx">     {
</span><del>-        if (!m_iterator.ensureSlot())
</del><ins>+        WTF::KeyValuePair&lt;JSValue, JSValue&gt; pair;
+        if (!m_iterator.next(pair))
</ins><span class="cx">             return false;
</span><span class="cx">         if (m_kind == SetIterateValue || m_kind == SetIterateKey)
</span><del>-            value = m_iterator.key();
</del><ins>+            value = pair.key;
</ins><span class="cx">         else
</span><del>-            value = createPair(callFrame, m_iterator.key(), m_iterator.key());
-        ++m_iterator;
</del><ins>+            value = createPair(callFrame, pair.key, pair.key);
</ins><span class="cx">         return true;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -77,21 +77,26 @@
</span><span class="cx">     JSValue iteratedValue() const { return m_set.get(); }
</span><span class="cx">     JSSetIterator* clone(ExecState*);
</span><span class="cx"> 
</span><ins>+    JSSet::SetData::IteratorData* iteratorData()
+    {
+        return &amp;m_iterator;
+    }
+
</ins><span class="cx"> private:
</span><span class="cx">     JSSetIterator(VM&amp; vm, Structure* structure, JSSet* iteratedObject, SetIterationKind kind)
</span><span class="cx">         : Base(vm, structure)
</span><del>-        , m_iterator(iteratedObject-&gt;begin())
</del><ins>+        , m_iterator(iteratedObject-&gt;m_setData.createIteratorData(this))
</ins><span class="cx">         , m_kind(kind)
</span><span class="cx">     {
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     static void destroy(JSCell*);
</span><del>-    void finishCreation(VM&amp;, JSSet*);
-    JSValue createPair(CallFrame*, JSValue, JSValue);
</del><ins>+    JS_EXPORT_PRIVATE void finishCreation(VM&amp;, JSSet*);
+    JS_EXPORT_PRIVATE JSValue createPair(CallFrame*, JSValue, JSValue);
</ins><span class="cx">     static void visitChildren(JSCell*, SlotVisitor&amp;);
</span><span class="cx"> 
</span><span class="cx">     WriteBarrier&lt;JSSet&gt; m_set;
</span><del>-    JSSet::const_iterator m_iterator;
</del><ins>+    JSSet::SetData::IteratorData m_iterator;
</ins><span class="cx">     SetIterationKind m_kind;
</span><span class="cx"> };
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeMapDatah"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/MapData.h (181921 => 181922)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/MapData.h        2015-03-24 23:54:04 UTC (rev 181921)
+++ trunk/Source/JavaScriptCore/runtime/MapData.h        2015-03-25 00:19:05 UTC (rev 181922)
</span><span class="lines">@@ -27,45 +27,57 @@
</span><span class="cx"> #define MapData_h
</span><span class="cx"> 
</span><span class="cx"> #include &quot;JSCell.h&quot;
</span><ins>+#include &quot;WeakGCMapInlines.h&quot;
</ins><span class="cx"> #include &lt;wtf/HashFunctions.h&gt;
</span><span class="cx"> #include &lt;wtf/HashMap.h&gt;
</span><span class="cx"> #include &lt;wtf/MathExtras.h&gt;
</span><ins>+#include &lt;wtf/RefCounted.h&gt;
+#include &lt;wtf/RefPtr.h&gt;
+#include &lt;wtf/Vector.h&gt;
</ins><span class="cx"> 
</span><span class="cx"> namespace JSC {
</span><span class="cx"> 
</span><span class="cx"> class ExecState;
</span><ins>+class VM;
</ins><span class="cx"> 
</span><del>-template&lt;typename Entry&gt;
</del><ins>+template&lt;typename Entry, typename JSIterator&gt;
</ins><span class="cx"> class MapDataImpl {
</span><span class="cx"> public:
</span><span class="cx">     enum : int32_t {
</span><span class="cx">         minimumMapSize = 8
</span><span class="cx">     };
</span><span class="cx"> 
</span><del>-    struct const_iterator {
-        const_iterator(const MapDataImpl*);
-        ~const_iterator();
-        const WTF::KeyValuePair&lt;JSValue, JSValue&gt; operator*() const;
-        JSValue key() const { RELEASE_ASSERT(!atEnd()); return m_mapData-&gt;m_entries[m_index].key().get(); }
-        JSValue value() const { RELEASE_ASSERT(!atEnd()); return m_mapData-&gt;m_entries[m_index].value().get(); }
-        void operator++() { ASSERT(!atEnd()); internalIncrement(); }
-        static const_iterator end(const MapDataImpl*);
-        bool operator!=(const const_iterator&amp; other);
-        bool operator==(const const_iterator&amp; other);
-        void finish() { m_index = std::numeric_limits&lt;int32_t&gt;::max(); }
</del><ins>+    class IteratorData {
+    public:
+        friend class MapDataImpl;
</ins><span class="cx"> 
</span><del>-        bool ensureSlot();
</del><ins>+        IteratorData(const MapDataImpl*);
+        bool next(WTF::KeyValuePair&lt;JSValue, JSValue&gt;&amp;);
</ins><span class="cx"> 
</span><ins>+        void didRemoveEntry(int32_t index)
+        {
+            if (m_index &lt;= index)
+                return;
+            --m_index;
+        }
+
+        void didRemoveAllEntries()
+        {
+            m_index = 0;
+        }
+
+        void finish()
+        {
+            m_index = -1;
+        }
+
</ins><span class="cx">     private:
</span><del>-        // This is a bit gnarly. We use an index of -1 to indicate the
-        // &quot;end()&quot; iterator. By casting to unsigned we can immediately
-        // test if both iterators are at the end of their iteration.
-        // We need this in order to keep the common case (eg. iter != end())
-        // fast.
-        bool atEnd() const { return static_cast&lt;size_t&gt;(m_index) &gt;= static_cast&lt;size_t&gt;(m_mapData-&gt;m_size); }
-        void internalIncrement();
</del><ins>+        bool ensureSlot() const;
+        bool isFinished() const { return m_index == -1; }
+        int32_t refreshCursor() const;
+
</ins><span class="cx">         const MapDataImpl* m_mapData;
</span><del>-        int32_t m_index;
</del><ins>+        mutable int32_t m_index;
</ins><span class="cx">     };
</span><span class="cx"> 
</span><span class="cx">     struct KeyType {
</span><span class="lines">@@ -74,7 +86,7 @@
</span><span class="cx">         JSValue value;
</span><span class="cx">     };
</span><span class="cx"> 
</span><del>-    MapDataImpl();
</del><ins>+    MapDataImpl(VM&amp;);
</ins><span class="cx"> 
</span><span class="cx">     void set(ExecState*, JSCell* owner, KeyType, JSValue);
</span><span class="cx">     JSValue get(ExecState*, KeyType);
</span><span class="lines">@@ -82,8 +94,7 @@
</span><span class="cx">     bool contains(ExecState*, KeyType);
</span><span class="cx">     size_t size(ExecState*) const { return m_size - m_deletedCount; }
</span><span class="cx"> 
</span><del>-    const_iterator begin() const { return const_iterator(this); }
-    const_iterator end() const { return const_iterator::end(this); }
</del><ins>+    IteratorData createIteratorData(JSIterator*);
</ins><span class="cx"> 
</span><span class="cx">     void clear();
</span><span class="cx"> 
</span><span class="lines">@@ -104,7 +115,7 @@
</span><span class="cx">     ALWAYS_INLINE Entry* add(ExecState*, JSCell* owner, KeyType);
</span><span class="cx">     template &lt;typename Map, typename Key&gt; ALWAYS_INLINE Entry* add(ExecState*, JSCell* owner, Map&amp;, Key, KeyType);
</span><span class="cx"> 
</span><del>-    ALWAYS_INLINE bool shouldPack() const { return m_deletedCount &amp;&amp; !m_iteratorCount; }
</del><ins>+    ALWAYS_INLINE bool shouldPack() const { return m_deletedCount; }
</ins><span class="cx">     CheckedBoolean ensureSpaceForAppend(ExecState*, JSCell* owner);
</span><span class="cx"> 
</span><span class="cx">     ALWAYS_INLINE void replaceAndPackBackingStore(Entry* destination, int32_t newSize);
</span><span class="lines">@@ -117,36 +128,23 @@
</span><span class="cx">     int32_t m_capacity;
</span><span class="cx">     int32_t m_size;
</span><span class="cx">     int32_t m_deletedCount;
</span><del>-    mutable int32_t m_iteratorCount;
</del><span class="cx">     Entry* m_entries;
</span><ins>+    WeakGCMap&lt;JSIterator*, JSIterator&gt; m_iterators;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><del>-template&lt;typename Entry&gt;
-ALWAYS_INLINE MapDataImpl&lt;Entry&gt;::MapDataImpl()
</del><ins>+template&lt;typename Entry, typename JSIterator&gt;
+ALWAYS_INLINE MapDataImpl&lt;Entry, JSIterator&gt;::MapDataImpl(VM&amp; vm)
</ins><span class="cx">     : m_capacity(0)
</span><span class="cx">     , m_size(0)
</span><span class="cx">     , m_deletedCount(0)
</span><del>-    , m_iteratorCount(0)
</del><span class="cx">     , m_entries(nullptr)
</span><ins>+    , m_iterators(vm)
</ins><span class="cx"> {
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-template&lt;typename Entry&gt;
-ALWAYS_INLINE void MapDataImpl&lt;Entry&gt;::clear()
</del><ins>+template&lt;typename Entry, typename JSIterator&gt;
+ALWAYS_INLINE MapDataImpl&lt;Entry, JSIterator&gt;::KeyType::KeyType(JSValue v)
</ins><span class="cx"> {
</span><del>-    m_cellKeyedTable.clear();
-    m_valueKeyedTable.clear();
-    m_stringKeyedTable.clear();
-    m_symbolKeyedTable.clear();
-    m_capacity = 0;
-    m_size = 0;
-    m_deletedCount = 0;
-    m_entries = nullptr;
-}
-
-template&lt;typename Entry&gt;
-ALWAYS_INLINE MapDataImpl&lt;Entry&gt;::KeyType::KeyType(JSValue v)
-{
</del><span class="cx">     if (!v.isDouble()) {
</span><span class="cx">         value = v;
</span><span class="cx">         return;
</span><span class="lines">@@ -164,73 +162,45 @@
</span><span class="cx">         value = jsNumber(i);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-template&lt;typename Entry&gt;
-ALWAYS_INLINE void MapDataImpl&lt;Entry&gt;::const_iterator::internalIncrement()
-{
-    Entry* entries = m_mapData-&gt;m_entries;
-    size_t index = m_index + 1;
-    size_t end = m_mapData-&gt;m_size;
-    while (index &lt; end &amp;&amp; !entries[index].key())
-        index++;
-    m_index = index;
-}
-
-template&lt;typename Entry&gt;
-ALWAYS_INLINE bool MapDataImpl&lt;Entry&gt;::const_iterator::ensureSlot()
-{
-    // When an iterator exists outside of host cost it is possible for
-    // the containing map to be modified
-    Entry* entries = m_mapData-&gt;m_entries;
-    size_t index = m_index;
-    size_t end = m_mapData-&gt;m_size;
-    if (index &lt; end &amp;&amp; entries[index].key())
-        return true;
-    internalIncrement();
-    return static_cast&lt;size_t&gt;(m_index) &lt; end;
-}
-
-template&lt;typename Entry&gt;
-ALWAYS_INLINE MapDataImpl&lt;Entry&gt;::const_iterator::const_iterator(const MapDataImpl&lt;Entry&gt;* mapData)
</del><ins>+template&lt;typename Entry, typename JSIterator&gt;
+ALWAYS_INLINE MapDataImpl&lt;Entry, JSIterator&gt;::IteratorData::IteratorData(const MapDataImpl&lt;Entry, JSIterator&gt;* mapData)
</ins><span class="cx">     : m_mapData(mapData)
</span><del>-    , m_index(-1)
</del><ins>+    , m_index(0)
</ins><span class="cx"> {
</span><del>-    internalIncrement();
</del><span class="cx"> }
</span><span class="cx"> 
</span><del>-template&lt;typename Entry&gt;
-ALWAYS_INLINE MapDataImpl&lt;Entry&gt;::const_iterator::~const_iterator()
</del><ins>+template&lt;typename Entry, typename JSIterator&gt;
+ALWAYS_INLINE bool MapDataImpl&lt;Entry, JSIterator&gt;::IteratorData::next(WTF::KeyValuePair&lt;JSValue, JSValue&gt;&amp; pair)
</ins><span class="cx"> {
</span><del>-    m_mapData-&gt;m_iteratorCount--;
-}
-
-template&lt;typename Entry&gt;
-ALWAYS_INLINE const WTF::KeyValuePair&lt;JSValue, JSValue&gt; MapDataImpl&lt;Entry&gt;::const_iterator::operator*() const
-{
</del><ins>+    if (!ensureSlot())
+        return false;
</ins><span class="cx">     Entry* entry = &amp;m_mapData-&gt;m_entries[m_index];
</span><del>-    return WTF::KeyValuePair&lt;JSValue, JSValue&gt;(entry-&gt;key().get(), entry-&gt;value().get());
</del><ins>+    pair = WTF::KeyValuePair&lt;JSValue, JSValue&gt;(entry-&gt;key().get(), entry-&gt;value().get());
+    m_index += 1;
+    return true;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-template&lt;typename Entry&gt;
-ALWAYS_INLINE auto MapDataImpl&lt;Entry&gt;::const_iterator::end(const MapDataImpl&lt;Entry&gt;* mapData) -&gt; const_iterator
</del><ins>+// This is a bit gnarly. We use an index of -1 to indicate the
+// finished state. By casting to unsigned we can immediately
+// test if both iterators are at the end of their iteration.
+template&lt;typename Entry, typename JSIterator&gt;
+ALWAYS_INLINE bool MapDataImpl&lt;Entry, JSIterator&gt;::IteratorData::ensureSlot() const
</ins><span class="cx"> {
</span><del>-    const_iterator result(mapData);
-    result.m_index = -1;
-    return result;
</del><ins>+    int32_t index = refreshCursor();
+    return static_cast&lt;size_t&gt;(index) &lt; static_cast&lt;size_t&gt;(m_mapData-&gt;m_size);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-template&lt;typename Entry&gt;
-ALWAYS_INLINE bool MapDataImpl&lt;Entry&gt;::const_iterator::operator!=(const const_iterator&amp; other)
</del><ins>+template&lt;typename Entry, typename JSIterator&gt;
+ALWAYS_INLINE int32_t MapDataImpl&lt;Entry, JSIterator&gt;::IteratorData::refreshCursor() const
</ins><span class="cx"> {
</span><del>-    ASSERT(other.m_mapData == m_mapData);
-    if (atEnd() &amp;&amp; other.atEnd())
-        return false;
-    return m_index != other.m_index;
-}
</del><ins>+    if (isFinished())
+        return m_index;
</ins><span class="cx"> 
</span><del>-template&lt;typename Entry&gt;
-ALWAYS_INLINE bool MapDataImpl&lt;Entry&gt;::const_iterator::operator==(const const_iterator&amp; other)
-{
-    return !(*this != other);
</del><ins>+    Entry* entries = m_mapData-&gt;m_entries;
+    size_t end = m_mapData-&gt;m_size;
+    while (static_cast&lt;size_t&gt;(m_index) &lt; end &amp;&amp; !entries[m_index].key())
+        m_index++;
+    return m_index;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeMapDataInlinesh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/MapDataInlines.h (181921 => 181922)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/MapDataInlines.h        2015-03-24 23:54:04 UTC (rev 181921)
+++ trunk/Source/JavaScriptCore/runtime/MapDataInlines.h        2015-03-25 00:19:05 UTC (rev 181922)
</span><span class="lines">@@ -37,9 +37,25 @@
</span><span class="cx"> 
</span><span class="cx"> namespace JSC {
</span><span class="cx"> 
</span><del>-template&lt;typename Entry&gt;
-inline Entry* MapDataImpl&lt;Entry&gt;::find(ExecState* exec, KeyType key)
</del><ins>+template&lt;typename Entry, typename JSIterator&gt;
+inline void MapDataImpl&lt;Entry, JSIterator&gt;::clear()
</ins><span class="cx"> {
</span><ins>+    m_cellKeyedTable.clear();
+    m_valueKeyedTable.clear();
+    m_stringKeyedTable.clear();
+    m_symbolKeyedTable.clear();
+    m_capacity = 0;
+    m_size = 0;
+    m_deletedCount = 0;
+    m_entries = nullptr;
+    m_iterators.forEach([](JSIterator* iterator, JSIterator*) {
+        iterator-&gt;iteratorData()-&gt;didRemoveAllEntries();
+    });
+}
+
+template&lt;typename Entry, typename JSIterator&gt;
+inline Entry* MapDataImpl&lt;Entry, JSIterator&gt;::find(ExecState* exec, KeyType key)
+{
</ins><span class="cx">     if (key.value.isString()) {
</span><span class="cx">         auto iter = m_stringKeyedTable.find(asString(key.value)-&gt;value(exec).impl());
</span><span class="cx">         if (iter == m_stringKeyedTable.end())
</span><span class="lines">@@ -65,15 +81,15 @@
</span><span class="cx">     return &amp;m_entries[iter-&gt;value];
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-template&lt;typename Entry&gt;
-inline bool MapDataImpl&lt;Entry&gt;::contains(ExecState* exec, KeyType key)
</del><ins>+template&lt;typename Entry, typename JSIterator&gt;
+inline bool MapDataImpl&lt;Entry, JSIterator&gt;::contains(ExecState* exec, KeyType key)
</ins><span class="cx"> {
</span><span class="cx">     return find(exec, key);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-template&lt;typename Entry&gt;
</del><ins>+template&lt;typename Entry, typename JSIterator&gt;
</ins><span class="cx"> template &lt;typename Map, typename Key&gt;
</span><del>-inline Entry* MapDataImpl&lt;Entry&gt;::add(ExecState* exec, JSCell* owner, Map&amp; map, Key key, KeyType keyValue)
</del><ins>+inline Entry* MapDataImpl&lt;Entry, JSIterator&gt;::add(ExecState* exec, JSCell* owner, Map&amp; map, Key key, KeyType keyValue)
</ins><span class="cx"> {
</span><span class="cx">     typename Map::iterator location = map.find(key);
</span><span class="cx">     if (location != map.end())
</span><span class="lines">@@ -90,8 +106,8 @@
</span><span class="cx">     return entry;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-template&lt;typename Entry&gt;
-inline void MapDataImpl&lt;Entry&gt;::set(ExecState* exec, JSCell* owner, KeyType key, JSValue value)
</del><ins>+template&lt;typename Entry, typename JSIterator&gt;
+inline void MapDataImpl&lt;Entry, JSIterator&gt;::set(ExecState* exec, JSCell* owner, KeyType key, JSValue value)
</ins><span class="cx"> {
</span><span class="cx">     Entry* location = add(exec, owner, key);
</span><span class="cx">     if (!location)
</span><span class="lines">@@ -99,8 +115,8 @@
</span><span class="cx">     location-&gt;setValue(exec-&gt;vm(), owner, value);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-template&lt;typename Entry&gt;
-inline Entry* MapDataImpl&lt;Entry&gt;::add(ExecState* exec, JSCell* owner, KeyType key)
</del><ins>+template&lt;typename Entry, typename JSIterator&gt;
+inline Entry* MapDataImpl&lt;Entry, JSIterator&gt;::add(ExecState* exec, JSCell* owner, KeyType key)
</ins><span class="cx"> {
</span><span class="cx">     if (key.value.isString())
</span><span class="cx">         return add(exec, owner, m_stringKeyedTable, asString(key.value)-&gt;value(exec).impl(), key);
</span><span class="lines">@@ -111,16 +127,16 @@
</span><span class="cx">     return add(exec, owner, m_valueKeyedTable, JSValue::encode(key.value), key);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-template&lt;typename Entry&gt;
-inline JSValue MapDataImpl&lt;Entry&gt;::get(ExecState* exec, KeyType key)
</del><ins>+template&lt;typename Entry, typename JSIterator&gt;
+inline JSValue MapDataImpl&lt;Entry, JSIterator&gt;::get(ExecState* exec, KeyType key)
</ins><span class="cx"> {
</span><span class="cx">     if (Entry* entry = find(exec, key))
</span><span class="cx">         return entry-&gt;value().get();
</span><span class="cx">     return JSValue();
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-template&lt;typename Entry&gt;
-inline bool MapDataImpl&lt;Entry&gt;::remove(ExecState* exec, KeyType key)
</del><ins>+template&lt;typename Entry, typename JSIterator&gt;
+inline bool MapDataImpl&lt;Entry, JSIterator&gt;::remove(ExecState* exec, KeyType key)
</ins><span class="cx"> {
</span><span class="cx">     int32_t location;
</span><span class="cx">     if (key.value.isString()) {
</span><span class="lines">@@ -153,16 +169,20 @@
</span><span class="cx">     return true;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-template&lt;typename Entry&gt;
-inline void MapDataImpl&lt;Entry&gt;::replaceAndPackBackingStore(Entry* destination, int32_t newCapacity)
</del><ins>+template&lt;typename Entry, typename JSIterator&gt;
+inline void MapDataImpl&lt;Entry, JSIterator&gt;::replaceAndPackBackingStore(Entry* destination, int32_t newCapacity)
</ins><span class="cx"> {
</span><span class="cx">     ASSERT(shouldPack());
</span><span class="cx">     int32_t newEnd = 0;
</span><span class="cx">     RELEASE_ASSERT(newCapacity &gt; 0);
</span><span class="cx">     for (int32_t i = 0; i &lt; m_size; i++) {
</span><span class="cx">         Entry&amp; entry = m_entries[i];
</span><del>-        if (!entry.key())
</del><ins>+        if (!entry.key()) {
+            m_iterators.forEach([i](JSIterator* iterator, JSIterator*) {
+                iterator-&gt;iteratorData()-&gt;didRemoveEntry(i);
+            });
</ins><span class="cx">             continue;
</span><ins>+        }
</ins><span class="cx">         ASSERT(newEnd &lt; newCapacity);
</span><span class="cx">         destination[newEnd] = entry;
</span><span class="cx"> 
</span><span class="lines">@@ -191,8 +211,8 @@
</span><span class="cx">     m_entries = destination;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-template&lt;typename Entry&gt;
-inline void MapDataImpl&lt;Entry&gt;::replaceBackingStore(Entry* destination, int32_t newCapacity)
</del><ins>+template&lt;typename Entry, typename JSIterator&gt;
+inline void MapDataImpl&lt;Entry, JSIterator&gt;::replaceBackingStore(Entry* destination, int32_t newCapacity)
</ins><span class="cx"> {
</span><span class="cx">     ASSERT(!shouldPack());
</span><span class="cx">     RELEASE_ASSERT(newCapacity &gt; 0);
</span><span class="lines">@@ -202,8 +222,8 @@
</span><span class="cx">     m_entries = destination;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-template&lt;typename Entry&gt;
-inline CheckedBoolean MapDataImpl&lt;Entry&gt;::ensureSpaceForAppend(ExecState* exec, JSCell* owner)
</del><ins>+template&lt;typename Entry, typename JSIterator&gt;
+inline CheckedBoolean MapDataImpl&lt;Entry, JSIterator&gt;::ensureSpaceForAppend(ExecState* exec, JSCell* owner)
</ins><span class="cx"> {
</span><span class="cx">     if (m_capacity &gt; m_size)
</span><span class="cx">         return true;
</span><span class="lines">@@ -224,8 +244,8 @@
</span><span class="cx">     return true;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-template&lt;typename Entry&gt;
-inline void MapDataImpl&lt;Entry&gt;::visitChildren(JSCell* owner, SlotVisitor&amp; visitor)
</del><ins>+template&lt;typename Entry, typename JSIterator&gt;
+inline void MapDataImpl&lt;Entry, JSIterator&gt;::visitChildren(JSCell* owner, SlotVisitor&amp; visitor)
</ins><span class="cx"> {
</span><span class="cx">     Entry* entries = m_entries;
</span><span class="cx">     if (!entries)
</span><span class="lines">@@ -244,8 +264,8 @@
</span><span class="cx">     visitor.copyLater(owner, MapBackingStoreCopyToken, entries, capacityInBytes());
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-template&lt;typename Entry&gt;
-inline void MapDataImpl&lt;Entry&gt;::copyBackingStore(CopyVisitor&amp; visitor, CopyToken token)
</del><ins>+template&lt;typename Entry, typename JSIterator&gt;
+inline void MapDataImpl&lt;Entry, JSIterator&gt;::copyBackingStore(CopyVisitor&amp; visitor, CopyToken token)
</ins><span class="cx"> {
</span><span class="cx">     if (token == MapBackingStoreCopyToken &amp;&amp; visitor.checkIfShouldCopy(m_entries)) {
</span><span class="cx">         Entry* oldEntries = m_entries;
</span><span class="lines">@@ -258,4 +278,11 @@
</span><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+template&lt;typename Entry, typename JSIterator&gt;
+inline auto MapDataImpl&lt;Entry, JSIterator&gt;::createIteratorData(JSIterator* iterator) -&gt; IteratorData
+{
+    m_iterators.set(iterator, iterator);
+    return IteratorData(this);
</ins><span class="cx"> }
</span><ins>+
+}
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeMapPrototypecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/MapPrototype.cpp (181921 => 181922)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/MapPrototype.cpp        2015-03-24 23:54:04 UTC (rev 181921)
+++ trunk/Source/JavaScriptCore/runtime/MapPrototype.cpp        2015-03-25 00:19:05 UTC (rev 181922)
</span><span class="lines">@@ -119,22 +119,26 @@
</span><span class="cx">         return JSValue::encode(throwTypeError(callFrame, WTF::ASCIILiteral(&quot;Map.prototype.forEach called without callback&quot;)));
</span><span class="cx">     JSValue thisValue = callFrame-&gt;argument(1);
</span><span class="cx">     VM* vm = &amp;callFrame-&gt;vm();
</span><ins>+    JSMapIterator* iterator = JSMapIterator::create(*vm, callFrame-&gt;callee()-&gt;globalObject()-&gt;mapIteratorStructure(), map, MapIterateKeyValue);
+    JSValue key, value;
</ins><span class="cx">     if (callType == CallTypeJS) {
</span><span class="cx">         JSFunction* function = jsCast&lt;JSFunction*&gt;(callBack);
</span><span class="cx">         CachedCall cachedCall(callFrame, function, 2);
</span><del>-        for (auto ptr = map-&gt;begin(), end = map-&gt;end(); ptr != end &amp;&amp; !vm-&gt;exception(); ++ptr) {
</del><ins>+        while (iterator-&gt;nextKeyValue(key, value) &amp;&amp; !vm-&gt;exception()) {
</ins><span class="cx">             cachedCall.setThis(thisValue);
</span><del>-            cachedCall.setArgument(0, ptr.value());
-            cachedCall.setArgument(1, ptr.key());
</del><ins>+            cachedCall.setArgument(0, value);
+            cachedCall.setArgument(1, key);
</ins><span class="cx">             cachedCall.call();
</span><span class="cx">         }
</span><ins>+        iterator-&gt;finish();
</ins><span class="cx">     } else {
</span><del>-        for (auto ptr = map-&gt;begin(), end = map-&gt;end(); ptr != end &amp;&amp; !vm-&gt;exception(); ++ptr) {
</del><ins>+        while (iterator-&gt;nextKeyValue(key, value) &amp;&amp; !vm-&gt;exception()) {
</ins><span class="cx">             MarkedArgumentBuffer args;
</span><del>-            args.append(ptr.value());
-            args.append(ptr.key());
</del><ins>+            args.append(value);
+            args.append(key);
</ins><span class="cx">             JSC::call(callFrame, callBack, callType, callData, thisValue, args);
</span><span class="cx">         }
</span><ins>+        iterator-&gt;finish();
</ins><span class="cx">     }
</span><span class="cx">     return JSValue::encode(jsUndefined());
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeSetPrototypecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/SetPrototype.cpp (181921 => 181922)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/SetPrototype.cpp        2015-03-24 23:54:04 UTC (rev 181921)
+++ trunk/Source/JavaScriptCore/runtime/SetPrototype.cpp        2015-03-25 00:19:05 UTC (rev 181922)
</span><span class="lines">@@ -128,20 +128,24 @@
</span><span class="cx">         return JSValue::encode(throwTypeError(callFrame, WTF::ASCIILiteral(&quot;Set.prototype.forEach called without callback&quot;)));
</span><span class="cx">     JSValue thisValue = callFrame-&gt;argument(1);
</span><span class="cx">     VM* vm = &amp;callFrame-&gt;vm();
</span><ins>+    JSSetIterator* iterator = JSSetIterator::create(*vm, callFrame-&gt;callee()-&gt;globalObject()-&gt;setIteratorStructure(), set, SetIterateKey);
+    JSValue key;
</ins><span class="cx">     if (callType == CallTypeJS) {
</span><span class="cx">         JSFunction* function = jsCast&lt;JSFunction*&gt;(callBack);
</span><span class="cx">         CachedCall cachedCall(callFrame, function, 1);
</span><del>-        for (auto ptr = set-&gt;begin(), end = set-&gt;end(); ptr != end &amp;&amp; !vm-&gt;exception(); ++ptr) {
</del><ins>+        while (iterator-&gt;next(callFrame, key) &amp;&amp; !vm-&gt;exception()) {
</ins><span class="cx">             cachedCall.setThis(thisValue);
</span><del>-            cachedCall.setArgument(0, ptr.key());
</del><ins>+            cachedCall.setArgument(0, key);
</ins><span class="cx">             cachedCall.call();
</span><span class="cx">         }
</span><ins>+        iterator-&gt;finish();
</ins><span class="cx">     } else {
</span><del>-        for (auto ptr = set-&gt;begin(), end = set-&gt;end(); ptr != end &amp;&amp; !vm-&gt;exception(); ++ptr) {
</del><ins>+        while (iterator-&gt;next(callFrame, key) &amp;&amp; !vm-&gt;exception()) {
</ins><span class="cx">             MarkedArgumentBuffer args;
</span><del>-            args.append(ptr.key());
</del><ins>+            args.append(key);
</ins><span class="cx">             JSC::call(callFrame, callBack, callType, callData, thisValue, args);
</span><span class="cx">         }
</span><ins>+        iterator-&gt;finish();
</ins><span class="cx">     }
</span><span class="cx">     return JSValue::encode(jsUndefined());
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeWeakGCMaph"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/WeakGCMap.h (181921 => 181922)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/WeakGCMap.h        2015-03-24 23:54:04 UTC (rev 181921)
+++ trunk/Source/JavaScriptCore/runtime/WeakGCMap.h        2015-03-25 00:19:05 UTC (rev 181922)
</span><span class="lines">@@ -70,6 +70,17 @@
</span><span class="cx">         m_map.clear();
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    bool isEmpty() const
+    {
+        const_iterator it = m_map.begin();
+        const_iterator end = m_map.end();
+        while (it != end) {
+            if (it-&gt;value)
+                return true;
+        }
+        return false;
+    }
+
</ins><span class="cx">     iterator find(const KeyType&amp; key)
</span><span class="cx">     {
</span><span class="cx">         iterator it = m_map.find(key);
</span><span class="lines">@@ -84,6 +95,15 @@
</span><span class="cx">         return const_cast&lt;WeakGCMap*&gt;(this)-&gt;find(key);
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    template&lt;typename Functor&gt;
+    void forEach(Functor functor)
+    {
+        for (auto&amp; pair : m_map) {
+            if (pair.value)
+                functor(pair.key, pair.value.get());
+        }
+    }
+
</ins><span class="cx">     bool contains(const KeyType&amp; key) const
</span><span class="cx">     {
</span><span class="cx">         return find(key) != m_map.end();
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoretestsstressmodifymapduringiterationjs"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/tests/stress/modify-map-during-iteration.js (0 => 181922)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/stress/modify-map-during-iteration.js                                (rev 0)
+++ trunk/Source/JavaScriptCore/tests/stress/modify-map-during-iteration.js        2015-03-25 00:19:05 UTC (rev 181922)
</span><span class="lines">@@ -0,0 +1,76 @@
</span><ins>+
+function testValue(value, expected) {
+    if (value !== expected)
+        throw new Error(&quot;bad value: expected:(&quot; + expected + &quot;),actual:(&quot; + value +&quot;).&quot;);
+}
+
+function identityPairs(array) {
+    return array.map(function (i) { return [i, i]; });
+}
+
+var map = new Map(identityPairs([0]));
+var counter = 0;
+for (var [elm, _] of map) {
+    testValue(elm, counter);
+    map.set(elm + 1, elm + 1);
+    if (elm &gt; 10000) {
+        map.clear();
+    }
+    ++counter;
+}
+testValue(counter, 10002);
+
+var map = new Map(identityPairs([0, 1, 2, 3]));
+var counter = 0;
+for (var [elm, _] of map) {
+    testValue(elm, counter);
+    map.clear();
+    ++counter;
+}
+testValue(counter, 1);
+
+var map = new Map(identityPairs([0, 1, 2, 3]));
+var exp = [0, 2, 3];
+var counter = 0;
+for (var [elm, _] of map) {
+    testValue(elm, exp[counter]);
+    map.delete(counter + 1);
+    ++counter;
+}
+testValue(counter, 3);
+
+var map = new Map(identityPairs([0, 1, 2, 3]));
+var iter = map.keys();
+var iter2 = map.keys();
+testValue(iter2.next().value, 0);
+
+// Consume all output of iter.
+for (var elm of iter);
+
+testValue(iter.next().done, true);
+testValue(iter.next().value, undefined);
+
+map.clear();
+map.set(1, 1).set(2, 2).set(3, 3);
+
+testValue(iter.next().done, true);
+testValue(iter.next().value, undefined);
+testValue(iter2.next().value, 1);
+testValue(iter2.next().value, 2);
+testValue(iter2.next().value, 3);
+
+var map = new Map();
+map.set(1, 1);
+map.delete(1);
+map.forEach(function (i) {
+    throw new Error(&quot;unreeachable.&quot;);
+});
+
+var map = new Map();
+var iter = map[Symbol.iterator]();
+map.set(1, 1);
+map.delete(1);
+for (var [elm, _] of map) {
+    throw new Error(&quot;unreeachable.&quot;);
+}
+
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoretestsstressmodifysetduringiterationjs"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/tests/stress/modify-set-during-iteration.js (0 => 181922)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/stress/modify-set-during-iteration.js                                (rev 0)
+++ trunk/Source/JavaScriptCore/tests/stress/modify-set-during-iteration.js        2015-03-25 00:19:05 UTC (rev 181922)
</span><span class="lines">@@ -0,0 +1,72 @@
</span><ins>+
+function testValue(value, expected) {
+    if (value !== expected)
+        throw new Error(&quot;bad value: expected:(&quot; + expected + &quot;),actual:(&quot; + value +&quot;).&quot;);
+}
+
+var set = new Set([0]);
+var counter = 0;
+for (var elm of set) {
+    testValue(elm, counter);
+    set.add(elm + 1);
+    if (elm &gt; 10000) {
+        set.clear();
+    }
+    ++counter;
+}
+testValue(counter, 10002);
+
+var set = new Set([0, 1, 2, 3]);
+var counter = 0;
+for (var elm of set) {
+    testValue(elm, counter);
+    set.clear();
+    ++counter;
+}
+testValue(counter, 1);
+
+var set = new Set([0, 1, 2, 3]);
+var exp = [0, 2, 3];
+var counter = 0;
+for (var elm of set) {
+    testValue(elm, exp[counter]);
+    set.delete(counter + 1);
+    ++counter;
+}
+testValue(counter, 3);
+
+var set = new Set([0, 1, 2, 3]);
+var iter = set[Symbol.iterator]();
+var iter2 = set[Symbol.iterator]();
+testValue(iter2.next().value, 0);
+
+// Consume all output of iter.
+for (var elm of iter);
+
+testValue(iter.next().done, true);
+testValue(iter.next().value, undefined);
+
+set.clear();
+set.add(1).add(2).add(3);
+
+testValue(iter.next().done, true);
+testValue(iter.next().value, undefined);
+testValue(iter2.next().value, 1);
+testValue(iter2.next().value, 2);
+testValue(iter2.next().value, 3);
+
+var set = new Set();
+set.add(1);
+set.delete(1);
+set.forEach(function (i) {
+    throw new Error(&quot;unreeachable.&quot;);
+});
+
+var set = new Set();
+var iter = set[Symbol.iterator]();
+set.add(1);
+set.delete(1);
+for (var elm of iter) {
+    throw new Error(&quot;unreeachable.&quot;);
+}
+
</ins></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (181921 => 181922)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2015-03-24 23:54:04 UTC (rev 181921)
+++ trunk/Source/WebCore/ChangeLog        2015-03-25 00:19:05 UTC (rev 181922)
</span><span class="lines">@@ -1,3 +1,17 @@
</span><ins>+2015-03-24  Yusuke Suzuki  &lt;utatane.tea@gmail.com&gt;
+
+        REGRESSION (r181458): Heap use-after-free in JSSetIterator destructor
+        https://bugs.webkit.org/show_bug.cgi?id=142696
+
+        Reviewed and tweaked by Geoffrey Garen.
+
+        Use JSSetIterator/JSMapIterator to iterate over JSSet and JSMap.
+
+        * ForwardingHeaders/runtime/JSMapIterator.h: Added.
+        * ForwardingHeaders/runtime/JSSetIterator.h: Added.
+        * bindings/js/SerializedScriptValue.cpp:
+        (WebCore::CloneSerializer::serialize):
+
</ins><span class="cx"> 2015-03-24  Dan Bernstein  &lt;mitz@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Tried to fix the iOS Simulator build.
</span></span></pre></div>
<a id="trunkSourceWebCoreForwardingHeadersruntimeJSMapIteratorh"></a>
<div class="addfile"><h4>Added: trunk/Source/WebCore/ForwardingHeaders/runtime/JSMapIterator.h (0 => 181922)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ForwardingHeaders/runtime/JSMapIterator.h                                (rev 0)
+++ trunk/Source/WebCore/ForwardingHeaders/runtime/JSMapIterator.h        2015-03-25 00:19:05 UTC (rev 181922)
</span><span class="lines">@@ -0,0 +1,4 @@
</span><ins>+#ifndef WebCore_FWD_JSMapIterator_h
+#define WebCore_FWD_JSMapIterator_h
+#include &lt;JavaScriptCore/JSMapIterator.h&gt;
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCoreForwardingHeadersruntimeJSSetIteratorh"></a>
<div class="addfile"><h4>Added: trunk/Source/WebCore/ForwardingHeaders/runtime/JSSetIterator.h (0 => 181922)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ForwardingHeaders/runtime/JSSetIterator.h                                (rev 0)
+++ trunk/Source/WebCore/ForwardingHeaders/runtime/JSSetIterator.h        2015-03-25 00:19:05 UTC (rev 181922)
</span><span class="lines">@@ -0,0 +1,4 @@
</span><ins>+#ifndef WebCore_FWD_JSSetIterator_h
+#define WebCore_FWD_JSSetIterator_h
+#include &lt;JavaScriptCore/JSSetIterator.h&gt;
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorebindingsjsSerializedScriptValuecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/bindings/js/SerializedScriptValue.cpp (181921 => 181922)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/bindings/js/SerializedScriptValue.cpp        2015-03-24 23:54:04 UTC (rev 181921)
+++ trunk/Source/WebCore/bindings/js/SerializedScriptValue.cpp        2015-03-25 00:19:05 UTC (rev 181922)
</span><span class="lines">@@ -61,7 +61,9 @@
</span><span class="cx"> #include &lt;runtime/JSCInlines.h&gt;
</span><span class="cx"> #include &lt;runtime/JSDataView.h&gt;
</span><span class="cx"> #include &lt;runtime/JSMap.h&gt;
</span><ins>+#include &lt;runtime/JSMapIterator.h&gt;
</ins><span class="cx"> #include &lt;runtime/JSSet.h&gt;
</span><ins>+#include &lt;runtime/JSSetIterator.h&gt;
</ins><span class="cx"> #include &lt;runtime/JSTypedArrays.h&gt;
</span><span class="cx"> #include &lt;runtime/MapData.h&gt;
</span><span class="cx"> #include &lt;runtime/MapDataInlines.h&gt;
</span><span class="lines">@@ -1220,10 +1222,8 @@
</span><span class="cx">     Vector&lt;uint32_t, 16&gt; lengthStack;
</span><span class="cx">     Vector&lt;PropertyNameArray, 16&gt; propertyStack;
</span><span class="cx">     Vector&lt;JSObject*, 32&gt; inputObjectStack;
</span><del>-    Vector&lt;JSMap*, 4&gt; mapStack;
-    Vector&lt;JSMap::const_iterator, 4&gt; mapIteratorStack;
-    Vector&lt;JSSet*, 4&gt; setStack;
-    Vector&lt;JSSet::const_iterator, 4&gt; setIteratorStack;
</del><ins>+    Vector&lt;JSMapIterator*, 4&gt; mapIteratorStack;
+    Vector&lt;JSSetIterator*, 4&gt; setIteratorStack;
</ins><span class="cx">     Vector&lt;JSValue, 4&gt; mapIteratorValueStack;
</span><span class="cx">     Vector&lt;WalkerState, 16&gt; stateStack;
</span><span class="cx">     WalkerState state = StateUnknown;
</span><span class="lines">@@ -1356,19 +1356,19 @@
</span><span class="cx">                 JSMap* inMap = jsCast&lt;JSMap*&gt;(inValue);
</span><span class="cx">                 if (!startMap(inMap))
</span><span class="cx">                     break;
</span><ins>+                JSMapIterator* iterator = JSMapIterator::create(m_exec-&gt;vm(), m_exec-&gt;lexicalGlobalObject()-&gt;mapIteratorStructure(), inMap, MapIterateKeyValue);
</ins><span class="cx">                 m_gcBuffer.append(inMap);
</span><del>-                mapStack.append(inMap);
-                mapIteratorStack.append(inMap-&gt;begin());
</del><ins>+                m_gcBuffer.append(iterator);
+                mapIteratorStack.append(iterator);
</ins><span class="cx">                 inputObjectStack.append(inMap);
</span><span class="cx">                 goto mapDataStartVisitEntry;
</span><span class="cx">             }
</span><span class="cx">             mapDataStartVisitEntry:
</span><span class="cx">             case MapDataStartVisitEntry: {
</span><del>-                JSMap::const_iterator&amp; ptr = mapIteratorStack.last();
-                JSMap* map = mapStack.last();
-                if (ptr == map-&gt;end()) {
</del><ins>+                JSMapIterator* iterator = mapIteratorStack.last();
+                JSValue key, value;
+                if (!iterator-&gt;nextKeyValue(key, value)) {
</ins><span class="cx">                     mapIteratorStack.removeLast();
</span><del>-                    mapStack.removeLast();
</del><span class="cx">                     JSObject* object = inputObjectStack.last();
</span><span class="cx">                     ASSERT(jsDynamicCast&lt;JSMap*&gt;(object));
</span><span class="cx">                     propertyStack.append(PropertyNameArray(m_exec));
</span><span class="lines">@@ -1377,9 +1377,9 @@
</span><span class="cx">                     indexStack.append(0);
</span><span class="cx">                     goto objectStartVisitMember;
</span><span class="cx">                 }
</span><del>-                inValue = ptr.key();
-                m_gcBuffer.append(ptr.value());
-                mapIteratorValueStack.append(ptr.value());
</del><ins>+                inValue = key;
+                m_gcBuffer.append(value);
+                mapIteratorValueStack.append(value);
</ins><span class="cx">                 stateStack.append(MapDataEndVisitKey);
</span><span class="cx">                 goto stateUnknown;
</span><span class="cx">             }
</span><span class="lines">@@ -1390,8 +1390,6 @@
</span><span class="cx">                 goto stateUnknown;
</span><span class="cx">             }
</span><span class="cx">             case MapDataEndVisitValue: {
</span><del>-                if (mapIteratorStack.last() != mapStack.last()-&gt;end())
-                    ++mapIteratorStack.last();
</del><span class="cx">                 goto mapDataStartVisitEntry;
</span><span class="cx">             }
</span><span class="cx"> 
</span><span class="lines">@@ -1402,19 +1400,19 @@
</span><span class="cx">                 JSSet* inSet = jsCast&lt;JSSet*&gt;(inValue);
</span><span class="cx">                 if (!startSet(inSet))
</span><span class="cx">                     break;
</span><ins>+                JSSetIterator* iterator = JSSetIterator::create(m_exec-&gt;vm(), m_exec-&gt;lexicalGlobalObject()-&gt;setIteratorStructure(), inSet, SetIterateKey);
</ins><span class="cx">                 m_gcBuffer.append(inSet);
</span><del>-                setStack.append(inSet);
-                setIteratorStack.append(inSet-&gt;begin());
</del><ins>+                m_gcBuffer.append(iterator);
+                setIteratorStack.append(iterator);
</ins><span class="cx">                 inputObjectStack.append(inSet);
</span><span class="cx">                 goto setDataStartVisitEntry;
</span><span class="cx">             }
</span><span class="cx">             setDataStartVisitEntry:
</span><span class="cx">             case SetDataStartVisitEntry: {
</span><del>-                JSSet::const_iterator&amp; ptr = setIteratorStack.last();
-                JSSet* set = setStack.last();
-                if (ptr == set-&gt;end()) {
</del><ins>+                JSSetIterator* iterator = setIteratorStack.last();
+                JSValue key;
+                if (!iterator-&gt;next(m_exec, key)) {
</ins><span class="cx">                     setIteratorStack.removeLast();
</span><del>-                    setStack.removeLast();
</del><span class="cx">                     JSObject* object = inputObjectStack.last();
</span><span class="cx">                     ASSERT(jsDynamicCast&lt;JSSet*&gt;(object));
</span><span class="cx">                     propertyStack.append(PropertyNameArray(m_exec));
</span><span class="lines">@@ -1423,13 +1421,11 @@
</span><span class="cx">                     indexStack.append(0);
</span><span class="cx">                     goto objectStartVisitMember;
</span><span class="cx">                 }
</span><del>-                inValue = ptr.key();
</del><ins>+                inValue = key;
</ins><span class="cx">                 stateStack.append(SetDataEndVisitKey);
</span><span class="cx">                 goto stateUnknown;
</span><span class="cx">             }
</span><span class="cx">             case SetDataEndVisitKey: {
</span><del>-                if (setIteratorStack.last() != setStack.last()-&gt;end())
-                    ++setIteratorStack.last();
</del><span class="cx">                 goto setDataStartVisitEntry;
</span><span class="cx">             }
</span><span class="cx"> 
</span></span></pre>
</div>
</div>

</body>
</html>