<!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>[200946] 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/200946">200946</a></dd>
<dt>Author</dt> <dd>msaboff@apple.com</dd>
<dt>Date</dt> <dd>2016-05-16 10:40:15 -0700 (Mon, 16 May 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>RegExp /y flag incorrect handling of mixed-length alternation
https://bugs.webkit.org/show_bug.cgi?id=157723

Reviewed by Filip Pizlo.

Source/JavaScriptCore:

Previously for sticky patterns, we were bailing out and exiting when backtracking
alternatives with dissimilar match lengths.  Deleted that code.  Instead, for
sticky patterns we need to process the backtracking except for advancing to the
next input index.

* yarr/YarrJIT.cpp:
(JSC::Yarr::YarrGenerator::backtrack):

LayoutTests:

Added tests for alternatives with shorter to longer lengths.

* js/regexp-sticky-expected.txt:
* js/script-tests/regexp-sticky.js:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsjsregexpstickyexpectedtxt">trunk/LayoutTests/js/regexp-sticky-expected.txt</a></li>
<li><a href="#trunkLayoutTestsjsscripttestsregexpstickyjs">trunk/LayoutTests/js/script-tests/regexp-sticky.js</a></li>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreyarrYarrJITcpp">trunk/Source/JavaScriptCore/yarr/YarrJIT.cpp</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (200945 => 200946)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2016-05-16 17:35:30 UTC (rev 200945)
+++ trunk/LayoutTests/ChangeLog        2016-05-16 17:40:15 UTC (rev 200946)
</span><span class="lines">@@ -1,3 +1,15 @@
</span><ins>+2016-05-16  Michael Saboff  &lt;msaboff@apple.com&gt;
+
+        RegExp /y flag incorrect handling of mixed-length alternation
+        https://bugs.webkit.org/show_bug.cgi?id=157723
+
+        Reviewed by Filip Pizlo.
+
+        Added tests for alternatives with shorter to longer lengths.
+
+        * js/regexp-sticky-expected.txt:
+        * js/script-tests/regexp-sticky.js:
+
</ins><span class="cx"> 2016-05-16  Brent Fulgham  &lt;bfulgham@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         REGRESSION (r192098): Content missing after copy and paste to Notes App on retina displays
</span></span></pre></div>
<a id="trunkLayoutTestsjsregexpstickyexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/js/regexp-sticky-expected.txt (200945 => 200946)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/js/regexp-sticky-expected.txt        2016-05-16 17:35:30 UTC (rev 200945)
+++ trunk/LayoutTests/js/regexp-sticky-expected.txt        2016-05-16 17:40:15 UTC (rev 200946)
</span><span class="lines">@@ -6,7 +6,9 @@
</span><span class="cx"> PASS Repeating Pattern
</span><span class="cx"> PASS Test lastIndex resets
</span><span class="cx"> PASS Ignore Case
</span><del>-PASS Alternates
</del><ins>+PASS Alternates, differing lengths long to short
+PASS Alternates, differing lengths long to short with mutliple matches 
+PASS Alternates, differing lengths, short to long
</ins><span class="cx"> PASS BOL Anchored, starting at 0
</span><span class="cx"> PASS BOL Anchored, starting at 1
</span><span class="cx"> PASS EOL Anchored, not at EOL
</span></span></pre></div>
<a id="trunkLayoutTestsjsscripttestsregexpstickyjs"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/js/script-tests/regexp-sticky.js (200945 => 200946)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/js/script-tests/regexp-sticky.js        2016-05-16 17:35:30 UTC (rev 200945)
+++ trunk/LayoutTests/js/script-tests/regexp-sticky.js        2016-05-16 17:40:15 UTC (rev 200946)
</span><span class="lines">@@ -72,7 +72,9 @@
</span><span class="cx"> testStickyExec(&quot;Repeating Pattern&quot;, new RegExp(&quot;abc&quot;, &quot;y&quot;), &quot;abcabcabc&quot;, 0, [&quot;abc&quot;, &quot;abc&quot;, &quot;abc&quot;, null]);
</span><span class="cx"> testStickyExec(&quot;Test lastIndex resets&quot;, /\d/y, &quot;12345&quot;, 0, [&quot;1&quot;, &quot;2&quot;, &quot;3&quot;, &quot;4&quot;, &quot;5&quot;, null, &quot;1&quot;, &quot;2&quot;, &quot;3&quot;, &quot;4&quot;, &quot;5&quot;, null]);
</span><span class="cx"> testStickyExec(&quot;Ignore Case&quot;, new RegExp(&quot;test&quot;, &quot;iy&quot;), &quot;TESTtestTest&quot;, 0, [&quot;TEST&quot;, &quot;test&quot;, &quot;Test&quot;, null]);
</span><del>-testStickyExec(&quot;Alternates&quot;, new RegExp(&quot;Dog |Cat |Mouse &quot;, &quot;y&quot;), &quot;Mouse Dog Cat &quot;, 0, [&quot;Mouse &quot;, &quot;Dog &quot;, &quot;Cat &quot;, null]);
</del><ins>+testStickyExec(&quot;Alternates, differing lengths long to short&quot;, new RegExp(&quot;bb|a&quot;, &quot;y&quot;), &quot;a&quot;, 0, [&quot;a&quot;, null]);
+testStickyExec(&quot;Alternates, differing lengths long to short with mutliple matches &quot;, new RegExp(&quot;abc|ab|a&quot;, &quot;y&quot;), &quot;aababcaabcab&quot;, 0, [&quot;a&quot;, &quot;ab&quot;, &quot;abc&quot;, &quot;a&quot;, &quot;abc&quot;, &quot;ab&quot;, null]);
+testStickyExec(&quot;Alternates, differing lengths, short to long&quot;, new RegExp(&quot;Dog |Cat |Mouse &quot;, &quot;y&quot;), &quot;Mouse Dog Cat &quot;, 0, [&quot;Mouse &quot;, &quot;Dog &quot;, &quot;Cat &quot;, null]);
</ins><span class="cx"> testStickyExec(&quot;BOL Anchored, starting at 0&quot;, /^X/y, &quot;XXX&quot;, 0, [&quot;X&quot;, null]);
</span><span class="cx"> testStickyExec(&quot;BOL Anchored, starting at 1&quot;, /^X/y, &quot;XXX&quot;, 1, [null, &quot;X&quot;, null]);
</span><span class="cx"> testStickyExec(&quot;EOL Anchored, not at EOL&quot;, /#$/y, &quot;##&quot;, 0, [null]);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (200945 => 200946)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2016-05-16 17:35:30 UTC (rev 200945)
+++ trunk/Source/JavaScriptCore/ChangeLog        2016-05-16 17:40:15 UTC (rev 200946)
</span><span class="lines">@@ -1,3 +1,18 @@
</span><ins>+2016-05-15  Michael Saboff  &lt;msaboff@apple.com&gt;
+
+        RegExp /y flag incorrect handling of mixed-length alternation
+        https://bugs.webkit.org/show_bug.cgi?id=157723
+
+        Reviewed by Filip Pizlo.
+
+        Previously for sticky patterns, we were bailing out and exiting when backtracking
+        alternatives with dissimilar match lengths.  Deleted that code.  Instead, for
+        sticky patterns we need to process the backtracking except for advancing to the
+        next input index.
+
+        * yarr/YarrJIT.cpp:
+        (JSC::Yarr::YarrGenerator::backtrack):
+
</ins><span class="cx"> 2016-05-15  Filip Pizlo  &lt;fpizlo@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         DFG::Plan shouldn't read from its VM once it's been cancelled
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreyarrYarrJITcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/yarr/YarrJIT.cpp (200945 => 200946)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/yarr/YarrJIT.cpp        2016-05-16 17:35:30 UTC (rev 200945)
+++ trunk/Source/JavaScriptCore/yarr/YarrJIT.cpp        2016-05-16 17:40:15 UTC (rev 200946)
</span><span class="lines">@@ -1888,20 +1888,6 @@
</span><span class="cx">                     }
</span><span class="cx">                 }
</span><span class="cx"> 
</span><del>-                if (m_pattern.sticky()) {
-                    // We have failed matching from the initial index and we're a sticky expression.
-                    // We are done matching. Link failures for any reason to here.
-                    YarrOp* tempOp = beginOp;
-                    do {
-                        tempOp-&gt;m_jumps.link(this);
-                        tempOp = &amp;m_ops[tempOp-&gt;m_nextOp];
-                    } while (tempOp-&gt;m_op != OpBodyAlternativeEnd);
-
-                    removeCallFrame();
-                    generateFailReturn();
-                    break;
-                }
-
</del><span class="cx">                 // We can reach this point in the code in two ways:
</span><span class="cx">                 //  - Fallthrough from the code above (a repeating alternative backtracked out of its
</span><span class="cx">                 //    last alternative, and did not have sufficent input to run the first).
</span><span class="lines">@@ -1965,52 +1951,53 @@
</span><span class="cx">                     needsToUpdateMatchStart = false;
</span><span class="cx">                 }
</span><span class="cx"> 
</span><del>-                // Check whether there is sufficient input to loop. Increment the input position by
-                // one, and check. Also add in the minimum disjunction size before checking - there
-                // is no point in looping if we're just going to fail all the input checks around
-                // the next iteration.
-                ASSERT(alternative-&gt;m_minimumSize &gt;= m_pattern.m_body-&gt;m_minimumSize);
-                if (alternative-&gt;m_minimumSize == m_pattern.m_body-&gt;m_minimumSize) {
-                    // If the last alternative had the same minimum size as the disjunction,
-                    // just simply increment input pos by 1, no adjustment based on minimum size.
-                    add32(TrustedImm32(1), index);
-                } else {
-                    // If the minumum for the last alternative was one greater than than that
-                    // for the disjunction, we're already progressed by 1, nothing to do!
-                    unsigned delta = (alternative-&gt;m_minimumSize - m_pattern.m_body-&gt;m_minimumSize) - 1;
-                    if (delta)
-                        sub32(Imm32(delta), index);
-                }
-                Jump matchFailed = jumpIfNoAvailableInput();
</del><ins>+                if (!m_pattern.sticky()) {
+                    // Check whether there is sufficient input to loop. Increment the input position by
+                    // one, and check. Also add in the minimum disjunction size before checking - there
+                    // is no point in looping if we're just going to fail all the input checks around
+                    // the next iteration.
+                    ASSERT(alternative-&gt;m_minimumSize &gt;= m_pattern.m_body-&gt;m_minimumSize);
+                    if (alternative-&gt;m_minimumSize == m_pattern.m_body-&gt;m_minimumSize) {
+                        // If the last alternative had the same minimum size as the disjunction,
+                        // just simply increment input pos by 1, no adjustment based on minimum size.
+                        add32(TrustedImm32(1), index);
+                    } else {
+                        // If the minumum for the last alternative was one greater than than that
+                        // for the disjunction, we're already progressed by 1, nothing to do!
+                        unsigned delta = (alternative-&gt;m_minimumSize - m_pattern.m_body-&gt;m_minimumSize) - 1;
+                        if (delta)
+                            sub32(Imm32(delta), index);
+                    }
+                    Jump matchFailed = jumpIfNoAvailableInput();
</ins><span class="cx"> 
</span><del>-                if (needsToUpdateMatchStart) {
-                    if (!m_pattern.m_body-&gt;m_minimumSize)
-                        setMatchStart(index);
</del><ins>+                    if (needsToUpdateMatchStart) {
+                        if (!m_pattern.m_body-&gt;m_minimumSize)
+                            setMatchStart(index);
+                        else {
+                            move(index, regT0);
+                            sub32(Imm32(m_pattern.m_body-&gt;m_minimumSize), regT0);
+                            setMatchStart(regT0);
+                        }
+                    }
+
+                    // Calculate how much more input the first alternative requires than the minimum
+                    // for the body as a whole. If no more is needed then we dont need an additional
+                    // input check here - jump straight back up to the start of the first alternative.
+                    if (beginOp-&gt;m_alternative-&gt;m_minimumSize == m_pattern.m_body-&gt;m_minimumSize)
+                        jump(beginOp-&gt;m_reentry);
</ins><span class="cx">                     else {
</span><del>-                        move(index, regT0);
-                        sub32(Imm32(m_pattern.m_body-&gt;m_minimumSize), regT0);
-                        setMatchStart(regT0);
</del><ins>+                        if (beginOp-&gt;m_alternative-&gt;m_minimumSize &gt; m_pattern.m_body-&gt;m_minimumSize)
+                            add32(Imm32(beginOp-&gt;m_alternative-&gt;m_minimumSize - m_pattern.m_body-&gt;m_minimumSize), index);
+                        else
+                            sub32(Imm32(m_pattern.m_body-&gt;m_minimumSize - beginOp-&gt;m_alternative-&gt;m_minimumSize), index);
+                        checkInput().linkTo(beginOp-&gt;m_reentry, this);
+                        jump(firstInputCheckFailed);
</ins><span class="cx">                     }
</span><del>-                }
</del><span class="cx"> 
</span><del>-                // Calculate how much more input the first alternative requires than the minimum
-                // for the body as a whole. If no more is needed then we dont need an additional
-                // input check here - jump straight back up to the start of the first alternative.
-                if (beginOp-&gt;m_alternative-&gt;m_minimumSize == m_pattern.m_body-&gt;m_minimumSize)
-                    jump(beginOp-&gt;m_reentry);
-                else {
-                    if (beginOp-&gt;m_alternative-&gt;m_minimumSize &gt; m_pattern.m_body-&gt;m_minimumSize)
-                        add32(Imm32(beginOp-&gt;m_alternative-&gt;m_minimumSize - m_pattern.m_body-&gt;m_minimumSize), index);
-                    else
-                        sub32(Imm32(m_pattern.m_body-&gt;m_minimumSize - beginOp-&gt;m_alternative-&gt;m_minimumSize), index);
-                    checkInput().linkTo(beginOp-&gt;m_reentry, this);
-                    jump(firstInputCheckFailed);
</del><ins>+                    // We jump to here if we iterate to the point that there is insufficient input to
+                    // run any matches, and need to return a failure state from JIT code.
+                    matchFailed.link(this);
</ins><span class="cx">                 }
</span><del>-
-                // We jump to here if we iterate to the point that there is insufficient input to
-                // run any matches, and need to return a failure state from JIT code.
-                matchFailed.link(this);
-
</del><span class="cx">                 removeCallFrame();
</span><span class="cx">                 generateFailReturn();
</span><span class="cx">                 break;
</span></span></pre>
</div>
</div>

</body>
</html>