<!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>[282014] 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/282014">282014</a></dd>
<dt>Author</dt> <dd>ysuzuki@apple.com</dd>
<dt>Date</dt> <dd>2021-09-03 12:46:40 -0700 (Fri, 03 Sep 2021)</dd>
</dl>

<h3>Log Message</h3>
<pre>[JSC] Validate JSPropertyNameEnumerator via watchpoints
https://bugs.webkit.org/show_bug.cgi?id=229846

Reviewed by Keith Miller.

JSTests:

* stress/for-in-cacheable-dictionary.js: Added.
(shouldBe):
(collect):
* stress/for-in-invalidate.js: Added.
(shouldBe):
(collect):
* stress/for-in-uncacheable-dictionary.js: Added.
(shouldBe):
(collect):

Source/JavaScriptCore:

Looked into Elm-TodoMVC sampling profiler data and found that op_get_property_enumerator is taking enough amount of time.
And Instruments say validating JSPropertyNameEnumerator via traversing StructureChain is costly.
We are caching JSPropertyNameEnumerator only when we meet the condition: objects in prototype chain can ensure identity of
property names if structure is not changed. So we can use watchpoint based approach to invalidate JSPropertyNameEnumerator.

This patch injects structure transition watchpoints if possible. And when watchpoint is fired, we invalidate JSPropertyNameEnumerator
cached in StructureRareData, as if we are ensuring prototype chain condition for the other property accesses.

This offers 0.6% improvement in Speedometer2.

----------------------------------------------------------------------------------------------------------------------------------
|               subtest                |     ms      |     ms      |  b / a   | pValue (significance using False Discovery Rate) |
----------------------------------------------------------------------------------------------------------------------------------
| Elm-TodoMVC                          |121.971667   |117.725000   |0.965183  | 0.000000 (significant)                           |
| VueJS-TodoMVC                        |26.246667    |26.035000    |0.991935  | 0.360614                                         |
| EmberJS-TodoMVC                      |126.196667   |126.653333   |1.003619  | 0.103138                                         |
| BackboneJS-TodoMVC                   |48.976667    |48.881667    |0.998060  | 0.474106                                         |
| Preact-TodoMVC                       |20.118333    |20.115000    |0.999834  | 0.989038                                         |
| AngularJS-TodoMVC                    |131.545000   |130.706667   |0.993627  | 0.015344 (significant)                           |
| Vanilla-ES2015-TodoMVC               |63.725000    |63.773333    |1.000758  | 0.706560                                         |
| Inferno-TodoMVC                      |64.231667    |62.653333    |0.975427  | 0.000000 (significant)                           |
| Flight-TodoMVC                       |77.223333    |77.690000    |1.006043  | 0.268309                                         |
| Angular2-TypeScript-TodoMVC          |39.686667    |39.500000    |0.995296  | 0.499678                                         |
| VanillaJS-TodoMVC                    |52.321667    |51.973333    |0.993342  | 0.077777                                         |
| jQuery-TodoMVC                       |224.908333   |225.761667   |1.003794  | 0.022136                                         |
| EmberJS-Debug-TodoMVC                |339.858333   |339.886667   |1.000083  | 0.950320                                         |
| React-TodoMVC                        |86.545000    |86.070000    |0.994512  | 0.001518 (significant)                           |
| React-Redux-TodoMVC                  |146.010000   |142.855000   |0.978392  | 0.000000 (significant)                           |
| Vanilla-ES2015-Babel-Webpack-TodoMVC |61.411667    |61.456667    |1.000733  | 0.631499                                         |
----------------------------------------------------------------------------------------------------------------------------------
a mean = 255.96543
b mean = 257.53379
pValue = 0.0000034394
(Bigger means are better.)
1.006 times better
Results ARE significant

