<!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->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->m_iteratorCount.
It is guaranteed that JSSet is live since JSSetIterator has a reference to JSSet
and marks it in visitChildren (WriteBarrier<Unknown>).
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, <1> and <2>.
JSSet<1> <- JSSetIterator<2>
And after that, when performing GC, Marker decides that the above 2 objects are not marked.
And Marker also decides MarkedBlocks <1> and <2> can be sweeped.
First Sweeper sweep <1>, destruct JSSet<1> and free MarkedBlock<1>.
Second Sweeper sweep <2>, attempt to destruct JSSetIterator<2>.
However, JSSetIterator<2>'s destructor,
JSSetIterator::~JSSetIterator requires live JSSet<1>, 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 <utatane.tea@gmail.com> 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>::MapDataImpl):
(JSC::JSIterator>::KeyType::KeyType):
(JSC::JSIterator>::IteratorData::IteratorData):
(JSC::JSIterator>::IteratorData::next):
(JSC::JSIterator>::IteratorData::ensureSlot):
(JSC::JSIterator>::IteratorData::applyMapDataPatch):
(JSC::JSIterator>::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<Entry>::MapDataImpl): Deleted.
(JSC::MapDataImpl<Entry>::clear): Deleted.
(JSC::MapDataImpl<Entry>::KeyType::KeyType): Deleted.
(JSC::MapDataImpl<Entry>::const_iterator::internalIncrement): Deleted.
(JSC::MapDataImpl<Entry>::const_iterator::ensureSlot): Deleted.
(JSC::MapDataImpl<Entry>::const_iterator::const_iterator): Deleted.
(JSC::MapDataImpl<Entry>::const_iterator::~const_iterator): Deleted.
(JSC::MapDataImpl<Entry>::const_iterator::operator): Deleted.
(JSC::=): Deleted.
* runtime/MapDataInlines.h:
(JSC::JSIterator>::clear):
(JSC::JSIterator>::find):
(JSC::JSIterator>::contains):
(JSC::JSIterator>::add):
(JSC::JSIterator>::set):
(JSC::JSIterator>::get):
(JSC::JSIterator>::remove):
(JSC::JSIterator>::replaceAndPackBackingStore):
(JSC::JSIterator>::replaceBackingStore):
(JSC::JSIterator>::ensureSpaceForAppend):
(JSC::JSIterator>::visitChildren):
(JSC::JSIterator>::copyBackingStore):
(JSC::JSIterator>::applyMapDataPatch):
(JSC::MapDataImpl<Entry>::find): Deleted.
(JSC::MapDataImpl<Entry>::contains): Deleted.
(JSC::MapDataImpl<Entry>::add): Deleted.
(JSC::MapDataImpl<Entry>::set): Deleted.
(JSC::MapDataImpl<Entry>::get): Deleted.
(JSC::MapDataImpl<Entry>::remove): Deleted.
(JSC::MapDataImpl<Entry>::replaceAndPackBackingStore): Deleted.
(JSC::MapDataImpl<Entry>::replaceBackingStore): Deleted.
(JSC::MapDataImpl<Entry>::ensureSpaceForAppend): Deleted.
(JSC::MapDataImpl<Entry>::visitChildren): Deleted.
(JSC::MapDataImpl<Entry>::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 <utatane.tea@gmail.com> 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 <utatane.tea@gmail.com>
+
+ 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->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->m_iteratorCount.
+
+ It is guaranteed that JSSet is live since JSSetIterator has a reference to JSSet
+ and marks it in visitChildren (WriteBarrier<Unknown>).
+ 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, <1> and <2>.
+
+ JSSet<1> <- JSSetIterator<2>
+
+ And after that, when performing GC, Marker decides that the above 2 objects are not marked.
+ And Marker also decides MarkedBlocks <1> and <2> can be sweeped.
+
+ First Sweeper sweep <1>, destruct JSSet<1> and free MarkedBlock<1>.
+ Second Sweeper sweep <2>, attempt to destruct JSSetIterator<2>.
+ However, JSSetIterator<2>'s destructor,
+ JSSetIterator::~JSSetIterator requires live JSSet<1>, 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>::MapDataImpl):
+ (JSC::JSIterator>::KeyType::KeyType):
+ (JSC::JSIterator>::IteratorData::IteratorData):
+ (JSC::JSIterator>::IteratorData::next):
+ (JSC::JSIterator>::IteratorData::ensureSlot):
+ (JSC::JSIterator>::IteratorData::applyMapDataPatch):
+ (JSC::JSIterator>::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<Entry>::MapDataImpl): Deleted.
+ (JSC::MapDataImpl<Entry>::clear): Deleted.
+ (JSC::MapDataImpl<Entry>::KeyType::KeyType): Deleted.
+ (JSC::MapDataImpl<Entry>::const_iterator::internalIncrement): Deleted.
+ (JSC::MapDataImpl<Entry>::const_iterator::ensureSlot): Deleted.
+ (JSC::MapDataImpl<Entry>::const_iterator::const_iterator): Deleted.
+ (JSC::MapDataImpl<Entry>::const_iterator::~const_iterator): Deleted.
+ (JSC::MapDataImpl<Entry>::const_iterator::operator): Deleted.
+ (JSC::=): Deleted.
+ * runtime/MapDataInlines.h:
+ (JSC::JSIterator>::clear):
+ (JSC::JSIterator>::find):
+ (JSC::JSIterator>::contains):
+ (JSC::JSIterator>::add):
+ (JSC::JSIterator>::set):
+ (JSC::JSIterator>::get):
+ (JSC::JSIterator>::remove):
+ (JSC::JSIterator>::replaceAndPackBackingStore):
+ (JSC::JSIterator>::replaceBackingStore):
+ (JSC::JSIterator>::ensureSpaceForAppend):
+ (JSC::JSIterator>::visitChildren):
+ (JSC::JSIterator>::copyBackingStore):
+ (JSC::JSIterator>::applyMapDataPatch):
+ (JSC::MapDataImpl<Entry>::find): Deleted.
+ (JSC::MapDataImpl<Entry>::contains): Deleted.
+ (JSC::MapDataImpl<Entry>::add): Deleted.
+ (JSC::MapDataImpl<Entry>::set): Deleted.
+ (JSC::MapDataImpl<Entry>::get): Deleted.
+ (JSC::MapDataImpl<Entry>::remove): Deleted.
+ (JSC::MapDataImpl<Entry>::replaceAndPackBackingStore): Deleted.
+ (JSC::MapDataImpl<Entry>::replaceBackingStore): Deleted.
+ (JSC::MapDataImpl<Entry>::ensureSpaceForAppend): Deleted.
+ (JSC::MapDataImpl<Entry>::visitChildren): Deleted.
+ (JSC::MapDataImpl<Entry>::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 <mark.lam@apple.com>
</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 "JSMap.h"
</span><span class="cx">
</span><span class="cx"> #include "JSCJSValueInlines.h"
</span><ins>+#include "JSMapIterator.h"
</ins><span class="cx"> #include "MapDataInlines.h"
</span><span class="cx"> #include "SlotVisitorInlines.h"
</span><span class="cx"> #include "StructureInlines.h"
</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<JSMap*>(cell)->~JSMap();
</del><ins>+ JSMap* thisObject = jsCast<JSMap*>(cell);
+ thisObject->JSMap::~JSMap();
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> void JSMap::visitChildren(JSCell* cell, SlotVisitor& 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<Unknown>
</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<Entry> MapData;
</del><ins>+ typedef MapDataImpl<Entry, JSMapIterator> 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->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& 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 "JSCJSValueInlines.h"
</span><span class="cx"> #include "JSCellInlines.h"
</span><span class="cx"> #include "JSMap.h"
</span><ins>+#include "MapDataInlines.h"
</ins><span class="cx"> #include "SlotVisitorInlines.h"
</span><span class="cx"> #include "StructureInlines.h"
</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<JSMapIterator*>(cell)->~JSMapIterator();
</del><ins>+ JSMapIterator* thisObject = jsCast<JSMapIterator*>(cell);
+ thisObject->JSMapIterator::~JSMapIterator();
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> void JSMapIterator::visitChildren(JSCell* cell, SlotVisitor& 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& value)
</span><span class="cx"> {
</span><del>- if (!m_iterator.ensureSlot())
</del><ins>+ WTF::KeyValuePair<JSValue, JSValue> 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& key, JSValue& value)
+ {
+ WTF::KeyValuePair<JSValue, JSValue> 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 &m_iterator;
+ }
+
</ins><span class="cx"> private:
</span><span class="cx"> JSMapIterator(VM& vm, Structure* structure, JSMap* iteratedObject, MapIterationKind kind)
</span><span class="cx"> : Base(vm, structure)
</span><del>- , m_iterator(iteratedObject->begin())
</del><ins>+ , m_iterator(iteratedObject->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&, JSMap*);
</del><ins>+ JS_EXPORT_PRIVATE void finishCreation(VM&, JSMap*);
</ins><span class="cx"> JSValue createPair(CallFrame*, JSValue, JSValue);
</span><span class="cx"> static void visitChildren(JSCell*, SlotVisitor&);
</span><span class="cx">
</span><span class="cx"> WriteBarrier<JSMap> 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 "JSSet.h"
</span><span class="cx">
</span><span class="cx"> #include "JSCJSValueInlines.h"
</span><ins>+#include "JSSetIterator.h"
</ins><span class="cx"> #include "MapDataInlines.h"
</span><span class="cx"> #include "SlotVisitorInlines.h"
</span><span class="cx"> #include "StructureInlines.h"
</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<JSSet*>(cell)->~JSSet();
</del><ins>+ JSSet* thisObject = jsCast<JSSet*>(cell);
+ thisObject->JSSet::~JSSet();
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> void JSSet::visitChildren(JSCell* cell, SlotVisitor& 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<Unknown>
</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<Entry> SetData;
</del><ins>+ typedef MapDataImpl<Entry, JSSetIterator> 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->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& 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 "JSCJSValueInlines.h"
</span><span class="cx"> #include "JSCellInlines.h"
</span><span class="cx"> #include "JSSet.h"
</span><ins>+#include "MapDataInlines.h"
</ins><span class="cx"> #include "SlotVisitorInlines.h"
</span><span class="cx"> #include "StructureInlines.h"
</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<JSSetIterator*>(cell)->~JSSetIterator();
</del><ins>+ JSSetIterator* thisObject = jsCast<JSSetIterator*>(cell);
+ thisObject->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& value)
</span><span class="cx"> {
</span><del>- if (!m_iterator.ensureSlot())
</del><ins>+ WTF::KeyValuePair<JSValue, JSValue> 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 &m_iterator;
+ }
+
</ins><span class="cx"> private:
</span><span class="cx"> JSSetIterator(VM& vm, Structure* structure, JSSet* iteratedObject, SetIterationKind kind)
</span><span class="cx"> : Base(vm, structure)
</span><del>- , m_iterator(iteratedObject->begin())
</del><ins>+ , m_iterator(iteratedObject->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&, JSSet*);
- JSValue createPair(CallFrame*, JSValue, JSValue);
</del><ins>+ JS_EXPORT_PRIVATE void finishCreation(VM&, JSSet*);
+ JS_EXPORT_PRIVATE JSValue createPair(CallFrame*, JSValue, JSValue);
</ins><span class="cx"> static void visitChildren(JSCell*, SlotVisitor&);
</span><span class="cx">
</span><span class="cx"> WriteBarrier<JSSet> 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 "JSCell.h"
</span><ins>+#include "WeakGCMapInlines.h"
</ins><span class="cx"> #include <wtf/HashFunctions.h>
</span><span class="cx"> #include <wtf/HashMap.h>
</span><span class="cx"> #include <wtf/MathExtras.h>
</span><ins>+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Vector.h>
</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<typename Entry>
</del><ins>+template<typename Entry, typename JSIterator>
</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<JSValue, JSValue> operator*() const;
- JSValue key() const { RELEASE_ASSERT(!atEnd()); return m_mapData->m_entries[m_index].key().get(); }
- JSValue value() const { RELEASE_ASSERT(!atEnd()); return m_mapData->m_entries[m_index].value().get(); }
- void operator++() { ASSERT(!atEnd()); internalIncrement(); }
- static const_iterator end(const MapDataImpl*);
- bool operator!=(const const_iterator& other);
- bool operator==(const const_iterator& other);
- void finish() { m_index = std::numeric_limits<int32_t>::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<JSValue, JSValue>&);
</ins><span class="cx">
</span><ins>+ void didRemoveEntry(int32_t index)
+ {
+ if (m_index <= 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
- // "end()" 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<size_t>(m_index) >= static_cast<size_t>(m_mapData->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&);
</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 <typename Map, typename Key> ALWAYS_INLINE Entry* add(ExecState*, JSCell* owner, Map&, Key, KeyType);
</span><span class="cx">
</span><del>- ALWAYS_INLINE bool shouldPack() const { return m_deletedCount && !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<JSIterator*, JSIterator> m_iterators;
</ins><span class="cx"> };
</span><span class="cx">
</span><del>-template<typename Entry>
-ALWAYS_INLINE MapDataImpl<Entry>::MapDataImpl()
</del><ins>+template<typename Entry, typename JSIterator>
+ALWAYS_INLINE MapDataImpl<Entry, JSIterator>::MapDataImpl(VM& 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<typename Entry>
-ALWAYS_INLINE void MapDataImpl<Entry>::clear()
</del><ins>+template<typename Entry, typename JSIterator>
+ALWAYS_INLINE MapDataImpl<Entry, JSIterator>::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<typename Entry>
-ALWAYS_INLINE MapDataImpl<Entry>::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<typename Entry>
-ALWAYS_INLINE void MapDataImpl<Entry>::const_iterator::internalIncrement()
-{
- Entry* entries = m_mapData->m_entries;
- size_t index = m_index + 1;
- size_t end = m_mapData->m_size;
- while (index < end && !entries[index].key())
- index++;
- m_index = index;
-}
-
-template<typename Entry>
-ALWAYS_INLINE bool MapDataImpl<Entry>::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->m_entries;
- size_t index = m_index;
- size_t end = m_mapData->m_size;
- if (index < end && entries[index].key())
- return true;
- internalIncrement();
- return static_cast<size_t>(m_index) < end;
-}
-
-template<typename Entry>
-ALWAYS_INLINE MapDataImpl<Entry>::const_iterator::const_iterator(const MapDataImpl<Entry>* mapData)
</del><ins>+template<typename Entry, typename JSIterator>
+ALWAYS_INLINE MapDataImpl<Entry, JSIterator>::IteratorData::IteratorData(const MapDataImpl<Entry, JSIterator>* 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<typename Entry>
-ALWAYS_INLINE MapDataImpl<Entry>::const_iterator::~const_iterator()
</del><ins>+template<typename Entry, typename JSIterator>
+ALWAYS_INLINE bool MapDataImpl<Entry, JSIterator>::IteratorData::next(WTF::KeyValuePair<JSValue, JSValue>& pair)
</ins><span class="cx"> {
</span><del>- m_mapData->m_iteratorCount--;
-}
-
-template<typename Entry>
-ALWAYS_INLINE const WTF::KeyValuePair<JSValue, JSValue> MapDataImpl<Entry>::const_iterator::operator*() const
-{
</del><ins>+ if (!ensureSlot())
+ return false;
</ins><span class="cx"> Entry* entry = &m_mapData->m_entries[m_index];
</span><del>- return WTF::KeyValuePair<JSValue, JSValue>(entry->key().get(), entry->value().get());
</del><ins>+ pair = WTF::KeyValuePair<JSValue, JSValue>(entry->key().get(), entry->value().get());
+ m_index += 1;
+ return true;
</ins><span class="cx"> }
</span><span class="cx">
</span><del>-template<typename Entry>
-ALWAYS_INLINE auto MapDataImpl<Entry>::const_iterator::end(const MapDataImpl<Entry>* mapData) -> 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<typename Entry, typename JSIterator>
+ALWAYS_INLINE bool MapDataImpl<Entry, JSIterator>::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<size_t>(index) < static_cast<size_t>(m_mapData->m_size);
</ins><span class="cx"> }
</span><span class="cx">
</span><del>-template<typename Entry>
-ALWAYS_INLINE bool MapDataImpl<Entry>::const_iterator::operator!=(const const_iterator& other)
</del><ins>+template<typename Entry, typename JSIterator>
+ALWAYS_INLINE int32_t MapDataImpl<Entry, JSIterator>::IteratorData::refreshCursor() const
</ins><span class="cx"> {
</span><del>- ASSERT(other.m_mapData == m_mapData);
- if (atEnd() && other.atEnd())
- return false;
- return m_index != other.m_index;
-}
</del><ins>+ if (isFinished())
+ return m_index;
</ins><span class="cx">
</span><del>-template<typename Entry>
-ALWAYS_INLINE bool MapDataImpl<Entry>::const_iterator::operator==(const const_iterator& other)
-{
- return !(*this != other);
</del><ins>+ Entry* entries = m_mapData->m_entries;
+ size_t end = m_mapData->m_size;
+ while (static_cast<size_t>(m_index) < end && !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<typename Entry>
-inline Entry* MapDataImpl<Entry>::find(ExecState* exec, KeyType key)
</del><ins>+template<typename Entry, typename JSIterator>
+inline void MapDataImpl<Entry, JSIterator>::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->iteratorData()->didRemoveAllEntries();
+ });
+}
+
+template<typename Entry, typename JSIterator>
+inline Entry* MapDataImpl<Entry, JSIterator>::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)->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 &m_entries[iter->value];
</span><span class="cx"> }
</span><span class="cx">
</span><del>-template<typename Entry>
-inline bool MapDataImpl<Entry>::contains(ExecState* exec, KeyType key)
</del><ins>+template<typename Entry, typename JSIterator>
+inline bool MapDataImpl<Entry, JSIterator>::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<typename Entry>
</del><ins>+template<typename Entry, typename JSIterator>
</ins><span class="cx"> template <typename Map, typename Key>
</span><del>-inline Entry* MapDataImpl<Entry>::add(ExecState* exec, JSCell* owner, Map& map, Key key, KeyType keyValue)
</del><ins>+inline Entry* MapDataImpl<Entry, JSIterator>::add(ExecState* exec, JSCell* owner, Map& 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<typename Entry>
-inline void MapDataImpl<Entry>::set(ExecState* exec, JSCell* owner, KeyType key, JSValue value)
</del><ins>+template<typename Entry, typename JSIterator>
+inline void MapDataImpl<Entry, JSIterator>::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->setValue(exec->vm(), owner, value);
</span><span class="cx"> }
</span><span class="cx">
</span><del>-template<typename Entry>
-inline Entry* MapDataImpl<Entry>::add(ExecState* exec, JSCell* owner, KeyType key)
</del><ins>+template<typename Entry, typename JSIterator>
+inline Entry* MapDataImpl<Entry, JSIterator>::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)->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<typename Entry>
-inline JSValue MapDataImpl<Entry>::get(ExecState* exec, KeyType key)
</del><ins>+template<typename Entry, typename JSIterator>
+inline JSValue MapDataImpl<Entry, JSIterator>::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->value().get();
</span><span class="cx"> return JSValue();
</span><span class="cx"> }
</span><span class="cx">
</span><del>-template<typename Entry>
-inline bool MapDataImpl<Entry>::remove(ExecState* exec, KeyType key)
</del><ins>+template<typename Entry, typename JSIterator>
+inline bool MapDataImpl<Entry, JSIterator>::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<typename Entry>
-inline void MapDataImpl<Entry>::replaceAndPackBackingStore(Entry* destination, int32_t newCapacity)
</del><ins>+template<typename Entry, typename JSIterator>
+inline void MapDataImpl<Entry, JSIterator>::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 > 0);
</span><span class="cx"> for (int32_t i = 0; i < m_size; i++) {
</span><span class="cx"> Entry& entry = m_entries[i];
</span><del>- if (!entry.key())
</del><ins>+ if (!entry.key()) {
+ m_iterators.forEach([i](JSIterator* iterator, JSIterator*) {
+ iterator->iteratorData()->didRemoveEntry(i);
+ });
</ins><span class="cx"> continue;
</span><ins>+ }
</ins><span class="cx"> ASSERT(newEnd < 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<typename Entry>
-inline void MapDataImpl<Entry>::replaceBackingStore(Entry* destination, int32_t newCapacity)
</del><ins>+template<typename Entry, typename JSIterator>
+inline void MapDataImpl<Entry, JSIterator>::replaceBackingStore(Entry* destination, int32_t newCapacity)
</ins><span class="cx"> {
</span><span class="cx"> ASSERT(!shouldPack());
</span><span class="cx"> RELEASE_ASSERT(newCapacity > 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<typename Entry>
-inline CheckedBoolean MapDataImpl<Entry>::ensureSpaceForAppend(ExecState* exec, JSCell* owner)
</del><ins>+template<typename Entry, typename JSIterator>
+inline CheckedBoolean MapDataImpl<Entry, JSIterator>::ensureSpaceForAppend(ExecState* exec, JSCell* owner)
</ins><span class="cx"> {
</span><span class="cx"> if (m_capacity > 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<typename Entry>
-inline void MapDataImpl<Entry>::visitChildren(JSCell* owner, SlotVisitor& visitor)
</del><ins>+template<typename Entry, typename JSIterator>
+inline void MapDataImpl<Entry, JSIterator>::visitChildren(JSCell* owner, SlotVisitor& 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<typename Entry>
-inline void MapDataImpl<Entry>::copyBackingStore(CopyVisitor& visitor, CopyToken token)
</del><ins>+template<typename Entry, typename JSIterator>
+inline void MapDataImpl<Entry, JSIterator>::copyBackingStore(CopyVisitor& visitor, CopyToken token)
</ins><span class="cx"> {
</span><span class="cx"> if (token == MapBackingStoreCopyToken && 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<typename Entry, typename JSIterator>
+inline auto MapDataImpl<Entry, JSIterator>::createIteratorData(JSIterator* iterator) -> 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("Map.prototype.forEach called without callback")));
</span><span class="cx"> JSValue thisValue = callFrame->argument(1);
</span><span class="cx"> VM* vm = &callFrame->vm();
</span><ins>+ JSMapIterator* iterator = JSMapIterator::create(*vm, callFrame->callee()->globalObject()->mapIteratorStructure(), map, MapIterateKeyValue);
+ JSValue key, value;
</ins><span class="cx"> if (callType == CallTypeJS) {
</span><span class="cx"> JSFunction* function = jsCast<JSFunction*>(callBack);
</span><span class="cx"> CachedCall cachedCall(callFrame, function, 2);
</span><del>- for (auto ptr = map->begin(), end = map->end(); ptr != end && !vm->exception(); ++ptr) {
</del><ins>+ while (iterator->nextKeyValue(key, value) && !vm->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->finish();
</ins><span class="cx"> } else {
</span><del>- for (auto ptr = map->begin(), end = map->end(); ptr != end && !vm->exception(); ++ptr) {
</del><ins>+ while (iterator->nextKeyValue(key, value) && !vm->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->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("Set.prototype.forEach called without callback")));
</span><span class="cx"> JSValue thisValue = callFrame->argument(1);
</span><span class="cx"> VM* vm = &callFrame->vm();
</span><ins>+ JSSetIterator* iterator = JSSetIterator::create(*vm, callFrame->callee()->globalObject()->setIteratorStructure(), set, SetIterateKey);
+ JSValue key;
</ins><span class="cx"> if (callType == CallTypeJS) {
</span><span class="cx"> JSFunction* function = jsCast<JSFunction*>(callBack);
</span><span class="cx"> CachedCall cachedCall(callFrame, function, 1);
</span><del>- for (auto ptr = set->begin(), end = set->end(); ptr != end && !vm->exception(); ++ptr) {
</del><ins>+ while (iterator->next(callFrame, key) && !vm->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->finish();
</ins><span class="cx"> } else {
</span><del>- for (auto ptr = set->begin(), end = set->end(); ptr != end && !vm->exception(); ++ptr) {
</del><ins>+ while (iterator->next(callFrame, key) && !vm->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->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->value)
+ return true;
+ }
+ return false;
+ }
+
</ins><span class="cx"> iterator find(const KeyType& 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<WeakGCMap*>(this)->find(key);
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+ template<typename Functor>
+ void forEach(Functor functor)
+ {
+ for (auto& pair : m_map) {
+ if (pair.value)
+ functor(pair.key, pair.value.get());
+ }
+ }
+
</ins><span class="cx"> bool contains(const KeyType& 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("bad value: expected:(" + expected + "),actual:(" + value +").");
+}
+
+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 > 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("unreeachable.");
+});
+
+var map = new Map();
+var iter = map[Symbol.iterator]();
+map.set(1, 1);
+map.delete(1);
+for (var [elm, _] of map) {
+ throw new Error("unreeachable.");
+}
+
</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("bad value: expected:(" + expected + "),actual:(" + value +").");
+}
+
+var set = new Set([0]);
+var counter = 0;
+for (var elm of set) {
+ testValue(elm, counter);
+ set.add(elm + 1);
+ if (elm > 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("unreeachable.");
+});
+
+var set = new Set();
+var iter = set[Symbol.iterator]();
+set.add(1);
+set.delete(1);
+for (var elm of iter) {
+ throw new Error("unreeachable.");
+}
+
</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 <utatane.tea@gmail.com>
+
+ 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 <mitz@apple.com>
</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 <JavaScriptCore/JSMapIterator.h>
+#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 <JavaScriptCore/JSSetIterator.h>
+#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 <runtime/JSCInlines.h>
</span><span class="cx"> #include <runtime/JSDataView.h>
</span><span class="cx"> #include <runtime/JSMap.h>
</span><ins>+#include <runtime/JSMapIterator.h>
</ins><span class="cx"> #include <runtime/JSSet.h>
</span><ins>+#include <runtime/JSSetIterator.h>
</ins><span class="cx"> #include <runtime/JSTypedArrays.h>
</span><span class="cx"> #include <runtime/MapData.h>
</span><span class="cx"> #include <runtime/MapDataInlines.h>
</span><span class="lines">@@ -1220,10 +1222,8 @@
</span><span class="cx"> Vector<uint32_t, 16> lengthStack;
</span><span class="cx"> Vector<PropertyNameArray, 16> propertyStack;
</span><span class="cx"> Vector<JSObject*, 32> inputObjectStack;
</span><del>- Vector<JSMap*, 4> mapStack;
- Vector<JSMap::const_iterator, 4> mapIteratorStack;
- Vector<JSSet*, 4> setStack;
- Vector<JSSet::const_iterator, 4> setIteratorStack;
</del><ins>+ Vector<JSMapIterator*, 4> mapIteratorStack;
+ Vector<JSSetIterator*, 4> setIteratorStack;
</ins><span class="cx"> Vector<JSValue, 4> mapIteratorValueStack;
</span><span class="cx"> Vector<WalkerState, 16> stateStack;
</span><span class="cx"> WalkerState state = StateUnknown;
</span><span class="lines">@@ -1356,19 +1356,19 @@
</span><span class="cx"> JSMap* inMap = jsCast<JSMap*>(inValue);
</span><span class="cx"> if (!startMap(inMap))
</span><span class="cx"> break;
</span><ins>+ JSMapIterator* iterator = JSMapIterator::create(m_exec->vm(), m_exec->lexicalGlobalObject()->mapIteratorStructure(), inMap, MapIterateKeyValue);
</ins><span class="cx"> m_gcBuffer.append(inMap);
</span><del>- mapStack.append(inMap);
- mapIteratorStack.append(inMap->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& ptr = mapIteratorStack.last();
- JSMap* map = mapStack.last();
- if (ptr == map->end()) {
</del><ins>+ JSMapIterator* iterator = mapIteratorStack.last();
+ JSValue key, value;
+ if (!iterator->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<JSMap*>(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()->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<JSSet*>(inValue);
</span><span class="cx"> if (!startSet(inSet))
</span><span class="cx"> break;
</span><ins>+ JSSetIterator* iterator = JSSetIterator::create(m_exec->vm(), m_exec->lexicalGlobalObject()->setIteratorStructure(), inSet, SetIterateKey);
</ins><span class="cx"> m_gcBuffer.append(inSet);
</span><del>- setStack.append(inSet);
- setIteratorStack.append(inSet->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& ptr = setIteratorStack.last();
- JSSet* set = setStack.last();
- if (ptr == set->end()) {
</del><ins>+ JSSetIterator* iterator = setIteratorStack.last();
+ JSValue key;
+ if (!iterator->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<JSSet*>(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()->end())
- ++setIteratorStack.last();
</del><span class="cx"> goto setDataStartVisitEntry;
</span><span class="cx"> }
</span><span class="cx">
</span></span></pre>
</div>
</div>
</body>
</html>