<!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>[167889] trunk</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/167889">167889</a></dd>
<dt>Author</dt> <dd>mhahnenberg@apple.com</dd>
<dt>Date</dt> <dd>2014-04-28 10:26:33 -0700 (Mon, 28 Apr 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>Deleting properties poisons objects
https://bugs.webkit.org/show_bug.cgi?id=131551

Reviewed by Oliver Hunt.

Source/JavaScriptCore:
This is ~3% progression on Dromaeo with a ~6% progression on the jslib portion of Dromaeo in particular.

* runtime/JSPropertyNameIterator.cpp:
(JSC::JSPropertyNameIterator::create):
* runtime/PropertyMapHashTable.h:
(JSC::PropertyTable::hasDeletedOffset):
(JSC::PropertyTable::hadDeletedOffset): If we ever had deleted properties we can no longer cache offsets when
iterating properties because we're required to iterate properties in insertion order.
* runtime/Structure.cpp:
(JSC::Structure::Structure):
(JSC::Structure::materializePropertyMap): We now re-use deleted properties when materializing the property map.
(JSC::Structure::removePropertyTransition): We allow up to 5 deletes for a particular path through the tree of
Structure transitions. After that, we convert to an uncacheable dictionary like we used to. We don't cache
delete transitions, but we allow transitioning from them.
(JSC::Structure::changePrototypeTransition):
(JSC::Structure::despecifyFunctionTransition):
(JSC::Structure::attributeChangeTransition):
(JSC::Structure::toDictionaryTransition):
(JSC::Structure::preventExtensionsTransition):
(JSC::Structure::addPropertyWithoutTransition):
(JSC::Structure::removePropertyWithoutTransition):
(JSC::Structure::pin): Now does only what it says it does--marks the property table as pinned.
(JSC::Structure::pinAndPreventTransitions): More descriptive version of what the old pin() was doing.
* runtime/Structure.h:
* runtime/StructureInlines.h:
(JSC::Structure::setEnumerationCache):
(JSC::Structure::hadDeletedOffsets):
(JSC::Structure::propertyTable):
(JSC::Structure::checkOffsetConsistency): Rearranged variables to be more sensible.
* tests/stress/for-in-after-delete.js: Added.
(foo):

LayoutTests:
New JS regress test. We're ~3.5x faster on this microbenchmark now.

* js/regress/delete-a-few-properties-then-get-by-id-expected.txt: Added.
* js/regress/delete-a-few-properties-then-get-by-id.html: Added.
* js/regress/script-tests/delete-a-few-properties-then-get-by-id.js: Added.
(MyObject):
(foo):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeJSPropertyNameIteratorcpp">trunk/Source/JavaScriptCore/runtime/JSPropertyNameIterator.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimePropertyMapHashTableh">trunk/Source/JavaScriptCore/runtime/PropertyMapHashTable.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeStructurecpp">trunk/Source/JavaScriptCore/runtime/Structure.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeStructureh">trunk/Source/JavaScriptCore/runtime/Structure.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeStructureInlinesh">trunk/Source/JavaScriptCore/runtime/StructureInlines.h</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsjsregressdeleteafewpropertiesthengetbyidexpectedtxt">trunk/LayoutTests/js/regress/delete-a-few-properties-then-get-by-id-expected.txt</a></li>
<li><a href="#trunkLayoutTestsjsregressdeleteafewpropertiesthengetbyidhtml">trunk/LayoutTests/js/regress/delete-a-few-properties-then-get-by-id.html</a></li>
<li><a href="#trunkLayoutTestsjsregressscripttestsdeleteafewpropertiesthengetbyidjs">trunk/LayoutTests/js/regress/script-tests/delete-a-few-properties-then-get-by-id.js</a></li>
<li><a href="#trunkSourceJavaScriptCoretestsstressforinafterdeletejs">trunk/Source/JavaScriptCore/tests/stress/for-in-after-delete.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (167888 => 167889)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2014-04-28 15:59:13 UTC (rev 167888)
+++ trunk/LayoutTests/ChangeLog        2014-04-28 17:26:33 UTC (rev 167889)
</span><span class="lines">@@ -1,3 +1,18 @@
</span><ins>+2014-04-28  Mark Hahnenberg  &lt;mhahnenberg@apple.com&gt;
+
+        Deleting properties poisons objects
+        https://bugs.webkit.org/show_bug.cgi?id=131551
+
+        Reviewed by Oliver Hunt.
+
+        New JS regress test. We're ~3.5x faster on this microbenchmark now.
+
+        * js/regress/delete-a-few-properties-then-get-by-id-expected.txt: Added.
+        * js/regress/delete-a-few-properties-then-get-by-id.html: Added.
+        * js/regress/script-tests/delete-a-few-properties-then-get-by-id.js: Added.
+        (MyObject):
+        (foo):
+
</ins><span class="cx"> 2014-04-28  Xabier Rodriguez Calvar  &lt;calvaris@igalia.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Unreviewed GTK gardening.
</span></span></pre></div>
<a id="trunkLayoutTestsjsregressdeleteafewpropertiesthengetbyidexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/js/regress/delete-a-few-properties-then-get-by-id-expected.txt (0 => 167889)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/js/regress/delete-a-few-properties-then-get-by-id-expected.txt                                (rev 0)
+++ trunk/LayoutTests/js/regress/delete-a-few-properties-then-get-by-id-expected.txt        2014-04-28 17:26:33 UTC (rev 167889)
</span><span class="lines">@@ -0,0 +1,10 @@
</span><ins>+JSRegress/delete-a-few-properties-then-get-by-id
+
+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestsjsregressdeleteafewpropertiesthengetbyidhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/js/regress/delete-a-few-properties-then-get-by-id.html (0 => 167889)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/js/regress/delete-a-few-properties-then-get-by-id.html                                (rev 0)
+++ trunk/LayoutTests/js/regress/delete-a-few-properties-then-get-by-id.html        2014-04-28 17:26:33 UTC (rev 167889)
</span><span class="lines">@@ -0,0 +1,12 @@
</span><ins>+&lt;!DOCTYPE HTML PUBLIC &quot;-//IETF//DTD HTML//EN&quot;&gt;
+&lt;html&gt;
+&lt;head&gt;
+&lt;script src=&quot;../../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+&lt;/head&gt;
+&lt;body&gt;
+&lt;script src=&quot;../../resources/regress-pre.js&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;script-tests/delete-a-few-properties-then-get-by-id.js&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;../../resources/regress-post.js&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;../../resources/js-test-post.js&quot;&gt;&lt;/script&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsjsregressscripttestsdeleteafewpropertiesthengetbyidjs"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/js/regress/script-tests/delete-a-few-properties-then-get-by-id.js (0 => 167889)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/js/regress/script-tests/delete-a-few-properties-then-get-by-id.js                                (rev 0)
+++ trunk/LayoutTests/js/regress/script-tests/delete-a-few-properties-then-get-by-id.js        2014-04-28 17:26:33 UTC (rev 167889)
</span><span class="lines">@@ -0,0 +1,21 @@
</span><ins>+function MyObject(x, y) {
+    this.x = x;
+    this.y = y;
+    this.deleteMe = &quot;delete me&quot;;
+}
+
+function foo(o) {
+    return o.x + o.y;
+}
+
+var niters = 100000;
+var sum = 0;
+var o = new MyObject(13, 42);
+delete o.deleteMe;
+
+for (var i = 0; i &lt; niters; ++i) {
+    sum += foo(o);
+}
+
+if (sum != 55 * niters)
+    throw new Error(&quot;Bad result!&quot;);
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (167888 => 167889)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2014-04-28 15:59:13 UTC (rev 167888)
+++ trunk/Source/JavaScriptCore/ChangeLog        2014-04-28 17:26:33 UTC (rev 167889)
</span><span class="lines">@@ -1,3 +1,42 @@
</span><ins>+2014-04-28  Mark Hahnenberg  &lt;mhahnenberg@apple.com&gt;
+
+        Deleting properties poisons objects
+        https://bugs.webkit.org/show_bug.cgi?id=131551
+
+        Reviewed by Oliver Hunt.
+
+        This is ~3% progression on Dromaeo with a ~6% progression on the jslib portion of Dromaeo in particular.
+
+        * runtime/JSPropertyNameIterator.cpp:
+        (JSC::JSPropertyNameIterator::create):
+        * runtime/PropertyMapHashTable.h:
+        (JSC::PropertyTable::hasDeletedOffset):
+        (JSC::PropertyTable::hadDeletedOffset): If we ever had deleted properties we can no longer cache offsets when 
+        iterating properties because we're required to iterate properties in insertion order.
+        * runtime/Structure.cpp:
+        (JSC::Structure::Structure):
+        (JSC::Structure::materializePropertyMap): We now re-use deleted properties when materializing the property map.
+        (JSC::Structure::removePropertyTransition): We allow up to 5 deletes for a particular path through the tree of 
+        Structure transitions. After that, we convert to an uncacheable dictionary like we used to. We don't cache 
+        delete transitions, but we allow transitioning from them.
+        (JSC::Structure::changePrototypeTransition):
+        (JSC::Structure::despecifyFunctionTransition):
+        (JSC::Structure::attributeChangeTransition):
+        (JSC::Structure::toDictionaryTransition):
+        (JSC::Structure::preventExtensionsTransition):
+        (JSC::Structure::addPropertyWithoutTransition):
+        (JSC::Structure::removePropertyWithoutTransition):
+        (JSC::Structure::pin): Now does only what it says it does--marks the property table as pinned.
+        (JSC::Structure::pinAndPreventTransitions): More descriptive version of what the old pin() was doing.
+        * runtime/Structure.h:
+        * runtime/StructureInlines.h:
+        (JSC::Structure::setEnumerationCache):
+        (JSC::Structure::hadDeletedOffsets):
+        (JSC::Structure::propertyTable):
+        (JSC::Structure::checkOffsetConsistency): Rearranged variables to be more sensible.
+        * tests/stress/for-in-after-delete.js: Added.
+        (foo):
+
</ins><span class="cx"> 2014-04-25  Andreas Kling  &lt;akling@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Inline (C++) GetByVal with numeric indices more aggressively.
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeJSPropertyNameIteratorcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/JSPropertyNameIterator.cpp (167888 => 167889)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/JSPropertyNameIterator.cpp        2014-04-28 15:59:13 UTC (rev 167888)
+++ trunk/Source/JavaScriptCore/runtime/JSPropertyNameIterator.cpp        2014-04-28 17:26:33 UTC (rev 167889)
</span><span class="lines">@@ -57,13 +57,14 @@
</span><span class="cx">     o-&gt;methodTable()-&gt;getPropertyNames(o, exec, propertyNames, ExcludeDontEnumProperties);
</span><span class="cx">     size_t numCacheableSlots = 0;
</span><span class="cx">     if (!o-&gt;structure()-&gt;hasNonEnumerableProperties() &amp;&amp; !o-&gt;structure()-&gt;hasGetterSetterProperties()
</span><del>-        &amp;&amp; !o-&gt;structure()-&gt;isUncacheableDictionary() &amp;&amp; !o-&gt;structure()-&gt;typeInfo().overridesGetPropertyNames())
</del><ins>+        &amp;&amp; !o-&gt;structure()-&gt;isUncacheableDictionary() &amp;&amp; !o-&gt;structure()-&gt;hadDeletedOffsets() &amp;&amp; !o-&gt;structure()-&gt;typeInfo().overridesGetPropertyNames())
</ins><span class="cx">         numCacheableSlots = propertyNames.numCacheableSlots();
</span><span class="cx">     
</span><span class="cx">     JSPropertyNameIterator* jsPropertyNameIterator = new (NotNull, allocateCell&lt;JSPropertyNameIterator&gt;(vm.heap)) JSPropertyNameIterator(exec, propertyNames.data(), numCacheableSlots);
</span><span class="cx">     jsPropertyNameIterator-&gt;finishCreation(vm, propertyNames.data(), o);
</span><span class="cx"> 
</span><del>-    if (o-&gt;structure()-&gt;isDictionary())
</del><ins>+    // JSPropertyNameIterator doesn't know how to skip deleted buckets, so just give up.
+    if (o-&gt;structure()-&gt;isDictionary() || o-&gt;structure()-&gt;hadDeletedOffsets())
</ins><span class="cx">         return jsPropertyNameIterator;
</span><span class="cx"> 
</span><span class="cx">     if (o-&gt;structure()-&gt;typeInfo().overridesGetPropertyNames())
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimePropertyMapHashTableh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/PropertyMapHashTable.h (167888 => 167889)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/PropertyMapHashTable.h        2014-04-28 15:59:13 UTC (rev 167888)
+++ trunk/Source/JavaScriptCore/runtime/PropertyMapHashTable.h        2014-04-28 17:26:33 UTC (rev 167889)
</span><span class="lines">@@ -189,7 +189,8 @@
</span><span class="cx"> 
</span><span class="cx">     // Used to maintain a list of unused entries in the property storage.
</span><span class="cx">     void clearDeletedOffsets();
</span><del>-    bool hasDeletedOffset();
</del><ins>+    bool hasDeletedOffset() const;
+    bool hadDeletedOffset() const; // Returns true if we ever had deleted properties.
</ins><span class="cx">     PropertyOffset getDeletedOffset();
</span><span class="cx">     void addDeletedOffset(PropertyOffset);
</span><span class="cx">     
</span><span class="lines">@@ -467,11 +468,16 @@
</span><span class="cx">     m_deletedOffsets.clear();
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-inline bool PropertyTable::hasDeletedOffset()
</del><ins>+inline bool PropertyTable::hasDeletedOffset() const
</ins><span class="cx"> {
</span><span class="cx">     return m_deletedOffsets &amp;&amp; !m_deletedOffsets-&gt;isEmpty();
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+inline bool PropertyTable::hadDeletedOffset() const
+{
+    return m_deletedOffsets;
+}
+
</ins><span class="cx"> inline PropertyOffset PropertyTable::getDeletedOffset()
</span><span class="cx"> {
</span><span class="cx">     PropertyOffset offset = m_deletedOffsets-&gt;last();
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeStructurecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/Structure.cpp (167888 => 167889)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/Structure.cpp        2014-04-28 15:59:13 UTC (rev 167888)
+++ trunk/Source/JavaScriptCore/runtime/Structure.cpp        2014-04-28 17:26:33 UTC (rev 167889)
</span><span class="lines">@@ -165,6 +165,7 @@
</span><span class="cx">     , m_transitionWatchpointSet(IsWatched)
</span><span class="cx">     , m_offset(invalidOffset)
</span><span class="cx">     , m_inlineCapacity(inlineCapacity)
</span><ins>+    , m_forgivenDeletes(0)
</ins><span class="cx">     , m_dictionaryKind(NoneDictionaryKind)
</span><span class="cx">     , m_isPinnedPropertyTable(false)
</span><span class="cx">     , m_hasGetterSetterProperties(classInfo-&gt;hasStaticSetterOrReadonlyProperties(vm))
</span><span class="lines">@@ -192,6 +193,7 @@
</span><span class="cx">     , m_transitionWatchpointSet(IsWatched)
</span><span class="cx">     , m_offset(invalidOffset)
</span><span class="cx">     , m_inlineCapacity(0)
</span><ins>+    , m_forgivenDeletes(0)
</ins><span class="cx">     , m_dictionaryKind(NoneDictionaryKind)
</span><span class="cx">     , m_isPinnedPropertyTable(false)
</span><span class="cx">     , m_hasGetterSetterProperties(m_classInfo-&gt;hasStaticSetterOrReadonlyProperties(vm))
</span><span class="lines">@@ -218,6 +220,7 @@
</span><span class="cx">     , m_transitionWatchpointSet(IsWatched)
</span><span class="cx">     , m_offset(invalidOffset)
</span><span class="cx">     , m_inlineCapacity(previous-&gt;m_inlineCapacity)
</span><ins>+    , m_forgivenDeletes(previous-&gt;m_forgivenDeletes)
</ins><span class="cx">     , m_dictionaryKind(previous-&gt;m_dictionaryKind)
</span><span class="cx">     , m_isPinnedPropertyTable(false)
</span><span class="cx">     , m_hasGetterSetterProperties(previous-&gt;m_hasGetterSetterProperties)
</span><span class="lines">@@ -310,7 +313,12 @@
</span><span class="cx">         structure = structures[i];
</span><span class="cx">         if (!structure-&gt;m_nameInPrevious)
</span><span class="cx">             continue;
</span><del>-        PropertyMapEntry entry(vm, this, structure-&gt;m_nameInPrevious.get(), structure-&gt;m_offset, structure-&gt;m_attributesInPrevious, structure-&gt;m_specificValueInPrevious.get());
</del><ins>+
+        PropertyMapEntry entry(vm, this, 
+            structure-&gt;m_nameInPrevious.get(), 
+            propertyTable()-&gt;nextOffset(m_inlineCapacity),
+            structure-&gt;m_attributesInPrevious, 
+            structure-&gt;m_specificValueInPrevious.get());
</ins><span class="cx">         propertyTable()-&gt;add(entry, m_offset, PropertyTable::PropertyOffsetMustNotChange);
</span><span class="cx">     }
</span><span class="cx">     
</span><span class="lines">@@ -458,10 +466,26 @@
</span><span class="cx"> {
</span><span class="cx">     ASSERT(!structure-&gt;isUncacheableDictionary());
</span><span class="cx"> 
</span><ins>+    if (structure-&gt;m_forgivenDeletes &lt; s_maxForgivenDeletes) {
+        Structure* transition = create(vm, structure);
+        ASSERT(!transition-&gt;enumerationCache());
+
+        DeferGC deferGC(vm.heap);
+        structure-&gt;materializePropertyMapIfNecessary(vm, deferGC);
+        transition-&gt;propertyTable().set(vm, transition, structure-&gt;copyPropertyTableForPinning(vm, transition));
+        transition-&gt;m_offset = structure-&gt;m_offset;
+        transition-&gt;pinAndPreventTransitions();
+
+        offset = transition-&gt;remove(propertyName);
+        ASSERT(offset != invalidOffset);
+        transition-&gt;m_forgivenDeletes = structure-&gt;m_forgivenDeletes + 1;
+
+        transition-&gt;checkOffsetConsistency();
+        return transition;
+    }
+
</ins><span class="cx">     Structure* transition = toUncacheableDictionaryTransition(vm, structure);
</span><del>-
</del><span class="cx">     offset = transition-&gt;remove(propertyName);
</span><del>-
</del><span class="cx">     transition-&gt;checkOffsetConsistency();
</span><span class="cx">     return transition;
</span><span class="cx"> }
</span><span class="lines">@@ -476,7 +500,7 @@
</span><span class="cx">     structure-&gt;materializePropertyMapIfNecessary(vm, deferGC);
</span><span class="cx">     transition-&gt;propertyTable().set(vm, transition, structure-&gt;copyPropertyTableForPinning(vm, transition));
</span><span class="cx">     transition-&gt;m_offset = structure-&gt;m_offset;
</span><del>-    transition-&gt;pin();
</del><ins>+    transition-&gt;pinAndPreventTransitions();
</ins><span class="cx"> 
</span><span class="cx">     transition-&gt;checkOffsetConsistency();
</span><span class="cx">     return transition;
</span><span class="lines">@@ -493,7 +517,7 @@
</span><span class="cx">     structure-&gt;materializePropertyMapIfNecessary(vm, deferGC);
</span><span class="cx">     transition-&gt;propertyTable().set(vm, transition, structure-&gt;copyPropertyTableForPinning(vm, transition));
</span><span class="cx">     transition-&gt;m_offset = structure-&gt;m_offset;
</span><del>-    transition-&gt;pin();
</del><ins>+    transition-&gt;pinAndPreventTransitions();
</ins><span class="cx"> 
</span><span class="cx">     if (transition-&gt;m_specificFunctionThrashCount == maxSpecificFunctionThrashCount)
</span><span class="cx">         transition-&gt;despecifyAllFunctions(vm);
</span><span class="lines">@@ -515,7 +539,7 @@
</span><span class="cx">         structure-&gt;materializePropertyMapIfNecessary(vm, deferGC);
</span><span class="cx">         transition-&gt;propertyTable().set(vm, transition, structure-&gt;copyPropertyTableForPinning(vm, transition));
</span><span class="cx">         transition-&gt;m_offset = structure-&gt;m_offset;
</span><del>-        transition-&gt;pin();
</del><ins>+        transition-&gt;pinAndPreventTransitions();
</ins><span class="cx">         
</span><span class="cx">         structure = transition;
</span><span class="cx">     }
</span><span class="lines">@@ -540,7 +564,7 @@
</span><span class="cx">     transition-&gt;propertyTable().set(vm, transition, structure-&gt;copyPropertyTableForPinning(vm, transition));
</span><span class="cx">     transition-&gt;m_offset = structure-&gt;m_offset;
</span><span class="cx">     transition-&gt;m_dictionaryKind = kind;
</span><del>-    transition-&gt;pin();
</del><ins>+    transition-&gt;pinAndPreventTransitions();
</ins><span class="cx"> 
</span><span class="cx">     transition-&gt;checkOffsetConsistency();
</span><span class="cx">     return transition;
</span><span class="lines">@@ -603,7 +627,7 @@
</span><span class="cx">     transition-&gt;propertyTable().set(vm, transition, structure-&gt;copyPropertyTableForPinning(vm, transition));
</span><span class="cx">     transition-&gt;m_offset = structure-&gt;m_offset;
</span><span class="cx">     transition-&gt;m_preventExtensions = true;
</span><del>-    transition-&gt;pin();
</del><ins>+    transition-&gt;pinAndPreventTransitions();
</ins><span class="cx"> 
</span><span class="cx">     transition-&gt;checkOffsetConsistency();
</span><span class="cx">     return transition;
</span><span class="lines">@@ -752,7 +776,7 @@
</span><span class="cx">     DeferGC deferGC(vm.heap);
</span><span class="cx">     materializePropertyMapIfNecessaryForPinning(vm, deferGC);
</span><span class="cx">     
</span><del>-    pin();
</del><ins>+    pinAndPreventTransitions();
</ins><span class="cx"> 
</span><span class="cx">     return putSpecificValue(vm, propertyName, attributes, specificValue);
</span><span class="cx"> }
</span><span class="lines">@@ -765,7 +789,7 @@
</span><span class="cx">     DeferGC deferGC(vm.heap);
</span><span class="cx">     materializePropertyMapIfNecessaryForPinning(vm, deferGC);
</span><span class="cx"> 
</span><del>-    pin();
</del><ins>+    pinAndPreventTransitions();
</ins><span class="cx">     return remove(propertyName);
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -773,6 +797,11 @@
</span><span class="cx"> {
</span><span class="cx">     ASSERT(propertyTable());
</span><span class="cx">     m_isPinnedPropertyTable = true;
</span><ins>+}
+
+void Structure::pinAndPreventTransitions()
+{
+    pin();
</ins><span class="cx">     clearPreviousID();
</span><span class="cx">     m_nameInPrevious.clear();
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeStructureh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/Structure.h (167888 => 167889)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/Structure.h        2014-04-28 15:59:13 UTC (rev 167888)
+++ trunk/Source/JavaScriptCore/runtime/Structure.h        2014-04-28 17:26:33 UTC (rev 167889)
</span><span class="lines">@@ -137,6 +137,8 @@
</span><span class="cx">     PropertyOffset removePropertyWithoutTransition(VM&amp;, PropertyName);
</span><span class="cx">     void setPrototypeWithoutTransition(VM&amp; vm, JSValue prototype) { m_prototype.set(vm, this, prototype); }
</span><span class="cx">         
</span><ins>+    bool hadDeletedOffsets() const;
+
</ins><span class="cx">     bool isDictionary() const { return m_dictionaryKind != NoneDictionaryKind; }
</span><span class="cx">     bool isUncacheableDictionary() const { return m_dictionaryKind == UncachedDictionaryKind; }
</span><span class="cx"> 
</span><span class="lines">@@ -411,6 +413,7 @@
</span><span class="cx">     void despecifyAllFunctions(VM&amp;);
</span><span class="cx"> 
</span><span class="cx">     WriteBarrier&lt;PropertyTable&gt;&amp; propertyTable();
</span><ins>+    const WriteBarrier&lt;PropertyTable&gt;&amp; propertyTable() const;
</ins><span class="cx">     PropertyTable* takePropertyTableOrCloneIfPinned(VM&amp;, Structure* owner);
</span><span class="cx">     PropertyTable* copyPropertyTable(VM&amp;, Structure* owner);
</span><span class="cx">     PropertyTable* copyPropertyTableForPinning(VM&amp;, Structure* owner);
</span><span class="lines">@@ -457,6 +460,7 @@
</span><span class="cx">     bool isValid(ExecState*, StructureChain* cachedPrototypeChain) const;
</span><span class="cx">         
</span><span class="cx">     void pin();
</span><ins>+    void pinAndPreventTransitions();
</ins><span class="cx"> 
</span><span class="cx">     Structure* previous() const
</span><span class="cx">     {
</span><span class="lines">@@ -512,6 +516,9 @@
</span><span class="cx">     
</span><span class="cx">     ConcurrentJITLock m_lock;
</span><span class="cx">     
</span><ins>+    static const unsigned s_maxForgivenDeletes = 5;
+    unsigned m_forgivenDeletes;
+
</ins><span class="cx">     unsigned m_dictionaryKind : 2;
</span><span class="cx">     bool m_isPinnedPropertyTable : 1;
</span><span class="cx">     bool m_hasGetterSetterProperties : 1;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeStructureInlinesh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/StructureInlines.h (167888 => 167889)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/StructureInlines.h        2014-04-28 15:59:13 UTC (rev 167888)
+++ trunk/Source/JavaScriptCore/runtime/StructureInlines.h        2014-04-28 17:26:33 UTC (rev 167889)
</span><span class="lines">@@ -135,6 +135,7 @@
</span><span class="cx"> inline void Structure::setEnumerationCache(VM&amp; vm, JSPropertyNameIterator* enumerationCache)
</span><span class="cx"> {
</span><span class="cx">     ASSERT(!isDictionary());
</span><ins>+    ASSERT(!hadDeletedOffsets());
</ins><span class="cx">     if (!typeInfo().structureHasRareData())
</span><span class="cx">         allocateRareData(vm);
</span><span class="cx">     rareData()-&gt;setEnumerationCache(vm, this, enumerationCache);
</span><span class="lines">@@ -217,8 +218,21 @@
</span><span class="cx">     return propertyTable()-&gt;size() == totalStorageCapacity();
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-ALWAYS_INLINE WriteBarrier&lt;PropertyTable&gt;&amp; Structure::propertyTable()
</del><ins>+inline bool Structure::hadDeletedOffsets() const
</ins><span class="cx"> {
</span><ins>+    // If we had deleted anything then we would have pinned our property table.
+    if (!propertyTable())
+        return false;
+    return propertyTable()-&gt;hadDeletedOffset();
+}
+
+inline WriteBarrier&lt;PropertyTable&gt;&amp; Structure::propertyTable()
+{
+    return const_cast&lt;WriteBarrier&lt;PropertyTable&gt;&amp;&gt;(static_cast&lt;const Structure*&gt;(this)-&gt;propertyTable());
+}
+
+inline const WriteBarrier&lt;PropertyTable&gt;&amp; Structure::propertyTable() const
+{
</ins><span class="cx">     ASSERT(!globalObject() || !globalObject()-&gt;vm().heap.isCollecting());
</span><span class="cx">     return m_propertyTableUnsafe;
</span><span class="cx"> }
</span><span class="lines">@@ -239,8 +253,8 @@
</span><span class="cx">     if (isCompilationThread())
</span><span class="cx">         return true;
</span><span class="cx">     
</span><del>-    RELEASE_ASSERT(numberOfSlotsForLastOffset(m_offset, m_inlineCapacity) == propertyTable-&gt;propertyStorageSize());
</del><span class="cx">     unsigned totalSize = propertyTable-&gt;propertyStorageSize();
</span><ins>+    RELEASE_ASSERT(numberOfSlotsForLastOffset(m_offset, m_inlineCapacity) == totalSize);
</ins><span class="cx">     RELEASE_ASSERT((totalSize &lt; inlineCapacity() ? 0 : totalSize - inlineCapacity()) == numberOfOutOfLineSlotsForLastOffset(m_offset));
</span><span class="cx"> 
</span><span class="cx">     return true;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoretestsstressforinafterdeletejs"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/tests/stress/for-in-after-delete.js (0 => 167889)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/stress/for-in-after-delete.js                                (rev 0)
+++ trunk/Source/JavaScriptCore/tests/stress/for-in-after-delete.js        2014-04-28 17:26:33 UTC (rev 167889)
</span><span class="lines">@@ -0,0 +1,21 @@
</span><ins>+var foo = function() {
+    var o = {};
+    o.x = &quot;foo&quot;;
+    o.y = 1;
+    
+    delete o.x;
+    
+    o.z = 2;
+    
+    var result = null;
+    var i = 0;
+    for (var p in o) {
+        if (result === null)
+            result = o[p];
+    }
+    
+    if (result !== 1)
+        throw new Error(&quot;Incorrect result: &quot; + result + &quot; (expected 1)&quot;);
+};
+
+foo();
</ins></span></pre>
</div>
</div>

</body>
</html>