* bytecode/Watchpoint.cpp:
* bytecode/Watchpoint.h:
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileGetPropertyEnumerator):
* ftl/FTLAbstractHeapRepository.h:
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileCompareStrictEq):
* runtime/JSPropertyNameEnumerator.h:
(JSC::propertyNameEnumerator):
* runtime/StructureRareData.h:
* runtime/StructureRareDataInlines.h:
(JSC::StructureRareData::setCachedPropertyNameEnumerator):
(JSC::StructureChainInvalidationWatchpoint::install):
(JSC::StructureChainInvalidationWatchpoint::fireInternal):
(JSC::StructureRareData::tryCachePropertyNameEnumeratorViaWatchpoint):
(JSC::StructureRareData::invalidateWatchpointBasedValidation):
* tools/JSDollarVM.cpp:
(JSC::JSC_DEFINE_HOST_FUNCTION):
(JSC::JSDollarVM::finishCreation):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkJSTestsChangeLog">trunk/JSTests/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCorebytecodeWatchpointcpp">trunk/Source/JavaScriptCore/bytecode/Watchpoint.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCorebytecodeWatchpointh">trunk/Source/JavaScriptCore/bytecode/Watchpoint.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGByteCodeParsercpp">trunk/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGNodeh">trunk/Source/JavaScriptCore/dfg/DFGNode.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGOperationscpp">trunk/Source/JavaScriptCore/dfg/DFGOperations.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGSpeculativeJITcpp">trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLAbstractHeapRepositoryh">trunk/Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLLowerDFGToB3cpp">trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCorejitJITPropertyAccesscpp">trunk/Source/JavaScriptCore/jit/JITPropertyAccess.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeCommonSlowPathscpp">trunk/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeJSPropertyNameEnumeratorcpp">trunk/Source/JavaScriptCore/runtime/JSPropertyNameEnumerator.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeJSPropertyNameEnumeratorh">trunk/Source/JavaScriptCore/runtime/JSPropertyNameEnumerator.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeStructureh">trunk/Source/JavaScriptCore/runtime/Structure.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeStructureRareDatah">trunk/Source/JavaScriptCore/runtime/StructureRareData.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeStructureRareDataInlinesh">trunk/Source/JavaScriptCore/runtime/StructureRareDataInlines.h</a></li>
<li><a href="#trunkSourceJavaScriptCoretoolsJSDollarVMcpp">trunk/Source/JavaScriptCore/tools/JSDollarVM.cpp</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkJSTestsstressforincacheabledictionaryjs">trunk/JSTests/stress/for-in-cacheable-dictionary.js</a></li>
<li><a href="#trunkJSTestsstressforininvalidatejs">trunk/JSTests/stress/for-in-invalidate.js</a></li>
<li><a href="#trunkJSTestsstressforinuncacheabledictionaryjs">trunk/JSTests/stress/for-in-uncacheable-dictionary.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkJSTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/JSTests/ChangeLog (282013 => 282014)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/ChangeLog  2021-09-03 19:26:19 UTC (rev 282013)
+++ trunk/JSTests/ChangeLog     2021-09-03 19:46:40 UTC (rev 282014)
</span><span class="lines">@@ -1,3 +1,20 @@
</span><ins>+2021-09-02  Yusuke Suzuki  <ysuzuki@apple.com>
+
+        [JSC] Validate JSPropertyNameEnumerator via watchpoints
+        https://bugs.webkit.org/show_bug.cgi?id=229846
+
+        Reviewed by Keith Miller.
+
+        * stress/for-in-cacheable-dictionary.js: Added.
+        (shouldBe):
+        (collect):
+        * stress/for-in-invalidate.js: Added.
+        (shouldBe):
+        (collect):
+        * stress/for-in-uncacheable-dictionary.js: Added.
+        (shouldBe):
+        (collect):
+
</ins><span class="cx"> 2021-08-31  Ross Kirsling  <ross.kirsling@sony.com>
</span><span class="cx"> 
</span><span class="cx">         [JSC] Implement Temporal.Duration
</span></span></pre></div>
<a id="trunkJSTestsstressforincacheabledictionaryjs"></a>
<div class="addfile"><h4>Added: trunk/JSTests/stress/for-in-cacheable-dictionary.js (0 => 282014)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/stress/for-in-cacheable-dictionary.js                              (rev 0)
+++ trunk/JSTests/stress/for-in-cacheable-dictionary.js 2021-09-03 19:46:40 UTC (rev 282014)
</span><span class="lines">@@ -0,0 +1,34 @@
</span><ins>+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+let object = {
+    a: 42,
+    b: 44,
+    c: 43,
+    d: 44,
+};
+
+function collect(object)
+{
+    let array = [];
+    for (let key in object)
+        array.push(key);
+    return array;
+}
+
+let array1 = collect(object);
+let array2 = collect(object);
+$vm.toCacheableDictionary(object);
+object.e = 44;
+let array3 = collect(object);
+let array4 = collect(object);
+object.f = 44;
+let array5 = collect(object);
+
+shouldBe(JSON.stringify(array1), `["a","b","c","d"]`);
+shouldBe(JSON.stringify(array2), `["a","b","c","d"]`);
+shouldBe(JSON.stringify(array3), `["a","b","c","d","e"]`);
+shouldBe(JSON.stringify(array4), `["a","b","c","d","e"]`);
+shouldBe(JSON.stringify(array5), `["a","b","c","d","e","f"]`);
</ins></span></pre></div>
<a id="trunkJSTestsstressforininvalidatejs"></a>
<div class="addfile"><h4>Added: trunk/JSTests/stress/for-in-invalidate.js (0 => 282014)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/stress/for-in-invalidate.js                                (rev 0)
+++ trunk/JSTests/stress/for-in-invalidate.js   2021-09-03 19:46:40 UTC (rev 282014)
</span><span class="lines">@@ -0,0 +1,48 @@
</span><ins>+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+let object = {
+    a: 42,
+    b: 44,
+    c: 43,
+    d: 44,
+};
+
+function collect(object)
+{
+    let array = [];
+    for (let key in object)
+        array.push(key);
+    return array;
+}
+
+let array1 = collect(object);
+let array2 = collect(object);
+object.e = 44;
+let array3 = collect(object);
+let array4 = collect(object);
+object.f = 44;
+let array5 = collect(object);
+let array6 = collect(object);
+let proto = { Hello: 33 };
+object.__proto__ = proto;
+let array7 = collect(object);
+let array8 = collect(object);
+proto.values = 33;
+let array9 = collect(object);
+let array10 = collect(object);
+let array11 = collect(object);
+
+shouldBe(JSON.stringify(array1), `["a","b","c","d"]`);
+shouldBe(JSON.stringify(array2), `["a","b","c","d"]`);
+shouldBe(JSON.stringify(array3), `["a","b","c","d","e"]`);
+shouldBe(JSON.stringify(array4), `["a","b","c","d","e"]`);
+shouldBe(JSON.stringify(array5), `["a","b","c","d","e","f"]`);
+shouldBe(JSON.stringify(array6), `["a","b","c","d","e","f"]`);
+shouldBe(JSON.stringify(array7), `["a","b","c","d","e","f","Hello"]`);
+shouldBe(JSON.stringify(array8), `["a","b","c","d","e","f","Hello"]`);
+shouldBe(JSON.stringify(array9), `["a","b","c","d","e","f","Hello","values"]`);
+shouldBe(JSON.stringify(array10), `["a","b","c","d","e","f","Hello","values"]`);
+shouldBe(JSON.stringify(array11), `["a","b","c","d","e","f","Hello","values"]`);
</ins></span></pre></div>
<a id="trunkJSTestsstressforinuncacheabledictionaryjs"></a>
<div class="addfile"><h4>Added: trunk/JSTests/stress/for-in-uncacheable-dictionary.js (0 => 282014)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/stress/for-in-uncacheable-dictionary.js                            (rev 0)
+++ trunk/JSTests/stress/for-in-uncacheable-dictionary.js       2021-09-03 19:46:40 UTC (rev 282014)
</span><span class="lines">@@ -0,0 +1,34 @@
</span><ins>+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+let object = {
+    a: 42,
+    b: 44,
+    c: 43,
+    d: 44,
+};
+
+function collect(object)
+{
+    let array = [];
+    for (let key in object)
+        array.push(key);
+    return array;
+}
+
+let array1 = collect(object);
+let array2 = collect(object);
+$vm.toUncacheableDictionary(object);
+object.e = 44;
+let array3 = collect(object);
+let array4 = collect(object);
+object.f = 44;
+let array5 = collect(object);
+
+shouldBe(JSON.stringify(array1), `["a","b","c","d"]`);
+shouldBe(JSON.stringify(array2), `["a","b","c","d"]`);
+shouldBe(JSON.stringify(array3), `["a","b","c","d","e"]`);
+shouldBe(JSON.stringify(array4), `["a","b","c","d","e"]`);
+shouldBe(JSON.stringify(array5), `["a","b","c","d","e","f"]`);
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (282013 => 282014)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog    2021-09-03 19:26:19 UTC (rev 282013)
+++ trunk/Source/JavaScriptCore/ChangeLog       2021-09-03 19:46:40 UTC (rev 282014)
</span><span class="lines">@@ -1,3 +1,67 @@
</span><ins>+2021-09-02  Yusuke Suzuki  <ysuzuki@apple.com>
+
+        [JSC] Validate JSPropertyNameEnumerator via watchpoints
+        https://bugs.webkit.org/show_bug.cgi?id=229846
+
+        Reviewed by Keith Miller.
+
+        Looked into Elm-TodoMVC sampling profiler data and found that op_get_property_enumerator is taking enough amount of time.
+        And Instruments say validating JSPropertyNameEnumerator via traversing StructureChain is costly.
+        We are caching JSPropertyNameEnumerator only when we meet the condition: objects in prototype chain can ensure identity of
+        property names if structure is not changed. So we can use watchpoint based approach to invalidate JSPropertyNameEnumerator.
+
+        This patch injects structure transition watchpoints if possible. And when watchpoint is fired, we invalidate JSPropertyNameEnumerator
+        cached in StructureRareData, as if we are ensuring prototype chain condition for the other property accesses.
+
+        This offers 0.6% improvement in Speedometer2.
+
+        ----------------------------------------------------------------------------------------------------------------------------------
+        |               subtest                |     ms      |     ms      |  b / a   | pValue (significance using False Discovery Rate) |
+        ----------------------------------------------------------------------------------------------------------------------------------
+        | Elm-TodoMVC                          |121.971667   |117.725000   |0.965183  | 0.000000 (significant)                           |
+        | VueJS-TodoMVC                        |26.246667    |26.035000    |0.991935  | 0.360614                                         |
+        | EmberJS-TodoMVC                      |126.196667   |126.653333   |1.003619  | 0.103138                                         |
+        | BackboneJS-TodoMVC                   |48.976667    |48.881667    |0.998060  | 0.474106                                         |
+        | Preact-TodoMVC                       |20.118333    |20.115000    |0.999834  | 0.989038                                         |
+        | AngularJS-TodoMVC                    |131.545000   |130.706667   |0.993627  | 0.015344 (significant)                           |
+        | Vanilla-ES2015-TodoMVC               |63.725000    |63.773333    |1.000758  | 0.706560                                         |
+        | Inferno-TodoMVC                      |64.231667    |62.653333    |0.975427  | 0.000000 (significant)                           |
+        | Flight-TodoMVC                       |77.223333    |77.690000    |1.006043  | 0.268309                                         |
+        | Angular2-TypeScript-TodoMVC          |39.686667    |39.500000    |0.995296  | 0.499678                                         |
+        | VanillaJS-TodoMVC                    |52.321667    |51.973333    |0.993342  | 0.077777                                         |
+        | jQuery-TodoMVC                       |224.908333   |225.761667   |1.003794  | 0.022136                                         |
+        | EmberJS-Debug-TodoMVC                |339.858333   |339.886667   |1.000083  | 0.950320                                         |
+        | React-TodoMVC                        |86.545000    |86.070000    |0.994512  | 0.001518 (significant)                           |
+        | React-Redux-TodoMVC                  |146.010000   |142.855000   |0.978392  | 0.000000 (significant)                           |
+        | Vanilla-ES2015-Babel-Webpack-TodoMVC |61.411667    |61.456667    |1.000733  | 0.631499                                         |
+        ----------------------------------------------------------------------------------------------------------------------------------
+        a mean = 255.96543
+        b mean = 257.53379
+        pValue = 0.0000034394
+        (Bigger means are better.)
+        1.006 times better
+        Results ARE significant
+
+        * bytecode/Watchpoint.cpp:
+        * bytecode/Watchpoint.h:
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileGetPropertyEnumerator):
+        * ftl/FTLAbstractHeapRepository.h:
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileCompareStrictEq):
+        * runtime/JSPropertyNameEnumerator.h:
+        (JSC::propertyNameEnumerator):
+        * runtime/StructureRareData.h:
+        * runtime/StructureRareDataInlines.h:
+        (JSC::StructureRareData::setCachedPropertyNameEnumerator):
+        (JSC::StructureChainInvalidationWatchpoint::install):
+        (JSC::StructureChainInvalidationWatchpoint::fireInternal):
+        (JSC::StructureRareData::tryCachePropertyNameEnumeratorViaWatchpoint):
+        (JSC::StructureRareData::invalidateWatchpointBasedValidation):
+        * tools/JSDollarVM.cpp:
+        (JSC::JSC_DEFINE_HOST_FUNCTION):
+        (JSC::JSDollarVM::finishCreation):
+
</ins><span class="cx"> 2021-09-03  Yusuke Suzuki  <ysuzuki@apple.com>
</span><span class="cx"> 
</span><span class="cx">         [JSC] Fix WebKitAdditions directory traversal in offlineasm part 2
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorebytecodeWatchpointcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/bytecode/Watchpoint.cpp (282013 => 282014)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/bytecode/Watchpoint.cpp      2021-09-03 19:26:19 UTC (rev 282013)
+++ trunk/Source/JavaScriptCore/bytecode/Watchpoint.cpp 2021-09-03 19:46:40 UTC (rev 282014)
</span><span class="lines">@@ -33,6 +33,7 @@
</span><span class="cx"> #include "FunctionRareData.h"
</span><span class="cx"> #include "HeapInlines.h"
</span><span class="cx"> #include "LLIntPrototypeLoadAdaptiveStructureWatchpoint.h"
</span><ins>+#include "StructureRareDataInlines.h"
</ins><span class="cx"> #include "StructureStubClearingWatchpoint.h"
</span><span class="cx"> #include "VM.h"
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorebytecodeWatchpointh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/bytecode/Watchpoint.h (282013 => 282014)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/bytecode/Watchpoint.h        2021-09-03 19:26:19 UTC (rev 282013)
+++ trunk/Source/JavaScriptCore/bytecode/Watchpoint.h   2021-09-03 19:46:40 UTC (rev 282014)
</span><span class="lines">@@ -108,7 +108,8 @@
</span><span class="cx">     macro(CodeBlockJettisoning, CodeBlockJettisoningWatchpoint) \
</span><span class="cx">     macro(LLIntPrototypeLoadAdaptiveStructure, LLIntPrototypeLoadAdaptiveStructureWatchpoint) \
</span><span class="cx">     macro(FunctionRareDataAllocationProfileClearing, FunctionRareData::AllocationProfileClearingWatchpoint) \
</span><del>-    macro(CachedSpecialPropertyAdaptiveStructure, CachedSpecialPropertyAdaptiveStructureWatchpoint)
</del><ins>+    macro(CachedSpecialPropertyAdaptiveStructure, CachedSpecialPropertyAdaptiveStructureWatchpoint) \
+    macro(StructureChainInvalidation, StructureChainInvalidationWatchpoint) \
</ins><span class="cx"> 
</span><span class="cx"> #if ENABLE(JIT)
</span><span class="cx"> #define JSC_WATCHPOINT_TYPES_WITHOUT_DFG(macro) \
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGByteCodeParsercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp (282013 => 282014)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp    2021-09-03 19:26:19 UTC (rev 282013)
+++ trunk/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp       2021-09-03 19:46:40 UTC (rev 282014)
</span><span class="lines">@@ -8416,7 +8416,7 @@
</span><span class="cx">             Node* enumerator = get(bytecode.m_enumerator);
</span><span class="cx">             Node* mode = get(bytecode.m_mode);
</span><span class="cx"> 
</span><del>-            auto seenModes = OptionSet<JSPropertyNameEnumerator::Mode>::fromRaw(metadata.m_enumeratorMetadata);
</del><ins>+            auto seenModes = OptionSet<JSPropertyNameEnumerator::Flag>::fromRaw(metadata.m_enumeratorMetadata);
</ins><span class="cx"> 
</span><span class="cx">             if (!seenModes)
</span><span class="cx">                 addToGraph(ForceOSRExit);
</span><span class="lines">@@ -8451,7 +8451,7 @@
</span><span class="cx">             Node* mode = get(bytecode.m_mode);
</span><span class="cx">             Node* enumerator = get(bytecode.m_enumerator);
</span><span class="cx"> 
</span><del>-            auto seenModes = OptionSet<JSPropertyNameEnumerator::Mode>::fromRaw(metadata.m_enumeratorMetadata);
</del><ins>+            auto seenModes = OptionSet<JSPropertyNameEnumerator::Flag>::fromRaw(metadata.m_enumeratorMetadata);
</ins><span class="cx">             if (!seenModes)
</span><span class="cx">                 addToGraph(ForceOSRExit);
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGNodeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGNode.h (282013 => 282014)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGNode.h        2021-09-03 19:26:19 UTC (rev 282013)
+++ trunk/Source/JavaScriptCore/dfg/DFGNode.h   2021-09-03 19:46:40 UTC (rev 282014)
</span><span class="lines">@@ -3267,10 +3267,10 @@
</span><span class="cx">         }
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    OptionSet<JSPropertyNameEnumerator::Mode> enumeratorMetadata()
</del><ins>+    OptionSet<JSPropertyNameEnumerator::Flag> enumeratorMetadata()
</ins><span class="cx">     {
</span><span class="cx">         ASSERT(hasEnumeratorMetadata());
</span><del>-        return OptionSet<JSPropertyNameEnumerator::Mode>::fromRaw(m_opInfo2.as<unsigned>());
</del><ins>+        return OptionSet<JSPropertyNameEnumerator::Flag>::fromRaw(m_opInfo2.as<unsigned>());
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     void dumpChildren(PrintStream& out)
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGOperationscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGOperations.cpp (282013 => 282014)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGOperations.cpp        2021-09-03 19:26:19 UTC (rev 282013)
+++ trunk/Source/JavaScriptCore/dfg/DFGOperations.cpp   2021-09-03 19:46:40 UTC (rev 282014)
</span><span class="lines">@@ -2462,7 +2462,7 @@
</span><span class="cx">     JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
</span><span class="cx">     auto scope = DECLARE_THROW_SCOPE(vm);
</span><span class="cx"> 
</span><del>-    JSPropertyNameEnumerator::Mode mode = static_cast<JSPropertyNameEnumerator::Mode>(modeNumber);
</del><ins>+    JSPropertyNameEnumerator::Flag mode = static_cast<JSPropertyNameEnumerator::Flag>(modeNumber);
</ins><span class="cx">     if (JSValue::decode(baseValue).isUndefinedOrNull()) {
</span><span class="cx">         ASSERT(mode == JSPropertyNameEnumerator::InitMode);
</span><span class="cx">         ASSERT(!index);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGSpeculativeJITcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp (282013 => 282014)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp    2021-09-03 19:26:19 UTC (rev 282013)
+++ trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp       2021-09-03 19:46:40 UTC (rev 282014)
</span><span class="lines">@@ -13510,7 +13510,8 @@
</span><span class="cx">         m_jit.load32(MacroAssembler::Address(baseGPR, JSCell::structureIDOffset()), resultRegs.payloadGPR());
</span><span class="cx">         speculationCheck(BadCache, JSValueSource(), node, m_jit.branch32(MacroAssembler::NotEqual, resultRegs.payloadGPR(), MacroAssembler::Address(enumeratorGPR, JSPropertyNameEnumerator::cachedStructureIDOffset())));
</span><span class="cx"> 
</span><del>-        m_jit.load32(MacroAssembler::Address(enumeratorGPR, JSPropertyNameEnumerator::modeSetOffset()), resultRegs.payloadGPR());
</del><ins>+        m_jit.load32(MacroAssembler::Address(enumeratorGPR, JSPropertyNameEnumerator::flagsOffset()), resultRegs.payloadGPR());
+        m_jit.and32(TrustedImm32(JSPropertyNameEnumerator::enumerationModeMask), resultRegs.payloadGPR());
</ins><span class="cx">         speculationCheck(BadCache, JSValueSource(), node, m_jit.branch32(MacroAssembler::NotEqual, TrustedImm32(JSPropertyNameEnumerator::OwnStructureMode), resultRegs.payloadGPR()));
</span><span class="cx"> 
</span><span class="cx">         m_jit.move(indexGPR, resultRegs.payloadGPR());
</span><span class="lines">@@ -13944,14 +13945,44 @@
</span><span class="cx"> {
</span><span class="cx">     if (node->child1().useKind() == CellUse) {
</span><span class="cx">         SpeculateCellOperand base(this, node->child1());
</span><ins>+        GPRTemporary scratch1(this);
+        GPRTemporary scratch2(this);
+
</ins><span class="cx">         GPRReg baseGPR = base.gpr();
</span><ins>+        GPRReg scratch1GPR = scratch1.gpr();
+        GPRReg scratch2GPR = scratch2.gpr();
</ins><span class="cx"> 
</span><del>-        flushRegisters();
-        GPRFlushedCallResult result(this);
-        GPRReg resultGPR = result.gpr();
-        callOperation(operationGetPropertyEnumeratorCell, resultGPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseGPR);
</del><ins>+        CCallHelpers::JumpList slowCases;
+
+        // We go to the inlined fast path if the object is UndecidedShape / NoIndexingShape for simplicity.
+        static_assert(!NonArray);
+        static_assert(ArrayClass == 1);
+        static_assert(UndecidedShape == 2);
+        static_assert(ArrayWithUndecided == 3);
+        static_assert(NonArray <= ArrayWithUndecided);
+        static_assert(ArrayClass <= ArrayWithUndecided);
+        static_assert(ArrayWithUndecided <= ArrayWithUndecided);
+        m_jit.load8(CCallHelpers::Address(baseGPR, JSCell::indexingTypeAndMiscOffset()), scratch1GPR);
+        m_jit.and32(CCallHelpers::TrustedImm32(IndexingTypeMask), scratch1GPR);
+        slowCases.append(m_jit.branch32(CCallHelpers::Above, scratch1GPR, CCallHelpers::TrustedImm32(ArrayWithUndecided)));
+        m_jit.emitLoadStructure(vm(), baseGPR, scratch1GPR, scratch2GPR);
+        m_jit.loadPtr(CCallHelpers::Address(scratch1GPR, Structure::previousOrRareDataOffset()), scratch1GPR);
+
+        slowCases.append(m_jit.branchTestPtr(CCallHelpers::Zero, scratch1GPR));
+        slowCases.append(m_jit.branch32(CCallHelpers::Equal, CCallHelpers::Address(scratch1GPR, JSCell::structureIDOffset()), TrustedImm32(bitwise_cast<int32_t>(vm().structureStructure->structureID()))));
+        m_jit.loadPtr(CCallHelpers::Address(scratch1GPR, StructureRareData::offsetOfCachedPropertyNameEnumerator()), scratch1GPR);
+        slowCases.append(m_jit.branchTestPtr(CCallHelpers::Zero, scratch1GPR));
+        slowCases.append(m_jit.branchTest32(CCallHelpers::Zero, CCallHelpers::Address(scratch1GPR, JSPropertyNameEnumerator::flagsOffset()), CCallHelpers::TrustedImm32(JSPropertyNameEnumerator::ValidatedViaWatchpoint)));
+        auto done = m_jit.jump();
+
+        slowCases.link(&m_jit);
+        silentSpillAllRegisters(scratch1GPR, scratch2GPR);
+        callOperation(operationGetPropertyEnumeratorCell, scratch1GPR, TrustedImmPtr::weakPointer(m_graph, m_graph.globalObjectFor(node->origin.semantic)), baseGPR);
+        silentFillAllRegisters();
</ins><span class="cx">         m_jit.exceptionCheck();
</span><del>-        cellResult(resultGPR, node);
</del><ins>+
+        done.link(&m_jit);
+        cellResult(scratch1GPR, node);
</ins><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLAbstractHeapRepositoryh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.h (282013 => 282014)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.h      2021-09-03 19:26:19 UTC (rev 282013)
+++ trunk/Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.h 2021-09-03 19:46:40 UTC (rev 282014)
</span><span class="lines">@@ -110,11 +110,12 @@
</span><span class="cx">     macro(JSObject_butterfly, JSObject::butterflyOffset()) \
</span><span class="cx">     macro(JSPropertyNameEnumerator_cachedInlineCapacity, JSPropertyNameEnumerator::cachedInlineCapacityOffset()) \
</span><span class="cx">     macro(JSPropertyNameEnumerator_cachedPropertyNamesVector, JSPropertyNameEnumerator::cachedPropertyNamesVectorOffset()) \
</span><ins>+    macro(JSPropertyNameEnumerator_cachedPrototypeChain, JSPropertyNameEnumerator::offsetOfCachedPrototypeChain()) \
</ins><span class="cx">     macro(JSPropertyNameEnumerator_cachedStructureID, JSPropertyNameEnumerator::cachedStructureIDOffset()) \
</span><span class="cx">     macro(JSPropertyNameEnumerator_endGenericPropertyIndex, JSPropertyNameEnumerator::endGenericPropertyIndexOffset()) \
</span><span class="cx">     macro(JSPropertyNameEnumerator_endStructurePropertyIndex, JSPropertyNameEnumerator::endStructurePropertyIndexOffset()) \
</span><span class="cx">     macro(JSPropertyNameEnumerator_indexLength, JSPropertyNameEnumerator::indexedLengthOffset()) \
</span><del>-    macro(JSPropertyNameEnumerator_modeSet, JSPropertyNameEnumerator::modeSetOffset()) \
</del><ins>+    macro(JSPropertyNameEnumerator_flags, JSPropertyNameEnumerator::flagsOffset()) \
</ins><span class="cx">     macro(JSRopeString_flags, JSRopeString::offsetOfFlags()) \
</span><span class="cx">     macro(JSRopeString_length, JSRopeString::offsetOfLength()) \
</span><span class="cx">     macro(JSRopeString_fiber0, JSRopeString::offsetOfFiber0()) \
</span><span class="lines">@@ -152,6 +153,7 @@
</span><span class="cx">     macro(Structure_structureID, Structure::structureIDOffset()) \
</span><span class="cx">     macro(StructureRareData_cachedKeys, StructureRareData::offsetOfCachedPropertyNames(CachedPropertyNamesKind::Keys)) \
</span><span class="cx">     macro(StructureRareData_cachedGetOwnPropertyNames, StructureRareData::offsetOfCachedPropertyNames(CachedPropertyNamesKind::GetOwnPropertyNames)) \
</span><ins>+    macro(StructureRareData_cachedPropertyNameEnumerator, StructureRareData::offsetOfCachedPropertyNameEnumerator()) \
</ins><span class="cx">     macro(HashMapImpl_capacity, HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfCapacity()) \
</span><span class="cx">     macro(HashMapImpl_buffer,  HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfBuffer()) \
</span><span class="cx">     macro(HashMapImpl_head,  HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfHead()) \
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLLowerDFGToB3cpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp (282013 => 282014)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp      2021-09-03 19:26:19 UTC (rev 282013)
+++ trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp 2021-09-03 19:46:40 UTC (rev 282014)
</span><span class="lines">@@ -13220,10 +13220,54 @@
</span><span class="cx">     void compileGetPropertyEnumerator()
</span><span class="cx">     {
</span><span class="cx">         JSGlobalObject* globalObject = m_graph.globalObjectFor(m_origin.semantic);
</span><del>-        if (m_node->child1().useKind() == CellUse)
-            setJSValue(vmCall(Int64, operationGetPropertyEnumeratorCell, weakPointer(globalObject), lowCell(m_node->child1())));
-        else
-            setJSValue(vmCall(Int64, operationGetPropertyEnumerator, weakPointer(globalObject), lowJSValue(m_node->child1())));
</del><ins>+        if (m_node->child1().useKind() == CellUse) {
+            LBasicBlock checkExistingCase = m_out.newBlock();
+            LBasicBlock notNullCase = m_out.newBlock();
+            LBasicBlock rareDataCase = m_out.newBlock();
+            LBasicBlock validationCase = m_out.newBlock();
+            LBasicBlock genericCase = m_out.newBlock();
+            LBasicBlock continuation = m_out.newBlock();
+
+            LValue cell = lowCell(m_node->child1());
+
+            // We go to the inlined fast path if the object is UndecidedShape / NoIndexingShape for simplicity.
+            static_assert(!NonArray);
+            static_assert(ArrayClass == 1);
+            static_assert(UndecidedShape == 2);
+            static_assert(ArrayWithUndecided == 3);
+            static_assert(NonArray <= ArrayWithUndecided);
+            static_assert(ArrayClass <= ArrayWithUndecided);
+            static_assert(ArrayWithUndecided <= ArrayWithUndecided);
+            LValue indexingType = m_out.bitAnd(m_out.load8ZeroExt32(cell, m_heaps.JSCell_indexingTypeAndMisc), m_out.constInt32(IndexingTypeMask));
+            m_out.branch(m_out.belowOrEqual(indexingType, m_out.constInt32(ArrayWithUndecided)), unsure(checkExistingCase), unsure(genericCase));
+
+            LBasicBlock lastNext = m_out.appendTo(checkExistingCase, notNullCase);
+            LValue structure = loadStructure(cell);
+            LValue previousOrRareData = m_out.loadPtr(structure, m_heaps.Structure_previousOrRareData);
+            m_out.branch(m_out.notNull(previousOrRareData), unsure(notNullCase), unsure(genericCase));
+
+            m_out.appendTo(notNullCase, rareDataCase);
+            m_out.branch(
+                m_out.notEqual(m_out.load32(previousOrRareData, m_heaps.JSCell_structureID), m_out.constInt32(m_graph.m_vm.structureStructure->structureID())),
+                unsure(rareDataCase), unsure(genericCase));
+
+            m_out.appendTo(rareDataCase, validationCase);
+            LValue cached = m_out.loadPtr(previousOrRareData, m_heaps.StructureRareData_cachedPropertyNameEnumerator);
+            m_out.branch(m_out.notNull(cached), unsure(validationCase), unsure(genericCase));
+
+            m_out.appendTo(validationCase, genericCase);
+            ValueFromBlock fastResult = m_out.anchor(cached);
+            m_out.branch(m_out.testNonZero32(m_out.load32(cached, m_heaps.JSPropertyNameEnumerator_flags), m_out.constInt32(JSPropertyNameEnumerator::ValidatedViaWatchpoint)), unsure(continuation), unsure(genericCase));
+
+            m_out.appendTo(genericCase, continuation);
+            ValueFromBlock genericResult = m_out.anchor(vmCall(pointerType(), operationGetPropertyEnumeratorCell, weakPointer(globalObject), cell));
+            m_out.jump(continuation);
+
+            m_out.appendTo(continuation, lastNext);
+            setJSValue(m_out.phi(pointerType(), fastResult, genericResult));
+            return;
+        }
+        setJSValue(vmCall(Int64, operationGetPropertyEnumerator, weakPointer(globalObject), lowJSValue(m_node->child1())));
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     void compileEnumeratorNextUpdateIndexAndMode()
</span><span class="lines">@@ -13269,7 +13313,7 @@
</span><span class="cx">             LValue base = lowCell(baseEdge);
</span><span class="cx">             speculate(BadCache, noValue(), m_node, m_out.notEqual(m_out.load32(base, m_heaps.JSCell_structureID), m_out.load32(enumerator, m_heaps.JSPropertyNameEnumerator_cachedStructureID)));
</span><span class="cx"> 
</span><del>-            speculate(BadCache, noValue(), m_node, m_out.notEqual(m_out.load32(enumerator, m_heaps.JSPropertyNameEnumerator_modeSet), m_out.constInt32(JSPropertyNameEnumerator::OwnStructureMode)));
</del><ins>+            speculate(BadCache, noValue(), m_node, m_out.notEqual(m_out.bitAnd(m_out.load32(enumerator, m_heaps.JSPropertyNameEnumerator_flags), m_out.constInt32(JSPropertyNameEnumerator::enumerationModeMask)), m_out.constInt32(JSPropertyNameEnumerator::OwnStructureMode)));
</ins><span class="cx"> 
</span><span class="cx">             LBasicBlock increment = m_out.newBlock();
</span><span class="cx">             LBasicBlock continuation = m_out.newBlock();
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorejitJITPropertyAccesscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/jit/JITPropertyAccess.cpp (282013 => 282014)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/jit/JITPropertyAccess.cpp    2021-09-03 19:26:19 UTC (rev 282013)
+++ trunk/Source/JavaScriptCore/jit/JITPropertyAccess.cpp       2021-09-03 19:46:40 UTC (rev 282014)
</span><span class="lines">@@ -2808,7 +2808,7 @@
</span><span class="cx">     if (metadata.m_enumeratorMetadata == JSPropertyNameEnumerator::OwnStructureMode) {
</span><span class="cx">         GPRReg enumeratorGPR = regT3;
</span><span class="cx">         emitGetVirtualRegister(enumerator, enumeratorGPR);
</span><del>-        operationCases.append(branchTest32(NonZero, Address(enumeratorGPR, JSPropertyNameEnumerator::modeSetOffset()), TrustedImm32(~JSPropertyNameEnumerator::OwnStructureMode)));
</del><ins>+        operationCases.append(branchTest32(NonZero, Address(enumeratorGPR, JSPropertyNameEnumerator::flagsOffset()), TrustedImm32((~JSPropertyNameEnumerator::OwnStructureMode) & JSPropertyNameEnumerator::enumerationModeMask)));
</ins><span class="cx">         emitGetVirtualRegister(base, baseGPR);
</span><span class="cx">         load32(Address(enumeratorGPR, JSPropertyNameEnumerator::cachedStructureIDOffset()), indexGPR);
</span><span class="cx">         operationCases.append(branch32(NotEqual, indexGPR, Address(baseGPR, JSCell::structureIDOffset())));
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeCommonSlowPathscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp (282013 => 282014)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp  2021-09-03 19:26:19 UTC (rev 282013)
+++ trunk/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp     2021-09-03 19:46:40 UTC (rev 282014)
</span><span class="lines">@@ -985,7 +985,7 @@
</span><span class="cx">     Register& indexRegister = GET(bytecode.m_index);
</span><span class="cx">     Register& nameRegister = GET(bytecode.m_propertyName);
</span><span class="cx"> 
</span><del>-    auto mode = static_cast<JSPropertyNameEnumerator::Mode>(modeRegister.jsValue().asUInt32());
</del><ins>+    auto mode = static_cast<JSPropertyNameEnumerator::Flag>(modeRegister.jsValue().asUInt32());
</ins><span class="cx">     uint32_t index = indexRegister.jsValue().asUInt32();
</span><span class="cx"> 
</span><span class="cx">     JSPropertyNameEnumerator* enumerator = jsCast<JSPropertyNameEnumerator*>(GET(bytecode.m_enumerator).jsValue());
</span><span class="lines">@@ -1011,7 +1011,7 @@
</span><span class="cx">     auto bytecode = pc->as<OpEnumeratorGetByVal>();
</span><span class="cx">     JSValue baseValue = GET_C(bytecode.m_base).jsValue();
</span><span class="cx">     auto& metadata = bytecode.metadata(codeBlock);
</span><del>-    auto mode = static_cast<JSPropertyNameEnumerator::Mode>(GET(bytecode.m_mode).jsValue().asUInt32());
</del><ins>+    auto mode = static_cast<JSPropertyNameEnumerator::Flag>(GET(bytecode.m_mode).jsValue().asUInt32());
</ins><span class="cx">     metadata.m_enumeratorMetadata |= static_cast<uint8_t>(mode);
</span><span class="cx"> 
</span><span class="cx">     JSPropertyNameEnumerator* enumerator = jsCast<JSPropertyNameEnumerator*>(GET(bytecode.m_enumerator).jsValue());
</span><span class="lines">@@ -1054,7 +1054,7 @@
</span><span class="cx">     auto bytecode = pc->as<OpEnumeratorInByVal>();
</span><span class="cx">     JSValue baseValue = GET_C(bytecode.m_base).jsValue();
</span><span class="cx">     auto& metadata = bytecode.metadata(codeBlock);
</span><del>-    auto mode = static_cast<JSPropertyNameEnumerator::Mode>(GET(bytecode.m_mode).jsValue().asUInt32());
</del><ins>+    auto mode = static_cast<JSPropertyNameEnumerator::Flag>(GET(bytecode.m_mode).jsValue().asUInt32());
</ins><span class="cx">     metadata.m_enumeratorMetadata |= static_cast<uint8_t>(mode);
</span><span class="cx"> 
</span><span class="cx">     CHECK_EXCEPTION();
</span><span class="lines">@@ -1077,7 +1077,7 @@
</span><span class="cx">     auto bytecode = pc->as<OpEnumeratorHasOwnProperty>();
</span><span class="cx">     JSValue baseValue = GET_C(bytecode.m_base).jsValue();
</span><span class="cx">     auto& metadata = bytecode.metadata(codeBlock);
</span><del>-    auto mode = static_cast<JSPropertyNameEnumerator::Mode>(GET(bytecode.m_mode).jsValue().asUInt32());
</del><ins>+    auto mode = static_cast<JSPropertyNameEnumerator::Flag>(GET(bytecode.m_mode).jsValue().asUInt32());
</ins><span class="cx">     metadata.m_enumeratorMetadata |= static_cast<uint8_t>(mode);
</span><span class="cx"> 
</span><span class="cx">     JSPropertyNameEnumerator* enumerator = jsCast<JSPropertyNameEnumerator*>(GET(bytecode.m_enumerator).jsValue());
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeJSPropertyNameEnumeratorcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/JSPropertyNameEnumerator.cpp (282013 => 282014)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/JSPropertyNameEnumerator.cpp 2021-09-03 19:26:19 UTC (rev 282013)
+++ trunk/Source/JavaScriptCore/runtime/JSPropertyNameEnumerator.cpp    2021-09-03 19:46:40 UTC (rev 282014)
</span><span class="lines">@@ -57,11 +57,11 @@
</span><span class="cx">     , m_cachedInlineCapacity(structure ? structure->inlineCapacity() : 0)
</span><span class="cx"> {
</span><span class="cx">     if (m_indexedLength)
</span><del>-        m_modeSet |= JSPropertyNameEnumerator::IndexedMode;
</del><ins>+        m_flags |= JSPropertyNameEnumerator::IndexedMode;
</ins><span class="cx">     if (m_endStructurePropertyIndex)
</span><del>-        m_modeSet |= JSPropertyNameEnumerator::OwnStructureMode;
</del><ins>+        m_flags |= JSPropertyNameEnumerator::OwnStructureMode;
</ins><span class="cx">     if (m_endGenericPropertyIndex - m_endStructurePropertyIndex)
</span><del>-        m_modeSet |= JSPropertyNameEnumerator::GenericMode;
</del><ins>+        m_flags |= JSPropertyNameEnumerator::GenericMode;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void JSPropertyNameEnumerator::finishCreation(VM& vm, RefPtr<PropertyNameArrayData>&& identifiers)
</span><span class="lines">@@ -154,7 +154,7 @@
</span><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-JSString* JSPropertyNameEnumerator::computeNext(JSGlobalObject* globalObject, JSObject* base, uint32_t& index, Mode& mode, bool shouldAllocateIndexedNameString)
</del><ins>+JSString* JSPropertyNameEnumerator::computeNext(JSGlobalObject* globalObject, JSObject* base, uint32_t& index, Flag& mode, bool shouldAllocateIndexedNameString)
</ins><span class="cx"> {
</span><span class="cx">     VM& vm = globalObject->vm();
</span><span class="cx">     auto scope = DECLARE_THROW_SCOPE(vm);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeJSPropertyNameEnumeratorh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/JSPropertyNameEnumerator.h (282013 => 282014)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/JSPropertyNameEnumerator.h   2021-09-03 19:26:19 UTC (rev 282013)
+++ trunk/Source/JavaScriptCore/runtime/JSPropertyNameEnumerator.h      2021-09-03 19:46:40 UTC (rev 282014)
</span><span class="lines">@@ -37,7 +37,7 @@
</span><span class="cx">     using Base = JSCell;
</span><span class="cx">     static constexpr unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal;
</span><span class="cx"> 
</span><del>-    enum Mode : uint8_t {
</del><ins>+    enum Flag : uint8_t {
</ins><span class="cx">         InitMode = 0,
</span><span class="cx">         IndexedMode = 1 << 0,
</span><span class="cx">         OwnStructureMode = 1 << 1,
</span><span class="lines">@@ -44,6 +44,8 @@
</span><span class="cx">         GenericMode = 1 << 2,
</span><span class="cx">         // Profiling Only
</span><span class="cx">         HasSeenOwnStructureModeStructureMismatch = 1 << 3,
</span><ins>+        // JSPropertyNameEnumerator Only
+        ValidatedViaWatchpoint = 1 << 4,
</ins><span class="cx">     };
</span><span class="cx"> 
</span><span class="cx">     static constexpr uint8_t enumerationModeMask = (GenericMode << 1) - 1;
</span><span class="lines">@@ -72,6 +74,7 @@
</span><span class="cx"> 
</span><span class="cx">     StructureChain* cachedPrototypeChain() const { return m_prototypeChain.get(); }
</span><span class="cx">     void setCachedPrototypeChain(VM& vm, StructureChain* prototypeChain) { return m_prototypeChain.set(vm, this, prototypeChain); }
</span><ins>+    static ptrdiff_t offsetOfCachedPrototypeChain() { return OBJECT_OFFSETOF(JSPropertyNameEnumerator, m_prototypeChain); }
</ins><span class="cx"> 
</span><span class="cx">     Structure* cachedStructure(VM& vm) const
</span><span class="cx">     {
</span><span class="lines">@@ -85,20 +88,29 @@
</span><span class="cx">     uint32_t endGenericPropertyIndex() const { return m_endGenericPropertyIndex; }
</span><span class="cx">     uint32_t cachedInlineCapacity() const { return m_cachedInlineCapacity; }
</span><span class="cx">     uint32_t sizeOfPropertyNames() const { return endGenericPropertyIndex(); }
</span><del>-    uint32_t modeSet() const { return m_modeSet; }
</del><ins>+    uint32_t flags() const { return m_flags; }
</ins><span class="cx">     static ptrdiff_t cachedStructureIDOffset() { return OBJECT_OFFSETOF(JSPropertyNameEnumerator, m_cachedStructureID); }
</span><span class="cx">     static ptrdiff_t indexedLengthOffset() { return OBJECT_OFFSETOF(JSPropertyNameEnumerator, m_indexedLength); }
</span><span class="cx">     static ptrdiff_t endStructurePropertyIndexOffset() { return OBJECT_OFFSETOF(JSPropertyNameEnumerator, m_endStructurePropertyIndex); }
</span><span class="cx">     static ptrdiff_t endGenericPropertyIndexOffset() { return OBJECT_OFFSETOF(JSPropertyNameEnumerator, m_endGenericPropertyIndex); }
</span><span class="cx">     static ptrdiff_t cachedInlineCapacityOffset() { return OBJECT_OFFSETOF(JSPropertyNameEnumerator, m_cachedInlineCapacity); }
</span><del>-    static ptrdiff_t modeSetOffset() { return OBJECT_OFFSETOF(JSPropertyNameEnumerator, m_modeSet); }
</del><ins>+    static ptrdiff_t flagsOffset() { return OBJECT_OFFSETOF(JSPropertyNameEnumerator, m_flags); }
</ins><span class="cx">     static ptrdiff_t cachedPropertyNamesVectorOffset()
</span><span class="cx">     {
</span><span class="cx">         return OBJECT_OFFSETOF(JSPropertyNameEnumerator, m_propertyNames);
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    JSString* computeNext(JSGlobalObject*, JSObject* base, uint32_t& currentIndex, Mode&, bool shouldAllocateIndexedNameString = true);
</del><ins>+    bool validatedViaWatchpoint() const { return m_flags & JSPropertyNameEnumerator::ValidatedViaWatchpoint; }
+    void setValidatedViaWatchpoint(bool validatedViaWatchpoint)
+    {
+        if (validatedViaWatchpoint)
+            m_flags |= JSPropertyNameEnumerator::ValidatedViaWatchpoint;
+        else
+            m_flags &= ~JSPropertyNameEnumerator::ValidatedViaWatchpoint;
+    }
</ins><span class="cx"> 
</span><ins>+    JSString* computeNext(JSGlobalObject*, JSObject* base, uint32_t& currentIndex, Flag&, bool shouldAllocateIndexedNameString = true);
+
</ins><span class="cx">     DECLARE_VISIT_CHILDREN;
</span><span class="cx"> 
</span><span class="cx"> private:
</span><span class="lines">@@ -114,7 +126,7 @@
</span><span class="cx">     uint32_t m_endStructurePropertyIndex;
</span><span class="cx">     uint32_t m_endGenericPropertyIndex;
</span><span class="cx">     uint32_t m_cachedInlineCapacity;
</span><del>-    uint32_t m_modeSet { 0 };
</del><ins>+    uint32_t m_flags { 0 };
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> void getEnumerablePropertyNames(JSGlobalObject*, JSObject*, PropertyNameArray&, uint32_t& indexedLength, uint32_t& structurePropertyCount);
</span><span class="lines">@@ -130,9 +142,10 @@
</span><span class="cx"> 
</span><span class="cx">     Structure* structure = base->structure(vm);
</span><span class="cx">     if (!indexedLength
</span><del>-        && (enumerator = structure->cachedPropertyNameEnumerator())
-        && enumerator->cachedPrototypeChain() == structure->prototypeChain(globalObject, base))
-        return enumerator;
</del><ins>+        && (enumerator = structure->cachedPropertyNameEnumerator())) {
+        if (enumerator->validatedViaWatchpoint() || enumerator->cachedPrototypeChain() == structure->prototypeChain(globalObject, base))
+            return enumerator;
+    }
</ins><span class="cx"> 
</span><span class="cx">     uint32_t numberStructureProperties = 0;
</span><span class="cx">     PropertyNameArray propertyNames(vm, PropertyNameMode::Strings, PrivateSymbolMode::Exclude);
</span><span class="lines">@@ -162,6 +175,6 @@
</span><span class="cx">     return enumerator;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-using EnumeratorMetadata = std::underlying_type_t<JSPropertyNameEnumerator::Mode>;
</del><ins>+using EnumeratorMetadata = std::underlying_type_t<JSPropertyNameEnumerator::Flag>;
</ins><span class="cx"> 
</span><span class="cx"> } // namespace JSC
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeStructureh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/Structure.h (282013 => 282014)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/Structure.h  2021-09-03 19:26:19 UTC (rev 282013)
+++ trunk/Source/JavaScriptCore/runtime/Structure.h     2021-09-03 19:46:40 UTC (rev 282014)
</span><span class="lines">@@ -634,6 +634,11 @@
</span><span class="cx">     {
</span><span class="cx">         return dfgShouldWatchIfPossible() && transitionWatchpointSetIsStillValid();
</span><span class="cx">     }
</span><ins>+
+    bool propertyNameEnumeratorShouldWatch() const
+    {
+        return dfgShouldWatch();
+    }
</ins><span class="cx">         
</span><span class="cx">     void addTransitionWatchpoint(Watchpoint* watchpoint) const
</span><span class="cx">     {
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeStructureRareDatah"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/StructureRareData.h (282013 => 282014)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/StructureRareData.h  2021-09-03 19:26:19 UTC (rev 282013)
+++ trunk/Source/JavaScriptCore/runtime/StructureRareData.h     2021-09-03 19:46:40 UTC (rev 282014)
</span><span class="lines">@@ -30,11 +30,13 @@
</span><span class="cx"> #include "JSTypeInfo.h"
</span><span class="cx"> #include "PropertyOffset.h"
</span><span class="cx"> #include "PropertySlot.h"
</span><ins>+#include <wtf/FixedVector.h>
</ins><span class="cx"> 
</span><span class="cx"> namespace JSC {
</span><span class="cx"> 
</span><span class="cx"> class JSPropertyNameEnumerator;
</span><span class="cx"> class Structure;
</span><ins>+class StructureChain;
</ins><span class="cx"> class CachedSpecialPropertyAdaptiveStructureWatchpoint;
</span><span class="cx"> class CachedSpecialPropertyAdaptiveInferredPropertyValueWatchpoint;
</span><span class="cx"> struct SpecialPropertyCache;
</span><span class="lines">@@ -52,6 +54,9 @@
</span><span class="cx"> };
</span><span class="cx"> static constexpr unsigned numberOfCachedSpecialPropertyKeys = 4;
</span><span class="cx"> 
</span><ins>+class StructureRareData;
+class StructureChainInvalidationWatchpoint;
+
</ins><span class="cx"> class StructureRareData final : public JSCell {
</span><span class="cx"> public:
</span><span class="cx">     typedef JSCell Base;
</span><span class="lines">@@ -102,10 +107,17 @@
</span><span class="cx">         return OBJECT_OFFSETOF(StructureRareData, m_cachedPropertyNames) + sizeof(WriteBarrier<JSImmutableButterfly>) * static_cast<unsigned>(kind);
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    static ptrdiff_t offsetOfCachedPropertyNameEnumerator()
+    {
+        return OBJECT_OFFSETOF(StructureRareData, m_cachedPropertyNameEnumerator);
+    }
+
</ins><span class="cx">     DECLARE_EXPORT_INFO;
</span><span class="cx"> 
</span><span class="cx">     void finalizeUnconditionally(VM&);
</span><span class="cx"> 
</span><ins>+    void invalidateWatchpointBasedValidation();
+
</ins><span class="cx"> private:
</span><span class="cx">     friend class Structure;
</span><span class="cx">     friend class CachedSpecialPropertyAdaptiveStructureWatchpoint;
</span><span class="lines">@@ -121,10 +133,13 @@
</span><span class="cx">     bool canCacheSpecialProperty(CachedSpecialPropertyKey);
</span><span class="cx">     void giveUpOnSpecialPropertyCache(CachedSpecialPropertyKey);
</span><span class="cx"> 
</span><ins>+    bool tryCachePropertyNameEnumeratorViaWatchpoint(VM&, StructureChain*);
+
</ins><span class="cx">     WriteBarrier<Structure> m_previous;
</span><span class="cx">     // FIXME: We should have some story for clearing these property names caches in GC.
</span><span class="cx">     // https://bugs.webkit.org/show_bug.cgi?id=192659
</span><span class="cx">     WriteBarrier<JSPropertyNameEnumerator> m_cachedPropertyNameEnumerator;
</span><ins>+    FixedVector<StructureChainInvalidationWatchpoint> m_cachedPropertyNameEnumeratorWatchpoints;
</ins><span class="cx">     WriteBarrier<JSImmutableButterfly> m_cachedPropertyNames[numberOfCachedPropertyNames] { };
</span><span class="cx"> 
</span><span class="cx">     typedef HashMap<PropertyOffset, RefPtr<WatchpointSet>, WTF::IntHash<PropertyOffset>, WTF::UnsignedWithZeroKeyHashTraits<PropertyOffset>> PropertyWatchpointMap;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeStructureRareDataInlinesh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/StructureRareDataInlines.h (282013 => 282014)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/StructureRareDataInlines.h   2021-09-03 19:26:19 UTC (rev 282013)
+++ trunk/Source/JavaScriptCore/runtime/StructureRareDataInlines.h      2021-09-03 19:46:40 UTC (rev 282014)
</span><span class="lines">@@ -28,6 +28,7 @@
</span><span class="cx"> #include "JSImmutableButterfly.h"
</span><span class="cx"> #include "JSPropertyNameEnumerator.h"
</span><span class="cx"> #include "JSString.h"
</span><ins>+#include "StructureChain.h"
</ins><span class="cx"> #include "StructureRareData.h"
</span><span class="cx"> 
</span><span class="cx"> namespace JSC {
</span><span class="lines">@@ -47,6 +48,21 @@
</span><span class="cx">     SpecialPropertyCacheEntry m_cache[numberOfCachedSpecialPropertyKeys];
</span><span class="cx"> };
</span><span class="cx"> 
</span><ins>+class StructureChainInvalidationWatchpoint final : public Watchpoint {
+public:
+    StructureChainInvalidationWatchpoint()
+        : Watchpoint(Watchpoint::Type::StructureChainInvalidation)
+        , m_structureRareData(nullptr)
+    { }
+
+    void install(StructureRareData*, Structure*);
+    void fireInternal(VM&, const FireDetail&);
+
+private:
+    // Own destructor may not be called. Keep members trivially destructible.
+    JSC_WATCHPOINT_FIELD(PackedCellPtr<StructureRareData>, m_structureRareData);
+};
+
</ins><span class="cx"> inline void StructureRareData::setPreviousID(VM& vm, Structure* structure)
</span><span class="cx"> {
</span><span class="cx">     m_previous.set(vm, this, structure);
</span><span class="lines">@@ -80,6 +96,9 @@
</span><span class="cx"> inline void StructureRareData::setCachedPropertyNameEnumerator(VM& vm, JSPropertyNameEnumerator* enumerator)
</span><span class="cx"> {
</span><span class="cx">     m_cachedPropertyNameEnumerator.set(vm, this, enumerator);
</span><ins>+    m_cachedPropertyNameEnumeratorWatchpoints = FixedVector<StructureChainInvalidationWatchpoint>();
+    bool validatedViaWatchpoint = tryCachePropertyNameEnumeratorViaWatchpoint(vm, enumerator->cachedPrototypeChain());
+    enumerator->setValidatedViaWatchpoint(validatedViaWatchpoint);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> inline JSImmutableButterfly* StructureRareData::cachedPropertyNames(CachedPropertyNamesKind kind) const
</span><span class="lines">@@ -140,4 +159,44 @@
</span><span class="cx">     return cacheSpecialPropertySlow(globalObject, vm, ownStructure, value, key, slot);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+inline void StructureChainInvalidationWatchpoint::install(StructureRareData* structureRareData, Structure* structure)
+{
+    m_structureRareData = structureRareData;
+    structure->addTransitionWatchpoint(this);
+}
+
+inline void StructureChainInvalidationWatchpoint::fireInternal(VM&, const FireDetail&)
+{
+    if (!m_structureRareData->isLive())
+        return;
+    m_structureRareData->invalidateWatchpointBasedValidation();
+}
+
+inline bool StructureRareData::tryCachePropertyNameEnumeratorViaWatchpoint(VM& vm, StructureChain* chain)
+{
+    unsigned size = 0;
+    for (auto* current = chain->head(); *current; ++current) {
+        ++size;
+        StructureID structureID = *current;
+        Structure* structure = vm.getStructure(structureID);
+        if (!structure->propertyNameEnumeratorShouldWatch())
+            return false;
+    }
+    m_cachedPropertyNameEnumeratorWatchpoints = FixedVector<StructureChainInvalidationWatchpoint>(size);
+    unsigned index = 0;
+    for (auto* current = chain->head(); *current; ++current) {
+        StructureID structureID = *current;
+        Structure* structure = vm.getStructure(structureID);
+        m_cachedPropertyNameEnumeratorWatchpoints[index].install(this, structure);
+        ++index;
+    }
+    return true;
+}
+
+inline void StructureRareData::invalidateWatchpointBasedValidation()
+{
+    m_cachedPropertyNameEnumerator.clear();
+    m_cachedPropertyNameEnumeratorWatchpoints = FixedVector<StructureChainInvalidationWatchpoint>();
+}
+
</ins><span class="cx"> } // namespace JSC
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoretoolsJSDollarVMcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/tools/JSDollarVM.cpp (282013 => 282014)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tools/JSDollarVM.cpp 2021-09-03 19:26:19 UTC (rev 282013)
+++ trunk/Source/JavaScriptCore/tools/JSDollarVM.cpp    2021-09-03 19:46:40 UTC (rev 282014)
</span><span class="lines">@@ -2118,6 +2118,7 @@
</span><span class="cx"> static JSC_DECLARE_HOST_FUNCTION(functionIsMemoryLimited);
</span><span class="cx"> static JSC_DECLARE_HOST_FUNCTION(functionUseJIT);
</span><span class="cx"> static JSC_DECLARE_HOST_FUNCTION(functionIsGigacageEnabled);
</span><ins>+static JSC_DECLARE_HOST_FUNCTION(functionToCacheableDictionary);
</ins><span class="cx"> static JSC_DECLARE_HOST_FUNCTION(functionToUncacheableDictionary);
</span><span class="cx"> static JSC_DECLARE_HOST_FUNCTION(functionIsPrivateSymbol);
</span><span class="cx"> static JSC_DECLARE_HOST_FUNCTION(functionDumpAndResetPasDebugSpectrum);
</span><span class="lines">@@ -3713,6 +3714,20 @@
</span><span class="cx">     return JSValue::encode(jsBoolean(Gigacage::isEnabled()));
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+JSC_DEFINE_HOST_FUNCTION(functionToCacheableDictionary, (JSGlobalObject* globalObject, CallFrame* callFrame))
+{
+    DollarVMAssertScope assertScope;
+    VM& vm = globalObject->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    JSObject* object = jsDynamicCast<JSObject*>(vm, callFrame->argument(0));
+    if (!object)
+        return throwVMTypeError(globalObject, scope, "Expected first argument to be an object"_s);
+    if (!object->structure(vm)->isUncacheableDictionary())
+        object->convertToDictionary(vm);
+    return JSValue::encode(object);
+}
+
</ins><span class="cx"> JSC_DEFINE_HOST_FUNCTION(functionToUncacheableDictionary, (JSGlobalObject* globalObject, CallFrame* callFrame))
</span><span class="cx"> {
</span><span class="cx">     DollarVMAssertScope assertScope;
</span><span class="lines">@@ -3946,6 +3961,7 @@
</span><span class="cx">     addFunction(vm, "useJIT", functionUseJIT, 0);
</span><span class="cx">     addFunction(vm, "isGigacageEnabled", functionIsGigacageEnabled, 0);
</span><span class="cx"> 
</span><ins>+    addFunction(vm, "toCacheableDictionary", functionToCacheableDictionary, 1);
</ins><span class="cx">     addFunction(vm, "toUncacheableDictionary", functionToUncacheableDictionary, 1);
</span><span class="cx"> 
</span><span class="cx">     addFunction(vm, "isPrivateSymbol", functionIsPrivateSymbol, 1);
</span></span></pre>
</div>
</div>

</body>
</html>