<!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>[201467] trunk/Source/JavaScriptCore</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/201467">201467</a></dd>
<dt>Author</dt> <dd>fpizlo@apple.com</dd>
<dt>Date</dt> <dd>2016-05-27 13:45:08 -0700 (Fri, 27 May 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>regExpProtoFuncSplitFast should OOM before it swaps
https://bugs.webkit.org/show_bug.cgi?id=158157

Reviewed by Mark Lam.
        
This is a huge speed-up on some jsfunfuzz test cases because it makes us realize much
sooner that running a regexp split will result in swapping. It uses the same basic
approach as http://trac.webkit.org/changeset/201451: if the result array crosses a certain
size threshold, we proceed with a dry run to see how big the array will get before
allocating anything else. This way, bogus uses of split that would have OOMed only after
killing the user's machine will now OOM before killing the user's machine.
        
This is an enormous speed-up on some jsfunfuzz tests: they go from running for a long
time to running instantly.

* runtime/RegExpPrototype.cpp:
(JSC::advanceStringIndex):
(JSC::genericSplit):
(JSC::regExpProtoFuncSplitFast):
* runtime/StringObject.h:
(JSC::jsStringWithReuse):
(JSC::jsSubstring):
* tests/stress/big-split-captures.js: Added.
* tests/stress/big-split.js: Added.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeRegExpPrototypecpp">trunk/Source/JavaScriptCore/runtime/RegExpPrototype.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeStringObjecth">trunk/Source/JavaScriptCore/runtime/StringObject.h</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoretestsstressbigsplitcapturesjs">trunk/Source/JavaScriptCore/tests/stress/big-split-captures.js</a></li>
<li><a href="#trunkSourceJavaScriptCoretestsstressbigsplitjs">trunk/Source/JavaScriptCore/tests/stress/big-split.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (201466 => 201467)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2016-05-27 20:32:42 UTC (rev 201466)
+++ trunk/Source/JavaScriptCore/ChangeLog        2016-05-27 20:45:08 UTC (rev 201467)
</span><span class="lines">@@ -1,3 +1,30 @@
</span><ins>+2016-05-27  Filip Pizlo  &lt;fpizlo@apple.com&gt;
+
+        regExpProtoFuncSplitFast should OOM before it swaps
+        https://bugs.webkit.org/show_bug.cgi?id=158157
+
+        Reviewed by Mark Lam.
+        
+        This is a huge speed-up on some jsfunfuzz test cases because it makes us realize much
+        sooner that running a regexp split will result in swapping. It uses the same basic
+        approach as http://trac.webkit.org/changeset/201451: if the result array crosses a certain
+        size threshold, we proceed with a dry run to see how big the array will get before
+        allocating anything else. This way, bogus uses of split that would have OOMed only after
+        killing the user's machine will now OOM before killing the user's machine.
+        
+        This is an enormous speed-up on some jsfunfuzz tests: they go from running for a long
+        time to running instantly.
+
+        * runtime/RegExpPrototype.cpp:
+        (JSC::advanceStringIndex):
+        (JSC::genericSplit):
+        (JSC::regExpProtoFuncSplitFast):
+        * runtime/StringObject.h:
+        (JSC::jsStringWithReuse):
+        (JSC::jsSubstring):
+        * tests/stress/big-split-captures.js: Added.
+        * tests/stress/big-split.js: Added.
+
</ins><span class="cx"> 2016-05-27  Saam barati  &lt;sbarati@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         ShadowChicken/DebuggerCallFrame don't properly handle when the entry stack frame is a tail deleted frame
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeRegExpPrototypecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/RegExpPrototype.cpp (201466 => 201467)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/RegExpPrototype.cpp        2016-05-27 20:32:42 UTC (rev 201466)
+++ trunk/Source/JavaScriptCore/runtime/RegExpPrototype.cpp        2016-05-27 20:45:08 UTC (rev 201467)
</span><span class="lines">@@ -457,6 +457,84 @@
</span><span class="cx">     return RegExpObject::advanceStringUnicode(str, strSize, index);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+enum SplitControl {
+    ContinueSplit,
+    AbortSplit
+};
+
+template&lt;typename ControlFunc, typename PushFunc&gt;
+void genericSplit(
+    VM&amp; vm, RegExp* regexp, const String&amp; input, unsigned inputSize, unsigned&amp; position,
+    unsigned&amp; matchPosition, bool regExpIsSticky, bool regExpIsUnicode,
+    const ControlFunc&amp; control, const PushFunc&amp; push)
+{
+    while (matchPosition &lt; inputSize) {
+        if (control() == AbortSplit)
+            return;
+        
+        Vector&lt;int, 32&gt; ovector;
+
+        // a. Perform ? Set(splitter, &quot;lastIndex&quot;, q, true).
+        // b. Let z be ? RegExpExec(splitter, S).
+        int mpos = regexp-&gt;match(vm, input, matchPosition, ovector);
+
+        // c. If z is null, let q be AdvanceStringIndex(S, q, unicodeMatching).
+        if (mpos &lt; 0) {
+            if (!regExpIsSticky)
+                break;
+            matchPosition = advanceStringIndex(input, inputSize, matchPosition, regExpIsUnicode);
+            continue;
+        }
+        if (static_cast&lt;unsigned&gt;(mpos) &gt;= inputSize) {
+            // The spec redoes the RegExpExec starting at the next character of the input.
+            // But in our case, mpos &lt; 0 means that the native regexp already searched all permutations
+            // and know that we won't be able to find a match for the separator even if we redo the
+            // RegExpExec starting at the next character of the input. So, just bail.
+            break;
+        }
+
+        // d. Else, z is not null
+        //    i. Let e be ? ToLength(? Get(splitter, &quot;lastIndex&quot;)).
+        //   ii. Let e be min(e, size).
+        matchPosition = mpos;
+        unsigned matchEnd = ovector[1];
+
+        //  iii. If e = p, let q be AdvanceStringIndex(S, q, unicodeMatching).
+        if (matchEnd == position) {
+            matchPosition = advanceStringIndex(input, inputSize, matchPosition, regExpIsUnicode);
+            continue;
+        }
+        // if matchEnd == 0 then position should also be zero and thus matchEnd should equal position.
+        ASSERT(matchEnd);
+
+        //   iv. Else e != p,
+        unsigned numberOfCaptures = regexp-&gt;numSubpatterns();
+        
+        // 1. Let T be a String value equal to the substring of S consisting of the elements at indices p (inclusive) through q (exclusive).
+        // 2. Perform ! CreateDataProperty(A, ! ToString(lengthA), T).
+        if (push(true, position, matchPosition - position) == AbortSplit)
+            return;
+        
+        // 5. Let p be e.
+        position = matchEnd;
+        
+        // 6. Let numberOfCaptures be ? ToLength(? Get(z, &quot;length&quot;)).
+        // 7. Let numberOfCaptures be max(numberOfCaptures-1, 0).
+        // 8. Let i be 1.
+        // 9. Repeat, while i &lt;= numberOfCaptures,
+        for (unsigned i = 1; i &lt;= numberOfCaptures; ++i) {
+            // a. Let nextCapture be ? Get(z, ! ToString(i)).
+            // b. Perform ! CreateDataProperty(A, ! ToString(lengthA), nextCapture).
+            int sub = ovector[i * 2];
+            if (push(sub &gt;= 0, sub, ovector[i * 2 + 1] - sub) == AbortSplit)
+                return;
+        }
+        
+        // 10. Let q be p.
+        matchPosition = position;
+    }
+}
+
</ins><span class="cx"> // ES 21.2.5.11 RegExp.prototype[@@split](string, limit)
</span><span class="cx"> EncodedJSValue JSC_HOST_CALL regExpProtoFuncSplitFast(ExecState* exec)
</span><span class="cx"> {
</span><span class="lines">@@ -468,7 +546,8 @@
</span><span class="cx">     RegExp* regexp = asRegExpObject(thisValue)-&gt;regExp();
</span><span class="cx"> 
</span><span class="cx">     // 3. [handled by JS builtin] Let S be ? ToString(string).
</span><del>-    String input = exec-&gt;argument(0).toString(exec)-&gt;value(exec);
</del><ins>+    JSString* inputString = exec-&gt;argument(0).toString(exec);
+    String input = inputString-&gt;value(exec);
</ins><span class="cx">     if (vm.exception())
</span><span class="cx">         return JSValue::encode(jsUndefined());
</span><span class="cx">     ASSERT(!input.isNull());
</span><span class="lines">@@ -507,7 +586,7 @@
</span><span class="cx">         // c. Perform ! CreateDataProperty(A, &quot;0&quot;, S).
</span><span class="cx">         // d. Return A.
</span><span class="cx">         if (!regexp-&gt;match(vm, input, 0))
</span><del>-            result-&gt;putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input));
</del><ins>+            result-&gt;putDirectIndex(exec, 0, inputString);
</ins><span class="cx">         return JSValue::encode(result);
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -516,99 +595,79 @@
</span><span class="cx">     // 19. Repeat, while q &lt; size
</span><span class="cx">     bool regExpIsSticky = regexp-&gt;sticky();
</span><span class="cx">     bool regExpIsUnicode = regexp-&gt;unicode();
</span><del>-    while (matchPosition &lt; inputSize) {
-        Vector&lt;int, 32&gt; ovector;
-
-        // a. Perform ? Set(splitter, &quot;lastIndex&quot;, q, true).
-        // b. Let z be ? RegExpExec(splitter, S).
-        int mpos = regexp-&gt;match(vm, input, matchPosition, ovector);
-
-        // c. If z is null, let q be AdvanceStringIndex(S, q, unicodeMatching).
-        if (mpos &lt; 0) {
-            if (!regExpIsSticky)
-                break;
-            matchPosition = advanceStringIndex(input, inputSize, matchPosition, regExpIsUnicode);
-            continue;
-        }
-        if (static_cast&lt;unsigned&gt;(mpos) &gt;= inputSize) {
-            // The spec redoes the RegExpExec starting at the next character of the input.
-            // But in our case, mpos &lt; 0 means that the native regexp already searched all permutations
-            // and know that we won't be able to find a match for the separator even if we redo the
-            // RegExpExec starting at the next character of the input. So, just bail.
-            break;
-        }
-
-        // d. Else, z is not null
-        //    i. Let e be ? ToLength(? Get(splitter, &quot;lastIndex&quot;)).
-        //   ii. Let e be min(e, size).
-        matchPosition = mpos;
-        unsigned matchEnd = ovector[1];
-
-        //  iii. If e = p, let q be AdvanceStringIndex(S, q, unicodeMatching).
-        if (matchEnd == position) {
-            matchPosition = advanceStringIndex(input, inputSize, matchPosition, regExpIsUnicode);
-            continue;
-        }
-        // if matchEnd == 0 then position should also be zero and thus matchEnd should equal position.
-        ASSERT(matchEnd);
-
-        //   iv. Else e != p,
-        {
-            unsigned numberOfCaptures = regexp-&gt;numSubpatterns();
-            unsigned newResultLength = resultLength + numberOfCaptures + 1;
-            if (newResultLength &lt; numberOfCaptures || newResultLength &gt;= MAX_STORAGE_VECTOR_INDEX) {
-                // Let's consider what's best for users here. We're about to increase the length of
-                // the split array beyond the maximum length that we can support efficiently. This
-                // will cause us to use a HashMap for the new entries after this point. That's going
-                // to result in a very long running time of this function and very large memory
-                // usage. In my experiments, JSC will sit spinning for minutes after getting here and
-                // it was using &gt;4GB of memory and eventually grew to 8GB. It kept running without
-                // finishing until I killed it. That's probably not what the user wanted. The user,
-                // or the program that the user is running, probably made a mistake by calling this
-                // method in such a way that it resulted in such an obnoxious array. Therefore, to
-                // protect ourselves, we bail at this point.
-                throwOutOfMemoryError(exec);
-                return JSValue::encode(jsUndefined());
-            }
-
-            // 1. Let T be a String value equal to the substring of S consisting of the elements at indices p (inclusive) through q (exclusive).
-            // 2. Perform ! CreateDataProperty(A, ! ToString(lengthA), T).
-            result-&gt;putDirectIndex(exec, resultLength, jsSubstring(exec, thisValue, input, position, matchPosition - position));
-
-            // 3. Let lengthA be lengthA + 1.
-            // 4. If lengthA = lim, return A.
-            if (++resultLength == limit)
-                return JSValue::encode(result);
-
-            // 5. Let p be e.
-            position = matchEnd;
-
-            // 6. Let numberOfCaptures be ? ToLength(? Get(z, &quot;length&quot;)).
-            // 7. Let numberOfCaptures be max(numberOfCaptures-1, 0).
-            // 8. Let i be 1.
-            // 9. Repeat, while i &lt;= numberOfCaptures,
-            for (unsigned i = 1; i &lt;= numberOfCaptures; ++i) {
-                // a. Let nextCapture be ? Get(z, ! ToString(i)).
-                // b. Perform ! CreateDataProperty(A, ! ToString(lengthA), nextCapture).
-                int sub = ovector[i * 2];
-                result-&gt;putDirectIndex(exec, resultLength, sub &lt; 0 ? jsUndefined() : jsSubstring(exec, thisValue, input, sub, ovector[i * 2 + 1] - sub));
-
-                // c. Let i be i + 1.
-                // d. Let lengthA be lengthA + 1.
-                // e. If lengthA = lim, return A.
-                if (++resultLength == limit)
-                    return JSValue::encode(result);
-            }
-
-            // 10. Let q be p.
-            matchPosition = position;
-        }
</del><ins>+    
+    unsigned maxSizeForDirectPath = 100000;
+    
+    genericSplit(
+        vm, regexp, input, inputSize, position, matchPosition, regExpIsSticky, regExpIsUnicode,
+        [&amp;] () -&gt; SplitControl {
+            if (resultLength &gt;= maxSizeForDirectPath)
+                return AbortSplit;
+            return ContinueSplit;
+        },
+        [&amp;] (bool isDefined, unsigned start, unsigned length) -&gt; SplitControl {
+            result-&gt;putDirectIndex(exec, resultLength++, isDefined ? JSRopeString::createSubstringOfResolved(vm, inputString, start, length) : jsUndefined());
+            if (resultLength &gt;= limit)
+                return AbortSplit;
+            return ContinueSplit;
+        });
+    
+    if (resultLength &gt;= limit)
+        return JSValue::encode(result);
+    if (resultLength &lt; maxSizeForDirectPath) {
+        // 20. Let T be a String value equal to the substring of S consisting of the elements at indices p (inclusive) through size (exclusive).
+        // 21. Perform ! CreateDataProperty(A, ! ToString(lengthA), T).
+        result-&gt;putDirectIndex(exec, resultLength, JSRopeString::createSubstringOfResolved(vm, inputString, position, inputSize - position));
+        
+        // 22. Return A.
+        return JSValue::encode(result);
</ins><span class="cx">     }
</span><del>-
</del><ins>+    
+    // Now do a dry run to see how big things get. Give up if they get absurd.
+    unsigned savedPosition = position;
+    unsigned savedMatchPosition = matchPosition;
+    unsigned dryRunCount = 0;
+    genericSplit(
+        vm, regexp, input, inputSize, position, matchPosition, regExpIsSticky, regExpIsUnicode,
+        [&amp;] () -&gt; SplitControl {
+            if (resultLength + dryRunCount &gt;= MAX_STORAGE_VECTOR_LENGTH)
+                return AbortSplit;
+            return ContinueSplit;
+        },
+        [&amp;] (bool, unsigned, unsigned) -&gt; SplitControl {
+            dryRunCount++;
+            if (resultLength + dryRunCount &gt;= limit)
+                return AbortSplit;
+            return ContinueSplit;
+        });
+    
+    if (resultLength + dryRunCount &gt;= MAX_STORAGE_VECTOR_LENGTH) {
+        throwOutOfMemoryError(exec);
+        return JSValue::encode(jsUndefined());
+    }
+    
+    // OK, we know that if we finish the split, we won't have to OOM.
+    position = savedPosition;
+    matchPosition = savedMatchPosition;
+    
+    genericSplit(
+        vm, regexp, input, inputSize, position, matchPosition, regExpIsSticky, regExpIsUnicode,
+        [&amp;] () -&gt; SplitControl {
+            return ContinueSplit;
+        },
+        [&amp;] (bool isDefined, unsigned start, unsigned length) -&gt; SplitControl {
+            result-&gt;putDirectIndex(exec, resultLength++, isDefined ? JSRopeString::createSubstringOfResolved(vm, inputString, start, length) : jsUndefined());
+            if (resultLength &gt;= limit)
+                return AbortSplit;
+            return ContinueSplit;
+        });
+    
+    if (resultLength &gt;= limit)
+        return JSValue::encode(result);
+    
</ins><span class="cx">     // 20. Let T be a String value equal to the substring of S consisting of the elements at indices p (inclusive) through size (exclusive).
</span><span class="cx">     // 21. Perform ! CreateDataProperty(A, ! ToString(lengthA), T).
</span><del>-    result-&gt;putDirectIndex(exec, resultLength++, jsSubstring(exec, thisValue, input, position, inputSize - position));
-
</del><ins>+    result-&gt;putDirectIndex(exec, resultLength, JSRopeString::createSubstringOfResolved(vm, inputString, position, inputSize - position));
</ins><span class="cx">     // 22. Return A.
</span><span class="cx">     return JSValue::encode(result);
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeStringObjecth"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/StringObject.h (201466 => 201467)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/StringObject.h        2016-05-27 20:32:42 UTC (rev 201466)
+++ trunk/Source/JavaScriptCore/runtime/StringObject.h        2016-05-27 20:45:08 UTC (rev 201467)
</span><span class="lines">@@ -84,6 +84,9 @@
</span><span class="cx"> // Helper for producing a JSString for 'string', where 'string' was been produced by
</span><span class="cx"> // calling ToString on 'originalValue'. In cases where 'originalValue' already was a
</span><span class="cx"> // string primitive we can just use this, otherwise we need to allocate a new JSString.
</span><ins>+// FIXME: Basically any use of this is bad. toString() returns a JSString* so we don't need to
+// pass around the originalValue; we could just pass around the JSString*. Then you don't need
+// this function. You just use the JSString* that toString() returned.
</ins><span class="cx"> static inline JSString* jsStringWithReuse(ExecState* exec, JSValue originalValue, const String&amp; string)
</span><span class="cx"> {
</span><span class="cx">     if (originalValue.isString()) {
</span><span class="lines">@@ -94,10 +97,9 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> // Helper that tries to use the JSString substring sharing mechanism if 'originalValue' is a JSString.
</span><del>-// FIXME: It would be even better if toString returned a JSString*, or if anyone who called
-// toString with the intent of later calling this functon first created a jsString from the String
-// that toString returned. That way, we'd get the substring optimization even when the input was
-// not a JSString.
</del><ins>+// FIXME: Basically any use of this is bad. toString() returns a JSString* so we don't need to
+// pass around the originalValue; we could just pass around the JSString*. And since we've
+// resolved it, we know that we can just allocate the substring cell directly.
</ins><span class="cx"> // https://bugs.webkit.org/show_bug.cgi?id=158140
</span><span class="cx"> static inline JSString* jsSubstring(ExecState* exec, JSValue originalValue, const String&amp; string, unsigned offset, unsigned length)
</span><span class="cx"> {
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoretestsstressbigsplitcapturesjs"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/tests/stress/big-split-captures.js (0 => 201467)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/stress/big-split-captures.js                                (rev 0)
+++ trunk/Source/JavaScriptCore/tests/stress/big-split-captures.js        2016-05-27 20:45:08 UTC (rev 201467)
</span><span class="lines">@@ -0,0 +1,27 @@
</span><ins>+&quot;use strict&quot;;
+
+var bigString = &quot;xyz&quot;;
+while (bigString.length &lt; 200000)
+    bigString = bigString + bigString;
+
+if (bigString.length != 393216)
+    throw &quot;Error: bad string length: &quot; + bigString.length;
+
+var result = /(x)(y)(z)/[Symbol.split](bigString);
+
+if (result.length != 524289)
+    throw &quot;Error: bad result array length: &quot; + result.length;
+
+if (result[0] != &quot;&quot;)
+    throw &quot;Error: array does not start with an empty string.&quot;;
+
+for (var i = 1; i &lt; result.length; i += 4) {
+    if (result[i + 0] != &quot;x&quot;)
+        throw &quot;Error: array does not contain \&quot;x\&quot; at i = &quot; + i + &quot; + 0: &quot; + result[i + 0];
+    if (result[i + 1] != &quot;y&quot;)
+        throw &quot;Error: array does not contain \&quot;y\&quot; at i = &quot; + i + &quot; + 1: &quot; + result[i + 1];
+    if (result[i + 2] != &quot;z&quot;)
+        throw &quot;Error: array does not contain \&quot;z\&quot; at i = &quot; + i + &quot; + 2: &quot; + result[i + 2];
+    if (result[i + 3] != &quot;&quot;)
+        throw &quot;Error: array does not contain \&quot;\&quot; at i = &quot; + i + &quot; + 3: &quot; + result[i + 3];
+}
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoretestsstressbigsplitjs"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/tests/stress/big-split.js (0 => 201467)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/stress/big-split.js                                (rev 0)
+++ trunk/Source/JavaScriptCore/tests/stress/big-split.js        2016-05-27 20:45:08 UTC (rev 201467)
</span><span class="lines">@@ -0,0 +1,21 @@
</span><ins>+&quot;use strict&quot;;
+
+var bigString = &quot;xy&quot;;
+while (bigString.length &lt; 200000)
+    bigString = bigString + bigString;
+
+if (bigString.length != 262144)
+    throw &quot;Error: bad string length: &quot; + bigString.length;
+
+var result = /x/[Symbol.split](bigString);
+
+if (result.length != 131073)
+    throw &quot;Error: bad result array length: &quot; + result.length;
+
+if (result[0] != &quot;&quot;)
+    throw &quot;Error: array does not start with an empty string.&quot;;
+
+for (var i = 1; i &lt; result.length; ++i) {
+    if (result[i] != &quot;y&quot;)
+        throw &quot;Error: array does not contain \&quot;y\&quot; at i = &quot; + i + &quot;: &quot; + result[i];
+}
</ins></span></pre>
</div>
</div>

</body>
</html>