<!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>[201451] 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/201451">201451</a></dd>
<dt>Author</dt> <dd>fpizlo@apple.com</dd>
<dt>Date</dt> <dd>2016-05-27 07:59:46 -0700 (Fri, 27 May 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>Bogus uses of regexp matching should realize that they will OOM before they start swapping
https://bugs.webkit.org/show_bug.cgi?id=158142

Reviewed by Michael Saboff.
        
Refactored the RegExpObject::matchGlobal() code so that there is less duplication. Took
advantage of this to make the code more resilient in case of absurd situations: if the
result array gets large, it proceeds with a dry run to detect how many matches there will
be. This allows it to OOM before it starts swapping.
        
This also improves the overall performance of the code by using lightweight substrings and
skipping the whole intermediate argument array.
        
This makes some jsfunfuzz tests run a lot faster and use a lot less memory.
        
* builtins/RegExpPrototype.js:
* CMakeLists.txt:
* JavaScriptCore.xcodeproj/project.pbxproj:
* runtime/MatchResult.cpp: Added.
(JSC::MatchResult::dump):
* runtime/MatchResult.h:
(JSC::MatchResult::empty):
(MatchResult::empty): Deleted.
* runtime/RegExpObject.cpp:
(JSC::RegExpObject::match):
(JSC::collectMatches):
(JSC::RegExpObject::matchGlobal):
* runtime/StringObject.h:
(JSC::jsStringWithReuse):
(JSC::jsSubstring):
* tests/stress/big-match.js: Added. Make sure that this optimization doesn't break big matches.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoreCMakeListstxt">trunk/Source/JavaScriptCore/CMakeLists.txt</a></li>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreJavaScriptCorexcodeprojprojectpbxproj">trunk/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj</a></li>
<li><a href="#trunkSourceJavaScriptCorebuiltinsRegExpPrototypejs">trunk/Source/JavaScriptCore/builtins/RegExpPrototype.js</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeMatchResulth">trunk/Source/JavaScriptCore/runtime/MatchResult.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeRegExpObjectcpp">trunk/Source/JavaScriptCore/runtime/RegExpObject.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeStringObjecth">trunk/Source/JavaScriptCore/runtime/StringObject.h</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoreruntimeMatchResultcpp">trunk/Source/JavaScriptCore/runtime/MatchResult.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoretestsstressbigmatchjs">trunk/Source/JavaScriptCore/tests/stress/big-match.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceJavaScriptCoreCMakeListstxt"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/CMakeLists.txt (201450 => 201451)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/CMakeLists.txt        2016-05-27 13:51:02 UTC (rev 201450)
+++ trunk/Source/JavaScriptCore/CMakeLists.txt        2016-05-27 14:59:46 UTC (rev 201451)
</span><span class="lines">@@ -744,6 +744,7 @@
</span><span class="cx">     runtime/MapConstructor.cpp
</span><span class="cx">     runtime/MapIteratorPrototype.cpp
</span><span class="cx">     runtime/MapPrototype.cpp
</span><ins>+    runtime/MatchResult.cpp
</ins><span class="cx">     runtime/MathCommon.cpp
</span><span class="cx">     runtime/MathObject.cpp
</span><span class="cx">     runtime/MemoryStatistics.cpp
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (201450 => 201451)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2016-05-27 13:51:02 UTC (rev 201450)
+++ trunk/Source/JavaScriptCore/ChangeLog        2016-05-27 14:59:46 UTC (rev 201451)
</span><span class="lines">@@ -1,3 +1,37 @@
</span><ins>+2016-05-26  Filip Pizlo  &lt;fpizlo@apple.com&gt;
+
+        Bogus uses of regexp matching should realize that they will OOM before they start swapping
+        https://bugs.webkit.org/show_bug.cgi?id=158142
+
+        Reviewed by Michael Saboff.
+        
+        Refactored the RegExpObject::matchGlobal() code so that there is less duplication. Took
+        advantage of this to make the code more resilient in case of absurd situations: if the
+        result array gets large, it proceeds with a dry run to detect how many matches there will
+        be. This allows it to OOM before it starts swapping.
+        
+        This also improves the overall performance of the code by using lightweight substrings and
+        skipping the whole intermediate argument array.
+        
+        This makes some jsfunfuzz tests run a lot faster and use a lot less memory.
+        
+        * builtins/RegExpPrototype.js:
+        * CMakeLists.txt:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * runtime/MatchResult.cpp: Added.
+        (JSC::MatchResult::dump):
+        * runtime/MatchResult.h:
+        (JSC::MatchResult::empty):
+        (MatchResult::empty): Deleted.
+        * runtime/RegExpObject.cpp:
+        (JSC::RegExpObject::match):
+        (JSC::collectMatches):
+        (JSC::RegExpObject::matchGlobal):
+        * runtime/StringObject.h:
+        (JSC::jsStringWithReuse):
+        (JSC::jsSubstring):
+        * tests/stress/big-match.js: Added. Make sure that this optimization doesn't break big matches.
+
</ins><span class="cx"> 2016-05-26  Gavin &amp; Ellie Barraclough  &lt;barraclough@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Static table property lookup should not require getOwnPropertySlot override.
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreJavaScriptCorexcodeprojprojectpbxproj"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj (201450 => 201451)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj        2016-05-27 13:51:02 UTC (rev 201450)
+++ trunk/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj        2016-05-27 14:59:46 UTC (rev 201451)
</span><span class="lines">@@ -1987,6 +1987,7 @@
</span><span class="cx">                 DC605B5E1CE26EA200593718 /* ProfilerEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = DC605B5A1CE26E9800593718 /* ProfilerEvent.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="cx">                 DC605B5F1CE26EA500593718 /* ProfilerUID.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DC605B5B1CE26E9800593718 /* ProfilerUID.cpp */; };
</span><span class="cx">                 DC605B601CE26EA700593718 /* ProfilerUID.h in Headers */ = {isa = PBXBuildFile; fileRef = DC605B5C1CE26E9800593718 /* ProfilerUID.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><ins>+                DC69AA661CF7A1F200C6272F /* MatchResult.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DC69AA651CF7A1EF00C6272F /* MatchResult.cpp */; };
</ins><span class="cx">                 DC7997831CDE9FA0004D4A09 /* TagRegistersMode.h in Headers */ = {isa = PBXBuildFile; fileRef = DC7997821CDE9F9E004D4A09 /* TagRegistersMode.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="cx">                 DC7997841CDE9FA2004D4A09 /* TagRegistersMode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DC7997811CDE9F9E004D4A09 /* TagRegistersMode.cpp */; };
</span><span class="cx">                 DCEE22091CEB9895000C2396 /* DFGBackwardsCFG.h in Headers */ = {isa = PBXBuildFile; fileRef = DCEE22061CEB9890000C2396 /* DFGBackwardsCFG.h */; };
</span><span class="lines">@@ -4200,6 +4201,7 @@
</span><span class="cx">                 DC605B5A1CE26E9800593718 /* ProfilerEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ProfilerEvent.h; path = profiler/ProfilerEvent.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 DC605B5B1CE26E9800593718 /* ProfilerUID.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ProfilerUID.cpp; path = profiler/ProfilerUID.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 DC605B5C1CE26E9800593718 /* ProfilerUID.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ProfilerUID.h; path = profiler/ProfilerUID.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><ins>+                DC69AA651CF7A1EF00C6272F /* MatchResult.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MatchResult.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</ins><span class="cx">                 DC7997811CDE9F9E004D4A09 /* TagRegistersMode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TagRegistersMode.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 DC7997821CDE9F9E004D4A09 /* TagRegistersMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TagRegistersMode.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 DCEE22061CEB9890000C2396 /* DFGBackwardsCFG.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGBackwardsCFG.h; path = dfg/DFGBackwardsCFG.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="lines">@@ -5504,12 +5506,6 @@
</span><span class="cx">                 7EF6E0BB0EB7A1EC0079AFAF /* runtime */ = {
</span><span class="cx">                         isa = PBXGroup;
</span><span class="cx">                         children = (
</span><del>-                                708EBE231CE8F35000453146 /* IntlObjectInlines.h */,
-                                DCF3D5641CD29468003D5C65 /* LazyClassStructure.cpp */,
-                                DCF3D5651CD29468003D5C65 /* LazyClassStructure.h */,
-                                DCF3D5661CD29468003D5C65 /* LazyClassStructureInlines.h */,
-                                DCF3D5671CD29468003D5C65 /* LazyProperty.h */,
-                                DCF3D5681CD29468003D5C65 /* LazyPropertyInlines.h */,
</del><span class="cx">                                 BCF605110E203EF800B9A64D /* ArgList.cpp */,
</span><span class="cx">                                 BCF605120E203EF800B9A64D /* ArgList.h */,
</span><span class="cx">                                 0FE0500C1AA9091100D33B33 /* ArgumentsMode.h */,
</span><span class="lines">@@ -5684,6 +5680,7 @@
</span><span class="cx">                                 A1D792FB1B43864B004516F5 /* IntlNumberFormatPrototype.h */,
</span><span class="cx">                                 A12BBFF31B044A9800664B69 /* IntlObject.cpp */,
</span><span class="cx">                                 A12BBFF11B044A8B00664B69 /* IntlObject.h */,
</span><ins>+                                708EBE231CE8F35000453146 /* IntlObjectInlines.h */,
</ins><span class="cx">                                 86BF642A148DB2B5004DE36A /* Intrinsic.h */,
</span><span class="cx">                                 FE4D55B71AE716CA0052E459 /* IterationStatus.h */,
</span><span class="cx">                                 70113D491A8DB093003848C4 /* IteratorOperations.cpp */,
</span><span class="lines">@@ -5839,6 +5836,11 @@
</span><span class="cx">                                 1442566015EDE98D0066A49B /* JSWithScope.h */,
</span><span class="cx">                                 65C7A1710A8EAACB00FA37EA /* JSWrapperObject.cpp */,
</span><span class="cx">                                 65C7A1720A8EAACB00FA37EA /* JSWrapperObject.h */,
</span><ins>+                                DCF3D5641CD29468003D5C65 /* LazyClassStructure.cpp */,
+                                DCF3D5651CD29468003D5C65 /* LazyClassStructure.h */,
+                                DCF3D5661CD29468003D5C65 /* LazyClassStructureInlines.h */,
+                                DCF3D5671CD29468003D5C65 /* LazyProperty.h */,
+                                DCF3D5681CD29468003D5C65 /* LazyPropertyInlines.h */,
</ins><span class="cx">                                 A7E2EA6A0FB460CF00601F06 /* LiteralParser.cpp */,
</span><span class="cx">                                 A7E2EA690FB460CF00601F06 /* LiteralParser.h */,
</span><span class="cx">                                 F692A8680255597D01FF60F7 /* Lookup.cpp */,
</span><span class="lines">@@ -5851,6 +5853,7 @@
</span><span class="cx">                                 A74DEF8E182D991400522C22 /* MapIteratorPrototype.h */,
</span><span class="cx">                                 A700873B17CBE8D300C3E643 /* MapPrototype.cpp */,
</span><span class="cx">                                 A700873C17CBE8D300C3E643 /* MapPrototype.h */,
</span><ins>+                                DC69AA651CF7A1EF00C6272F /* MatchResult.cpp */,
</ins><span class="cx">                                 8612E4CB1522918400C836BE /* MatchResult.h */,
</span><span class="cx">                                 4340A4821A9051AF00D73CCA /* MathCommon.cpp */,
</span><span class="cx">                                 4340A4831A9051AF00D73CCA /* MathCommon.h */,
</span><span class="lines">@@ -9223,6 +9226,7 @@
</span><span class="cx">                                 14469DE1107EC7E700650446 /* NativeErrorPrototype.cpp in Sources */,
</span><span class="cx">                                 E33E8D201B9013DE00346B52 /* NativeStdFunctionCell.cpp in Sources */,
</span><span class="cx">                                 148F21B7107EC5470042EC2C /* Nodes.cpp in Sources */,
</span><ins>+                                DC69AA661CF7A1F200C6272F /* MatchResult.cpp in Sources */,
</ins><span class="cx">                                 E3963CEE1B73F75000EB4CE5 /* NodesAnalyzeModule.cpp in Sources */,
</span><span class="cx">                                 655EB29B10CE2581001A990E /* NodesCodegen.cpp in Sources */,
</span><span class="cx">                                 6546F5211A32B313006F07D5 /* NullGetterFunction.cpp in Sources */,
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorebuiltinsRegExpPrototypejs"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/builtins/RegExpPrototype.js (201450 => 201451)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/builtins/RegExpPrototype.js        2016-05-27 13:51:02 UTC (rev 201450)
+++ trunk/Source/JavaScriptCore/builtins/RegExpPrototype.js        2016-05-27 14:59:46 UTC (rev 201451)
</span><span class="lines">@@ -98,6 +98,9 @@
</span><span class="cx">     regexp.lastIndex = 0;
</span><span class="cx">     let resultList = [];
</span><span class="cx"> 
</span><ins>+    // FIXME: It would be great to implement a solution similar to what we do in
+    // RegExpObject::matchGlobal(). It's not clear if this is possible, since this loop has
+    // effects. https://bugs.webkit.org/show_bug.cgi?id=158145
</ins><span class="cx">     const maximumReasonableMatchSize = 100000000;
</span><span class="cx"> 
</span><span class="cx">     while (true) {
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeMatchResultcpp"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/runtime/MatchResult.cpp (0 => 201451)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/MatchResult.cpp                                (rev 0)
+++ trunk/Source/JavaScriptCore/runtime/MatchResult.cpp        2016-05-27 14:59:46 UTC (rev 201451)
</span><span class="lines">@@ -0,0 +1,40 @@
</span><ins>+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include &quot;config.h&quot;
+#include &quot;MatchResult.h&quot;
+
+namespace JSC {
+
+void MatchResult::dump(PrintStream&amp; out) const
+{
+    if (start == WTF::notFound)
+        out.print(&quot;notFound&quot;);
+    else
+        out.print(start, &quot;...&quot;, end);
+}
+
+} // namespace JSC
+
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeMatchResulth"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/MatchResult.h (201450 => 201451)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/MatchResult.h        2016-05-27 13:51:02 UTC (rev 201450)
+++ trunk/Source/JavaScriptCore/runtime/MatchResult.h        2016-05-27 14:59:46 UTC (rev 201451)
</span><span class="lines">@@ -1,5 +1,5 @@
</span><span class="cx"> /*
</span><del>- * Copyright (C) 2012 Apple Inc. All rights reserved.
</del><ins>+ * Copyright (C) 2012, 2016 Apple Inc. All rights reserved.
</ins><span class="cx">  *
</span><span class="cx">  * Redistribution and use in source and binary forms, with or without
</span><span class="cx">  * modification, are permitted provided that the following conditions
</span><span class="lines">@@ -26,6 +26,11 @@
</span><span class="cx"> #ifndef MatchResult_h
</span><span class="cx"> #define MatchResult_h
</span><span class="cx"> 
</span><ins>+#include &lt;wtf/PrintStream.h&gt;
+#include &lt;wtf/Vector.h&gt; // for notFound
+
+namespace JSC {
+
</ins><span class="cx"> typedef uint64_t EncodedMatchResult;
</span><span class="cx"> 
</span><span class="cx"> struct MatchResult {
</span><span class="lines">@@ -69,9 +74,13 @@
</span><span class="cx">     {
</span><span class="cx">         return start == end;
</span><span class="cx">     }
</span><ins>+    
+    void dump(PrintStream&amp;) const;
</ins><span class="cx"> 
</span><span class="cx">     size_t start;
</span><span class="cx">     size_t end;
</span><span class="cx"> };
</span><span class="cx"> 
</span><ins>+} // namespace JSC
+
</ins><span class="cx"> #endif
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeRegExpObjectcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/RegExpObject.cpp (201450 => 201451)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/RegExpObject.cpp        2016-05-27 13:51:02 UTC (rev 201450)
+++ trunk/Source/JavaScriptCore/runtime/RegExpObject.cpp        2016-05-27 14:59:46 UTC (rev 201451)
</span><span class="lines">@@ -169,6 +169,65 @@
</span><span class="cx">     return matchInline(exec, globalObject, string);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+template&lt;typename FixEndFunc&gt;
+JSValue collectMatches(VM&amp; vm, ExecState* exec, JSString* string, const String&amp; s, RegExpConstructor* constructor, RegExp* regExp, const FixEndFunc&amp; fixEnd)
+{
+    MatchResult result = constructor-&gt;performMatch(vm, regExp, string, s, 0);
+    if (!result)
+        return jsNull();
+    
+    static unsigned maxSizeForDirectPath = 100000;
+    
+    JSArray* array = constructEmptyArray(exec, nullptr);
+
+    auto iterate = [&amp;] () {
+        size_t end = result.end;
+        size_t length = end - result.start;
+        array-&gt;push(exec, JSRopeString::createSubstringOfResolved(vm, string, result.start, length));
+        if (!length)
+            end = fixEnd(end);
+        result = constructor-&gt;performMatch(vm, regExp, string, s, end);
+    };
+    
+    do {
+        if (array-&gt;length() &gt;= maxSizeForDirectPath) {
+            // First do a throw-away match to see how many matches we'll get.
+            unsigned matchCount = 0;
+            MatchResult savedResult = result;
+            do {
+                if (array-&gt;length() + matchCount &gt;= MAX_STORAGE_VECTOR_LENGTH) {
+                    throwOutOfMemoryError(exec);
+                    return jsUndefined();
+                }
+                
+                size_t end = result.end;
+                matchCount++;
+                if (result.empty())
+                    end = fixEnd(end);
+                
+                // Using RegExpConstructor::performMatch() instead of calling RegExp::match()
+                // directly is a surprising but profitable choice: it means that when we do OOM, we
+                // will leave the cached result in the state it ought to have had just before the
+                // OOM! On the other hand, if this loop concludes that the result is small enough,
+                // then the iterate() loop below will overwrite the cached result anyway.
+                result = constructor-&gt;performMatch(vm, regExp, string, s, end);
+            } while (result);
+            
+            // OK, we have a sensible number of matches. Now we can create them for reals.
+            result = savedResult;
+            do
+                iterate();
+            while (result);
+            
+            return array;
+        }
+        
+        iterate();
+    } while (result);
+    
+    return array;
+}
+
</ins><span class="cx"> JSValue RegExpObject::matchGlobal(ExecState* exec, JSGlobalObject* globalObject, JSString* string)
</span><span class="cx"> {
</span><span class="cx">     RegExp* regExp = this-&gt;regExp();
</span><span class="lines">@@ -183,52 +242,21 @@
</span><span class="cx"> 
</span><span class="cx">     String s = string-&gt;value(exec);
</span><span class="cx">     RegExpConstructor* regExpConstructor = globalObject-&gt;regExpConstructor();
</span><del>-    MatchResult result = regExpConstructor-&gt;performMatch(*vm, regExp, string, s, 0);
-
-    // return array of matches
-    MarkedArgumentBuffer list;
-    // We defend ourselves from crazy.
-    const size_t maximumReasonableMatchSize = 1000000000;
-
</del><ins>+    
</ins><span class="cx">     if (regExp-&gt;unicode()) {
</span><span class="cx">         unsigned stringLength = s.length();
</span><del>-        while (result) {
-            if (list.size() &gt; maximumReasonableMatchSize) {
-                throwOutOfMemoryError(exec);
-                return jsUndefined();
-            }
-
-            size_t end = result.end;
-            size_t length = end - result.start;
-            list.append(jsSubstring(exec, s, result.start, length));
-            if (!length)
-                end = advanceStringUnicode(s, stringLength, end);
-            result = regExpConstructor-&gt;performMatch(*vm, regExp, string, s, end);
-        }
-    } else {
-        while (result) {
-            if (list.size() &gt; maximumReasonableMatchSize) {
-                throwOutOfMemoryError(exec);
-                return jsUndefined();
-            }
-
-            size_t end = result.end;
-            size_t length = end - result.start;
-            list.append(jsSubstring(exec, s, result.start, length));
-            if (!length)
-                ++end;
-            result = regExpConstructor-&gt;performMatch(*vm, regExp, string, s, end);
-        }
</del><ins>+        return collectMatches(
+            *vm, exec, string, s, regExpConstructor, regExp,
+            [&amp;] (size_t end) -&gt; size_t {
+                return advanceStringUnicode(s, stringLength, end);
+            });
</ins><span class="cx">     }
</span><del>-
-    if (list.isEmpty()) {
-        // if there are no matches at all, it's important to return
-        // Null instead of an empty array, because this matches
-        // other browsers and because Null is a false value.
-        return jsNull();
-    }
-
-    return constructArray(exec, static_cast&lt;ArrayAllocationProfile*&gt;(0), list);
</del><ins>+    
+    return collectMatches(
+        *vm, exec, string, s, regExpConstructor, regExp,
+        [&amp;] (size_t end) -&gt; size_t {
+            return end + 1;
+        });
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> } // namespace JSC
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeStringObjecth"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/StringObject.h (201450 => 201451)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/StringObject.h        2016-05-27 13:51:02 UTC (rev 201450)
+++ trunk/Source/JavaScriptCore/runtime/StringObject.h        2016-05-27 14:59:46 UTC (rev 201451)
</span><span class="lines">@@ -94,6 +94,11 @@
</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><ins>+// 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.
+// https://bugs.webkit.org/show_bug.cgi?id=158140
</ins><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 class="cx">     if (originalValue.isString()) {
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoretestsstressbigmatchjs"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/tests/stress/big-match.js (0 => 201451)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/stress/big-match.js                                (rev 0)
+++ trunk/Source/JavaScriptCore/tests/stress/big-match.js        2016-05-27 14:59:46 UTC (rev 201451)
</span><span class="lines">@@ -0,0 +1,20 @@
</span><ins>+&quot;use strict&quot;;
+
+var bigString = &quot;x&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/g[Symbol.match](bigString);
+
+if (result.length != 262144)
+    throw &quot;Error: bad result array length: &quot; + result.length;
+
+for (var i = 0; i &lt; result.length; ++i) {
+    if (result[i] != &quot;x&quot;)
+        throw &quot;Error: array does not contain \&quot;x\&quot; at i = &quot; + i + &quot;: &quot; + result[i];
+}
+
+
</ins></span></pre>
</div>
</div>

</body>
</html>