<!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>[184447] 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/184447">184447</a></dd>
<dt>Author</dt> <dd>benjamin@webkit.org</dd>
<dt>Date</dt> <dd>2015-05-17 23:23:31 -0700 (Sun, 17 May 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>[JSC] Make StringRecursionChecker faster in the simple cases without any recursion
https://bugs.webkit.org/show_bug.cgi?id=145102

Reviewed by Darin Adler.

Source/JavaScriptCore:

In general, the array targeted by Array.toString() or Array.join() are pretty
simple. In those simple cases, we spend as much time in StringRecursionChecker
as we do on the actual operation.

The reason for this is the HashSet stringRecursionCheckVisitedObjects used
to detect recursion. We are constantly adding and removing objects which
dirty buckets and force constant rehash.

This patch adds a simple shortcut for those simple case: in addition to the HashSet,
we keep a pointer to the root object of the recursion.
In the vast majority of cases, we no longer touch the HashSet at all.

This patch is a 12% progression on the overall score of ArrayWeighted.

* runtime/StringRecursionChecker.h:
(JSC::StringRecursionChecker::performCheck):
(JSC::StringRecursionChecker::~StringRecursionChecker):
* runtime/VM.h:

LayoutTests:

Improve the coverage a tiny bit.

* js/array-string-recursion-expected.txt: Added.
* js/array-string-recursion.html: Added.
* js/script-tests/array-string-recursion.js: Added.</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="#trunkSourceJavaScriptCoreruntimeStringRecursionCheckerh">trunk/Source/JavaScriptCore/runtime/StringRecursionChecker.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeVMh">trunk/Source/JavaScriptCore/runtime/VM.h</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsjsarraystringrecursionexpectedtxt">trunk/LayoutTests/js/array-string-recursion-expected.txt</a></li>
<li><a href="#trunkLayoutTestsjsarraystringrecursionhtml">trunk/LayoutTests/js/array-string-recursion.html</a></li>
<li><a href="#trunkLayoutTestsjsscripttestsarraystringrecursionjs">trunk/LayoutTests/js/script-tests/array-string-recursion.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (184446 => 184447)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2015-05-18 06:16:58 UTC (rev 184446)
+++ trunk/LayoutTests/ChangeLog        2015-05-18 06:23:31 UTC (rev 184447)
</span><span class="lines">@@ -1,3 +1,16 @@
</span><ins>+2015-05-17  Benjamin Poulain  &lt;benjamin@webkit.org&gt;
+
+        [JSC] Make StringRecursionChecker faster in the simple cases without any recursion
+        https://bugs.webkit.org/show_bug.cgi?id=145102
+
+        Reviewed by Darin Adler.
+
+        Improve the coverage a tiny bit.
+
+        * js/array-string-recursion-expected.txt: Added.
+        * js/array-string-recursion.html: Added.
+        * js/script-tests/array-string-recursion.js: Added.
+
</ins><span class="cx"> 2015-05-17  Manuel Rego Casasnovas  &lt;rego@igalia.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [CSS Grid Layout] Add scrollbar width in intrinsic logical widths computation
</span></span></pre></div>
<a id="trunkLayoutTestsjsarraystringrecursionexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/js/array-string-recursion-expected.txt (0 => 184447)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/js/array-string-recursion-expected.txt                                (rev 0)
+++ trunk/LayoutTests/js/array-string-recursion-expected.txt        2015-05-18 06:23:31 UTC (rev 184447)
</span><span class="lines">@@ -0,0 +1,84 @@
</span><ins>+Verify that we do not recurse infinitely through one of the Array-&gt;string conversion.
+
+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+PASS var arrayDirectlyContainingItself = [];
+    arrayDirectlyContainingItself.push(arrayDirectlyContainingItself);
+    arrayDirectlyContainingItself.toString(); is &quot;&quot;
+PASS var arrayDirectlyContainingItself = [];
+    arrayDirectlyContainingItself.push(arrayDirectlyContainingItself);
+    arrayDirectlyContainingItself.toLocaleString(); is &quot;&quot;
+PASS var arrayDirectlyContainingItself = [];
+    arrayDirectlyContainingItself.push(arrayDirectlyContainingItself);
+    arrayDirectlyContainingItself.join(&quot;,&quot;); is &quot;&quot;
+PASS var arrayDirectlyContainingItself = [];
+    arrayDirectlyContainingItself.push(1);
+    arrayDirectlyContainingItself.push(arrayDirectlyContainingItself);
+    arrayDirectlyContainingItself.push(&quot;WebKit!&quot;);
+    arrayDirectlyContainingItself.push(arrayDirectlyContainingItself);
+    arrayDirectlyContainingItself.toString(); is &quot;1,,WebKit!,&quot;
+PASS var arrayDirectlyContainingItself = [];
+    arrayDirectlyContainingItself.push(1);
+    arrayDirectlyContainingItself.push(arrayDirectlyContainingItself);
+    arrayDirectlyContainingItself.push(&quot;WebKit!&quot;);
+    arrayDirectlyContainingItself.push(arrayDirectlyContainingItself);
+    arrayDirectlyContainingItself.toLocaleString(); is &quot;1,,WebKit!,&quot;
+PASS var arrayDirectlyContainingItself = [];
+    arrayDirectlyContainingItself.push(1);
+    arrayDirectlyContainingItself.push(arrayDirectlyContainingItself);
+    arrayDirectlyContainingItself.push(&quot;WebKit!&quot;);
+    arrayDirectlyContainingItself.push(arrayDirectlyContainingItself);
+    arrayDirectlyContainingItself.join(&quot;-&quot;); is &quot;1--WebKit!-&quot;
+PASS var arrayIndirectlyContainingItself = [];
+    arrayIndirectlyContainingItself.push(1);
+    arrayIndirectlyContainingItself.push([1, 2, [5, 6, [arrayIndirectlyContainingItself]]]);
+    arrayIndirectlyContainingItself.push(&quot;WebKit!&quot;);
+    arrayIndirectlyContainingItself.toString(); is &quot;1,1,2,5,6,,WebKit!&quot;
+PASS var arrayIndirectlyContainingItself = [];
+    arrayIndirectlyContainingItself.push(1);
+    arrayIndirectlyContainingItself.push([1, 2, [5, 6, [arrayIndirectlyContainingItself]]]);
+    arrayIndirectlyContainingItself.push(&quot;WebKit!&quot;);
+    arrayIndirectlyContainingItself.toLocaleString(); is &quot;1,1,2,5,6,,WebKit!&quot;
+PASS var arrayIndirectlyContainingItself = [];
+    arrayIndirectlyContainingItself.push(1);
+    arrayIndirectlyContainingItself.push([1, 2, [5, 6, [arrayIndirectlyContainingItself]]]);
+    arrayIndirectlyContainingItself.push(&quot;WebKit!&quot;);
+    arrayIndirectlyContainingItself.join(&quot;=&quot;); is &quot;1=1,2,5,6,=WebKit!&quot;
+PASS var arrayIndirectlyContainingItself = [];
+    arrayIndirectlyContainingItself.push(1);
+    arrayIndirectlyContainingItself.push([1, 2, [5, 6, [arrayIndirectlyContainingItself]]]);
+    arrayIndirectlyContainingItself.push(&quot;WebKit!&quot;);
+    [&quot;z&quot;, arrayIndirectlyContainingItself, 9].toString(); is &quot;z,1,1,2,5,6,,WebKit!,9&quot;
+PASS var arrayIndirectlyContainingItself = [];
+    arrayIndirectlyContainingItself.push(1);
+    arrayIndirectlyContainingItself.push([1, 2, [5, 6, [arrayIndirectlyContainingItself]]]);
+    arrayIndirectlyContainingItself.push(&quot;WebKit!&quot;);
+    [&quot;z&quot;, arrayIndirectlyContainingItself, 9].toLocaleString(); is &quot;z,1,1,2,5,6,,WebKit!,9&quot;
+PASS var arrayIndirectlyContainingItself = [];
+    arrayIndirectlyContainingItself.push(1);
+    arrayIndirectlyContainingItself.push([1, 2, [5, 6, [arrayIndirectlyContainingItself]]]);
+    arrayIndirectlyContainingItself.push(&quot;WebKit!&quot;);
+    [&quot;z&quot;, arrayIndirectlyContainingItself, 9].join(&quot;&amp;&quot;); is &quot;z&amp;1,1,2,5,6,,WebKit!&amp;9&quot;
+PASS var arrayIndirectlyConvertingItself = [&quot;a&quot;];
+    arrayIndirectlyConvertingItself.push({ array: arrayIndirectlyConvertingItself, toString: function() { return this.array.toString() } });
+    arrayIndirectlyConvertingItself.push({ array: arrayIndirectlyConvertingItself, toString: function() { return this.array.toLocaleString() } });
+    arrayIndirectlyConvertingItself.push({ array: arrayIndirectlyConvertingItself, toString: function() { return this.array.join(&quot;~&quot;) } });
+    arrayIndirectlyConvertingItself.push(&quot;WebKit!&quot;);
+    [&quot;z&quot;, arrayIndirectlyConvertingItself, 9].toString(); is &quot;z,a,,,,WebKit!,9&quot;
+PASS var arrayIndirectlyConvertingItself = [&quot;a&quot;];
+    arrayIndirectlyConvertingItself.push({ array: arrayIndirectlyConvertingItself, toString: function() { return this.array.toString() } });
+    arrayIndirectlyConvertingItself.push({ array: arrayIndirectlyConvertingItself, toString: function() { return this.array.toLocaleString() } });
+    arrayIndirectlyConvertingItself.push({ array: arrayIndirectlyConvertingItself, toString: function() { return this.array.join(&quot;~&quot;) } });
+    arrayIndirectlyConvertingItself.push(&quot;WebKit!&quot;);
+    [&quot;z&quot;, arrayIndirectlyConvertingItself, 9].toLocaleString(); is &quot;z,a,,,,WebKit!,9&quot;
+PASS var arrayIndirectlyConvertingItself = [&quot;a&quot;];
+    arrayIndirectlyConvertingItself.push({ array: arrayIndirectlyConvertingItself, toString: function() { return this.array.toString() } });
+    arrayIndirectlyConvertingItself.push({ array: arrayIndirectlyConvertingItself, toString: function() { return this.array.toLocaleString() } });
+    arrayIndirectlyConvertingItself.push({ array: arrayIndirectlyConvertingItself, toString: function() { return this.array.join(&quot;~&quot;) } });
+    arrayIndirectlyConvertingItself.push(&quot;WebKit!&quot;);
+    [&quot;z&quot;, arrayIndirectlyConvertingItself, 9].join(&quot;*&quot;); is &quot;z*a,,,,WebKit!*9&quot;
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestsjsarraystringrecursionhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/js/array-string-recursion.html (0 => 184447)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/js/array-string-recursion.html                                (rev 0)
+++ trunk/LayoutTests/js/array-string-recursion.html        2015-05-18 06:23:31 UTC (rev 184447)
</span><span class="lines">@@ -0,0 +1,10 @@
</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;script-tests/array-string-recursion.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="trunkLayoutTestsjsscripttestsarraystringrecursionjs"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/js/script-tests/array-string-recursion.js (0 => 184447)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/js/script-tests/array-string-recursion.js                                (rev 0)
+++ trunk/LayoutTests/js/script-tests/array-string-recursion.js        2015-05-18 06:23:31 UTC (rev 184447)
</span><span class="lines">@@ -0,0 +1,87 @@
</span><ins>+description(&quot;Verify that we do not recurse infinitely through one of the Array-&gt;string conversion.&quot;);
+
+// Array that only contains itself.
+shouldBeEqualToString(`var arrayDirectlyContainingItself = [];
+    arrayDirectlyContainingItself.push(arrayDirectlyContainingItself);
+    arrayDirectlyContainingItself.toString();`, &quot;&quot;);
+shouldBeEqualToString(`var arrayDirectlyContainingItself = [];
+    arrayDirectlyContainingItself.push(arrayDirectlyContainingItself);
+    arrayDirectlyContainingItself.toLocaleString();`, &quot;&quot;);
+shouldBeEqualToString(`var arrayDirectlyContainingItself = [];
+    arrayDirectlyContainingItself.push(arrayDirectlyContainingItself);
+    arrayDirectlyContainingItself.join(&quot;,&quot;);`, &quot;&quot;);
+
+// Array containing itself and a bunch of other objects.
+shouldBeEqualToString(`var arrayDirectlyContainingItself = [];
+    arrayDirectlyContainingItself.push(1);
+    arrayDirectlyContainingItself.push(arrayDirectlyContainingItself);
+    arrayDirectlyContainingItself.push(&quot;WebKit!&quot;);
+    arrayDirectlyContainingItself.push(arrayDirectlyContainingItself);
+    arrayDirectlyContainingItself.toString();`, &quot;1,,WebKit!,&quot;);
+shouldBeEqualToString(`var arrayDirectlyContainingItself = [];
+    arrayDirectlyContainingItself.push(1);
+    arrayDirectlyContainingItself.push(arrayDirectlyContainingItself);
+    arrayDirectlyContainingItself.push(&quot;WebKit!&quot;);
+    arrayDirectlyContainingItself.push(arrayDirectlyContainingItself);
+    arrayDirectlyContainingItself.toLocaleString();`, &quot;1,,WebKit!,&quot;);
+shouldBeEqualToString(`var arrayDirectlyContainingItself = [];
+    arrayDirectlyContainingItself.push(1);
+    arrayDirectlyContainingItself.push(arrayDirectlyContainingItself);
+    arrayDirectlyContainingItself.push(&quot;WebKit!&quot;);
+    arrayDirectlyContainingItself.push(arrayDirectlyContainingItself);
+    arrayDirectlyContainingItself.join(&quot;-&quot;);`, &quot;1--WebKit!-&quot;);
+
+// Array indirectly containing itself.
+shouldBeEqualToString(`var arrayIndirectlyContainingItself = [];
+    arrayIndirectlyContainingItself.push(1);
+    arrayIndirectlyContainingItself.push([1, 2, [5, 6, [arrayIndirectlyContainingItself]]]);
+    arrayIndirectlyContainingItself.push(&quot;WebKit!&quot;);
+    arrayIndirectlyContainingItself.toString();`, &quot;1,1,2,5,6,,WebKit!&quot;);
+shouldBeEqualToString(`var arrayIndirectlyContainingItself = [];
+    arrayIndirectlyContainingItself.push(1);
+    arrayIndirectlyContainingItself.push([1, 2, [5, 6, [arrayIndirectlyContainingItself]]]);
+    arrayIndirectlyContainingItself.push(&quot;WebKit!&quot;);
+    arrayIndirectlyContainingItself.toLocaleString();`, &quot;1,1,2,5,6,,WebKit!&quot;);
+shouldBeEqualToString(`var arrayIndirectlyContainingItself = [];
+    arrayIndirectlyContainingItself.push(1);
+    arrayIndirectlyContainingItself.push([1, 2, [5, 6, [arrayIndirectlyContainingItself]]]);
+    arrayIndirectlyContainingItself.push(&quot;WebKit!&quot;);
+    arrayIndirectlyContainingItself.join(&quot;=&quot;);`, &quot;1=1,2,5,6,=WebKit!&quot;);
+
+// Array containing another array with recursion.
+shouldBeEqualToString(`var arrayIndirectlyContainingItself = [];
+    arrayIndirectlyContainingItself.push(1);
+    arrayIndirectlyContainingItself.push([1, 2, [5, 6, [arrayIndirectlyContainingItself]]]);
+    arrayIndirectlyContainingItself.push(&quot;WebKit!&quot;);
+    [&quot;z&quot;, arrayIndirectlyContainingItself, 9].toString();`, &quot;z,1,1,2,5,6,,WebKit!,9&quot;);
+shouldBeEqualToString(`var arrayIndirectlyContainingItself = [];
+    arrayIndirectlyContainingItself.push(1);
+    arrayIndirectlyContainingItself.push([1, 2, [5, 6, [arrayIndirectlyContainingItself]]]);
+    arrayIndirectlyContainingItself.push(&quot;WebKit!&quot;);
+    [&quot;z&quot;, arrayIndirectlyContainingItself, 9].toLocaleString();`, &quot;z,1,1,2,5,6,,WebKit!,9&quot;);
+shouldBeEqualToString(`var arrayIndirectlyContainingItself = [];
+    arrayIndirectlyContainingItself.push(1);
+    arrayIndirectlyContainingItself.push([1, 2, [5, 6, [arrayIndirectlyContainingItself]]]);
+    arrayIndirectlyContainingItself.push(&quot;WebKit!&quot;);
+    [&quot;z&quot;, arrayIndirectlyContainingItself, 9].join(&quot;&amp;&quot;);`, &quot;z&amp;1,1,2,5,6,,WebKit!&amp;9&quot;);
+
+// Indirectly recurse to an other of the functions. The object do not contains itself, but contains object that recursively call
+// an array to string conversion.
+shouldBeEqualToString(`var arrayIndirectlyConvertingItself = [&quot;a&quot;];
+    arrayIndirectlyConvertingItself.push({ array: arrayIndirectlyConvertingItself, toString: function() { return this.array.toString() } });
+    arrayIndirectlyConvertingItself.push({ array: arrayIndirectlyConvertingItself, toString: function() { return this.array.toLocaleString() } });
+    arrayIndirectlyConvertingItself.push({ array: arrayIndirectlyConvertingItself, toString: function() { return this.array.join(&quot;~&quot;) } });
+    arrayIndirectlyConvertingItself.push(&quot;WebKit!&quot;);
+    [&quot;z&quot;, arrayIndirectlyConvertingItself, 9].toString();`, &quot;z,a,,,,WebKit!,9&quot;);
+shouldBeEqualToString(`var arrayIndirectlyConvertingItself = [&quot;a&quot;];
+    arrayIndirectlyConvertingItself.push({ array: arrayIndirectlyConvertingItself, toString: function() { return this.array.toString() } });
+    arrayIndirectlyConvertingItself.push({ array: arrayIndirectlyConvertingItself, toString: function() { return this.array.toLocaleString() } });
+    arrayIndirectlyConvertingItself.push({ array: arrayIndirectlyConvertingItself, toString: function() { return this.array.join(&quot;~&quot;) } });
+    arrayIndirectlyConvertingItself.push(&quot;WebKit!&quot;);
+    [&quot;z&quot;, arrayIndirectlyConvertingItself, 9].toLocaleString();`, &quot;z,a,,,,WebKit!,9&quot;);
+shouldBeEqualToString(`var arrayIndirectlyConvertingItself = [&quot;a&quot;];
+    arrayIndirectlyConvertingItself.push({ array: arrayIndirectlyConvertingItself, toString: function() { return this.array.toString() } });
+    arrayIndirectlyConvertingItself.push({ array: arrayIndirectlyConvertingItself, toString: function() { return this.array.toLocaleString() } });
+    arrayIndirectlyConvertingItself.push({ array: arrayIndirectlyConvertingItself, toString: function() { return this.array.join(&quot;~&quot;) } });
+    arrayIndirectlyConvertingItself.push(&quot;WebKit!&quot;);
+    [&quot;z&quot;, arrayIndirectlyConvertingItself, 9].join(&quot;*&quot;);`, &quot;z*a,,,,WebKit!*9&quot;);
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (184446 => 184447)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2015-05-18 06:16:58 UTC (rev 184446)
+++ trunk/Source/JavaScriptCore/ChangeLog        2015-05-18 06:23:31 UTC (rev 184447)
</span><span class="lines">@@ -1,3 +1,29 @@
</span><ins>+2015-05-17  Benjamin Poulain  &lt;benjamin@webkit.org&gt;
+
+        [JSC] Make StringRecursionChecker faster in the simple cases without any recursion
+        https://bugs.webkit.org/show_bug.cgi?id=145102
+
+        Reviewed by Darin Adler.
+
+        In general, the array targeted by Array.toString() or Array.join() are pretty
+        simple. In those simple cases, we spend as much time in StringRecursionChecker
+        as we do on the actual operation.
+
+        The reason for this is the HashSet stringRecursionCheckVisitedObjects used
+        to detect recursion. We are constantly adding and removing objects which
+        dirty buckets and force constant rehash.
+
+        This patch adds a simple shortcut for those simple case: in addition to the HashSet,
+        we keep a pointer to the root object of the recursion.
+        In the vast majority of cases, we no longer touch the HashSet at all.
+
+        This patch is a 12% progression on the overall score of ArrayWeighted.
+
+        * runtime/StringRecursionChecker.h:
+        (JSC::StringRecursionChecker::performCheck):
+        (JSC::StringRecursionChecker::~StringRecursionChecker):
+        * runtime/VM.h:
+
</ins><span class="cx"> 2015-05-17  Filip Pizlo  &lt;fpizlo@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Insert store barriers late so that IR transformations don't have to worry about them
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeStringRecursionCheckerh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/StringRecursionChecker.h (184446 => 184447)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/StringRecursionChecker.h        2015-05-18 06:16:58 UTC (rev 184446)
+++ trunk/Source/JavaScriptCore/runtime/StringRecursionChecker.h        2015-05-18 06:23:31 UTC (rev 184447)
</span><span class="lines">@@ -52,7 +52,15 @@
</span><span class="cx">     VM&amp; vm = m_exec-&gt;vm();
</span><span class="cx">     if (!vm.isSafeToRecurse())
</span><span class="cx">         return throwStackOverflowError();
</span><del>-    bool alreadyVisited = !vm.stringRecursionCheckVisitedObjects.add(m_thisObject).isNewEntry;
</del><ins>+
+    bool alreadyVisited = false;
+    if (!vm.stringRecursionCheckFirstObject)
+        vm.stringRecursionCheckFirstObject = m_thisObject;
+    else if (vm.stringRecursionCheckFirstObject == m_thisObject)
+        alreadyVisited = true;
+    else
+        alreadyVisited = !vm.stringRecursionCheckVisitedObjects.add(m_thisObject).isNewEntry;
+
</ins><span class="cx">     if (alreadyVisited)
</span><span class="cx">         return emptyString(); // Return empty string to avoid infinite recursion.
</span><span class="cx">     return JSValue(); // Indicate success.
</span><span class="lines">@@ -74,8 +82,14 @@
</span><span class="cx"> {
</span><span class="cx">     if (m_earlyReturnValue)
</span><span class="cx">         return;
</span><del>-    ASSERT(m_exec-&gt;vm().stringRecursionCheckVisitedObjects.contains(m_thisObject));
-    m_exec-&gt;vm().stringRecursionCheckVisitedObjects.remove(m_thisObject);
</del><ins>+
+    VM&amp; vm = m_exec-&gt;vm();
+    if (vm.stringRecursionCheckFirstObject == m_thisObject)
+        vm.stringRecursionCheckFirstObject = nullptr;
+    else {
+        ASSERT(vm.stringRecursionCheckVisitedObjects.contains(m_thisObject));
+        vm.stringRecursionCheckVisitedObjects.remove(m_thisObject);
+    }
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeVMh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/VM.h (184446 => 184447)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/VM.h        2015-05-18 06:16:58 UTC (rev 184446)
+++ trunk/Source/JavaScriptCore/runtime/VM.h        2015-05-18 06:23:31 UTC (rev 184447)
</span><span class="lines">@@ -453,6 +453,7 @@
</span><span class="cx"> 
</span><span class="cx">     VMEntryScope* entryScope;
</span><span class="cx"> 
</span><ins>+    JSObject* stringRecursionCheckFirstObject { nullptr };
</ins><span class="cx">     HashSet&lt;JSObject*&gt; stringRecursionCheckVisitedObjects;
</span><span class="cx"> 
</span><span class="cx">     LocalTimeOffsetCache localTimeOffsetCache;
</span></span></pre>
</div>
</div>

</body>
</html>