<!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>[210854] branches/safari-603-branch</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/210854">210854</a></dd>
<dt>Author</dt> <dd>matthew_hanson@apple.com</dd>
<dt>Date</dt> <dd>2017-01-18 10:45:12 -0800 (Wed, 18 Jan 2017)</dd>
</dl>

<h3>Log Message</h3>
<pre>Merge <a href="http://trac.webkit.org/projects/webkit/changeset/210837">r210837</a>. rdar://problem/29432371</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#branchessafari603branchJSTestsChangeLog">branches/safari-603-branch/JSTests/ChangeLog</a></li>
<li><a href="#branchessafari603branchSourceJavaScriptCoreChangeLog">branches/safari-603-branch/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#branchessafari603branchSourceJavaScriptCoreyarrYarrInterpretercpp">branches/safari-603-branch/Source/JavaScriptCore/yarr/YarrInterpreter.cpp</a></li>
<li><a href="#branchessafari603branchSourceJavaScriptCoreyarrYarrInterpreterh">branches/safari-603-branch/Source/JavaScriptCore/yarr/YarrInterpreter.h</a></li>
<li><a href="#branchessafari603branchSourceJavaScriptCoreyarrYarrJITcpp">branches/safari-603-branch/Source/JavaScriptCore/yarr/YarrJIT.cpp</a></li>
<li><a href="#branchessafari603branchSourceJavaScriptCoreyarrYarrPatterncpp">branches/safari-603-branch/Source/JavaScriptCore/yarr/YarrPattern.cpp</a></li>
<li><a href="#branchessafari603branchSourceJavaScriptCoreyarrYarrPatternh">branches/safari-603-branch/Source/JavaScriptCore/yarr/YarrPattern.h</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#branchessafari603branchJSTestsmicrobenchmarksregexpnestednonzeromincountedparensjs">branches/safari-603-branch/JSTests/microbenchmarks/regexp-nested-nonzero-min-counted-parens.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="branchessafari603branchJSTestsChangeLog"></a>
<div class="modfile"><h4>Modified: branches/safari-603-branch/JSTests/ChangeLog (210853 => 210854)</h4>
<pre class="diff"><span>
<span class="info">--- branches/safari-603-branch/JSTests/ChangeLog        2017-01-18 18:37:30 UTC (rev 210853)
+++ branches/safari-603-branch/JSTests/ChangeLog        2017-01-18 18:45:12 UTC (rev 210854)
</span><span class="lines">@@ -1,3 +1,18 @@
</span><ins>+2017-01-18  Matthew Hanson  &lt;matthew_hanson@apple.com&gt;
+
+        Merge r210837. rdar://problem/29432371
+
+    2017-01-17  Michael Saboff  &lt;msaboff@apple.com&gt;
+
+            Nested parenthesized regular expressions with non-zero minimum counts appear to hang and use lots of memory
+            https://bugs.webkit.org/show_bug.cgi?id=167125
+
+            Reviewed by Filip Pizlo.
+
+            * microbenchmarks/regexp-nested-nonzero-min-counted-parens.js: Added.
+            New test with limits that run slow and take a reasonable amount of memory
+            before the change and run fast, using little memory with the change.
+
</ins><span class="cx"> 2017-01-12  Matthew Hanson  &lt;matthew_hanson@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Merge r210563. rdar://problem/29940224
</span></span></pre></div>
<a id="branchessafari603branchJSTestsmicrobenchmarksregexpnestednonzeromincountedparensjs"></a>
<div class="addfile"><h4>Added: branches/safari-603-branch/JSTests/microbenchmarks/regexp-nested-nonzero-min-counted-parens.js (0 => 210854)</h4>
<pre class="diff"><span>
<span class="info">--- branches/safari-603-branch/JSTests/microbenchmarks/regexp-nested-nonzero-min-counted-parens.js                                (rev 0)
+++ branches/safari-603-branch/JSTests/microbenchmarks/regexp-nested-nonzero-min-counted-parens.js        2017-01-18 18:45:12 UTC (rev 210854)
</span><span class="lines">@@ -0,0 +1,11 @@
</span><ins>+// This checks that we didn't regress the performance of regular expressions with nested, counted parenthesis
+// where the minimum count is not 0.
+
+/$($($($($($($($($($($($($($($($($($($($(${-2,16}+)+)+)+)+)+)+)+)+)+)+)+)+)+)+)+)+)+)+)+)+/.exec(&quot;a&quot;);
+
+/$(?:$(?:$(?:$(?:$(?:$(?:$(?:$(?:$(?:$(?:$(?:$(?:$(?:$(?:$(?:$(?:$(?:$(?:$(?:$(?:${-2,16}+)+)+)+)+)+)+)+)+)+)+)+)+)+)+)+)+)+)+)+)+/.exec(&quot;a&quot;);
+
+/$(?=$(?=$(?=$(?=$(?=$(?=$(?=$(?=$(?=$(?=$(?=$(?=$(?=$(?=$(?=$(?=$(?=$(?=$(?=$(?=${-2,16}+)+)+)+)+)+)+)+)+)+)+)+)+)+)+)+)+)+)+)+)+/.exec(&quot;a&quot;);
+
+
+
</ins></span></pre></div>
<a id="branchessafari603branchSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: branches/safari-603-branch/Source/JavaScriptCore/ChangeLog (210853 => 210854)</h4>
<pre class="diff"><span>
<span class="info">--- branches/safari-603-branch/Source/JavaScriptCore/ChangeLog        2017-01-18 18:37:30 UTC (rev 210853)
+++ branches/safari-603-branch/Source/JavaScriptCore/ChangeLog        2017-01-18 18:45:12 UTC (rev 210854)
</span><span class="lines">@@ -1,3 +1,83 @@
</span><ins>+2017-01-18  Matthew Hanson  &lt;matthew_hanson@apple.com&gt;
+
+        Merge r210837. rdar://problem/29432371
+
+    2017-01-17  Michael Saboff  &lt;msaboff@apple.com&gt;
+
+            Nested parenthesized regular expressions with non-zero minimum counts appear to hang and use lots of memory
+            https://bugs.webkit.org/show_bug.cgi?id=167125
+
+            Reviewed by Filip Pizlo.
+
+            Changed Yarr to handle nested parenthesized subexpressions where the minimum count is
+            not 0 directly in the Yarr interpreter.  Previously we'd factor an expression like
+            (a|b)+ into (a|b)(a|b)* with special handling for captures.  This factoring was done
+            using a deep copy that doubled the size of the resulting expresion for each nested
+            parenthesized subexpression.  Now the Yarr interpreter can directly process a regexp
+            like (a|b){2,42}.
+
+            The parser will allow one level of nested, non-zero minimum, counted parenthesis using
+            the old copy method.  After one level, it will generate parenthesis terms with a non-zero
+            minimum.   Such an expression wasn't handled by the Yarr JIT before the change, so this
+            change isn't a performance regression.
+
+            Added a minimum count to the YarrPattern and ByteTerm classes, and then factored that
+            minimum into the interpreter.  A non-zero minimum is only handled by the Yarr interpreter.
+            If the Yarr JIT see such a term, it punts back to the interpreter.
+
+            * yarr/YarrInterpreter.cpp:
+            (JSC::Yarr::Interpreter::backtrackPatternCharacter):
+            (JSC::Yarr::Interpreter::backtrackPatternCasedCharacter):
+            (JSC::Yarr::Interpreter::matchCharacterClass):
+            (JSC::Yarr::Interpreter::backtrackCharacterClass):
+            (JSC::Yarr::Interpreter::matchBackReference):
+            (JSC::Yarr::Interpreter::backtrackBackReference):
+            (JSC::Yarr::Interpreter::matchParenthesesOnceBegin):
+            (JSC::Yarr::Interpreter::matchParenthesesOnceEnd):
+            (JSC::Yarr::Interpreter::backtrackParenthesesOnceBegin):
+            (JSC::Yarr::Interpreter::backtrackParenthesesOnceEnd):
+            (JSC::Yarr::Interpreter::matchParenthesesTerminalBegin):
+            (JSC::Yarr::Interpreter::backtrackParenthesesTerminalBegin):
+            (JSC::Yarr::Interpreter::matchParentheticalAssertionBegin):
+            (JSC::Yarr::Interpreter::matchParentheticalAssertionEnd):
+            (JSC::Yarr::Interpreter::backtrackParentheticalAssertionBegin):
+            (JSC::Yarr::Interpreter::backtrackParentheticalAssertionEnd):
+            (JSC::Yarr::Interpreter::matchParentheses):
+            (JSC::Yarr::Interpreter::backtrackParentheses):
+            (JSC::Yarr::Interpreter::matchDisjunction):
+            (JSC::Yarr::ByteCompiler::atomPatternCharacter):
+            (JSC::Yarr::ByteCompiler::atomCharacterClass):
+            (JSC::Yarr::ByteCompiler::atomBackReference):
+            (JSC::Yarr::ByteCompiler::atomParentheticalAssertionEnd):
+            (JSC::Yarr::ByteCompiler::atomParenthesesSubpatternEnd):
+            (JSC::Yarr::ByteCompiler::atomParenthesesOnceEnd):
+            (JSC::Yarr::ByteCompiler::atomParenthesesTerminalEnd):
+            (JSC::Yarr::ByteCompiler::emitDisjunction):
+            * yarr/YarrInterpreter.h:
+            (JSC::Yarr::ByteTerm::ByteTerm):
+            * yarr/YarrJIT.cpp:
+            (JSC::Yarr::YarrGenerator::generatePatternCharacterOnce):
+            (JSC::Yarr::YarrGenerator::generatePatternCharacterFixed):
+            (JSC::Yarr::YarrGenerator::generatePatternCharacterGreedy):
+            (JSC::Yarr::YarrGenerator::backtrackPatternCharacterNonGreedy):
+            (JSC::Yarr::YarrGenerator::generateCharacterClassFixed):
+            (JSC::Yarr::YarrGenerator::generateCharacterClassGreedy):
+            (JSC::Yarr::YarrGenerator::backtrackCharacterClassNonGreedy):
+            (JSC::Yarr::YarrGenerator::generateTerm):
+            (JSC::Yarr::YarrGenerator::backtrackTerm):
+            (JSC::Yarr::YarrGenerator::generate):
+            (JSC::Yarr::YarrGenerator::backtrack):
+            (JSC::Yarr::YarrGenerator::opCompileParenthesesSubpattern):
+            * yarr/YarrPattern.cpp:
+            (JSC::Yarr::YarrPatternConstructor::copyTerm):
+            (JSC::Yarr::YarrPatternConstructor::quantifyAtom):
+            (JSC::Yarr::YarrPatternConstructor::checkForTerminalParentheses):
+            (JSC::Yarr::YarrPattern::YarrPattern):
+            * yarr/YarrPattern.h:
+            (JSC::Yarr::PatternTerm::PatternTerm):
+            (JSC::Yarr::PatternTerm::quantify):
+            (JSC::Yarr::YarrPattern::reset):
+
</ins><span class="cx"> 2017-01-13  Matthew Hanson  &lt;matthew_hanson@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Merge r210694. rdar://problem/29983526
</span></span></pre></div>
<a id="branchessafari603branchSourceJavaScriptCoreyarrYarrInterpretercpp"></a>
<div class="modfile"><h4>Modified: branches/safari-603-branch/Source/JavaScriptCore/yarr/YarrInterpreter.cpp (210853 => 210854)</h4>
<pre class="diff"><span>
<span class="info">--- branches/safari-603-branch/Source/JavaScriptCore/yarr/YarrInterpreter.cpp        2017-01-18 18:37:30 UTC (rev 210853)
+++ branches/safari-603-branch/Source/JavaScriptCore/yarr/YarrInterpreter.cpp        2017-01-18 18:45:12 UTC (rev 210854)
</span><span class="lines">@@ -438,7 +438,7 @@
</span><span class="cx">             break;
</span><span class="cx"> 
</span><span class="cx">         case QuantifierNonGreedy:
</span><del>-            if ((backTrack-&gt;matchAmount &lt; term.atom.quantityCount) &amp;&amp; input.checkInput(1)) {
</del><ins>+            if ((backTrack-&gt;matchAmount &lt; term.atom.quantityMaxCount) &amp;&amp; input.checkInput(1)) {
</ins><span class="cx">                 ++backTrack-&gt;matchAmount;
</span><span class="cx">                 if (checkCharacter(term.atom.patternCharacter, term.inputPosition + 1))
</span><span class="cx">                     return true;
</span><span class="lines">@@ -467,7 +467,7 @@
</span><span class="cx">             break;
</span><span class="cx"> 
</span><span class="cx">         case QuantifierNonGreedy:
</span><del>-            if ((backTrack-&gt;matchAmount &lt; term.atom.quantityCount) &amp;&amp; input.checkInput(1)) {
</del><ins>+            if ((backTrack-&gt;matchAmount &lt; term.atom.quantityMaxCount) &amp;&amp; input.checkInput(1)) {
</ins><span class="cx">                 ++backTrack-&gt;matchAmount;
</span><span class="cx">                 if (checkCasedCharacter(term.atom.casedCharacter.lo, term.atom.casedCharacter.hi, term.inputPosition + 1))
</span><span class="cx">                     return true;
</span><span class="lines">@@ -489,7 +489,7 @@
</span><span class="cx">             if (unicode) {
</span><span class="cx">                 backTrack-&gt;begin = input.getPos();
</span><span class="cx">                 unsigned matchAmount = 0;
</span><del>-                for (matchAmount = 0; matchAmount &lt; term.atom.quantityCount; ++matchAmount) {
</del><ins>+                for (matchAmount = 0; matchAmount &lt; term.atom.quantityMaxCount; ++matchAmount) {
</ins><span class="cx">                     if (!checkCharacterClass(term.atom.characterClass, term.invert(), term.inputPosition - matchAmount)) {
</span><span class="cx">                         input.setPos(backTrack-&gt;begin);
</span><span class="cx">                         return false;
</span><span class="lines">@@ -499,7 +499,7 @@
</span><span class="cx">                 return true;
</span><span class="cx">             }
</span><span class="cx"> 
</span><del>-            for (unsigned matchAmount = 0; matchAmount &lt; term.atom.quantityCount; ++matchAmount) {
</del><ins>+            for (unsigned matchAmount = 0; matchAmount &lt; term.atom.quantityMaxCount; ++matchAmount) {
</ins><span class="cx">                 if (!checkCharacterClass(term.atom.characterClass, term.invert(), term.inputPosition - matchAmount))
</span><span class="cx">                     return false;
</span><span class="cx">             }
</span><span class="lines">@@ -510,7 +510,7 @@
</span><span class="cx">             unsigned position = input.getPos();
</span><span class="cx">             backTrack-&gt;begin = position;
</span><span class="cx">             unsigned matchAmount = 0;
</span><del>-            while ((matchAmount &lt; term.atom.quantityCount) &amp;&amp; input.checkInput(1)) {
</del><ins>+            while ((matchAmount &lt; term.atom.quantityMaxCount) &amp;&amp; input.checkInput(1)) {
</ins><span class="cx">                 if (!checkCharacterClass(term.atom.characterClass, term.invert(), term.inputPosition + 1)) {
</span><span class="cx">                     input.setPos(position);
</span><span class="cx">                     break;
</span><span class="lines">@@ -565,7 +565,7 @@
</span><span class="cx">             break;
</span><span class="cx"> 
</span><span class="cx">         case QuantifierNonGreedy:
</span><del>-            if ((backTrack-&gt;matchAmount &lt; term.atom.quantityCount) &amp;&amp; input.checkInput(1)) {
</del><ins>+            if ((backTrack-&gt;matchAmount &lt; term.atom.quantityMaxCount) &amp;&amp; input.checkInput(1)) {
</ins><span class="cx">                 ++backTrack-&gt;matchAmount;
</span><span class="cx">                 if (checkCharacterClass(term.atom.characterClass, term.invert(), term.inputPosition + 1))
</span><span class="cx">                     return true;
</span><span class="lines">@@ -602,7 +602,7 @@
</span><span class="cx">         switch (term.atom.quantityType) {
</span><span class="cx">         case QuantifierFixedCount: {
</span><span class="cx">             backTrack-&gt;begin = input.getPos();
</span><del>-            for (unsigned matchAmount = 0; matchAmount &lt; term.atom.quantityCount; ++matchAmount) {
</del><ins>+            for (unsigned matchAmount = 0; matchAmount &lt; term.atom.quantityMaxCount; ++matchAmount) {
</ins><span class="cx">                 if (!tryConsumeBackReference(matchBegin, matchEnd, term.inputPosition)) {
</span><span class="cx">                     input.setPos(backTrack-&gt;begin);
</span><span class="cx">                     return false;
</span><span class="lines">@@ -613,7 +613,7 @@
</span><span class="cx"> 
</span><span class="cx">         case QuantifierGreedy: {
</span><span class="cx">             unsigned matchAmount = 0;
</span><del>-            while ((matchAmount &lt; term.atom.quantityCount) &amp;&amp; tryConsumeBackReference(matchBegin, matchEnd, term.inputPosition))
</del><ins>+            while ((matchAmount &lt; term.atom.quantityMaxCount) &amp;&amp; tryConsumeBackReference(matchBegin, matchEnd, term.inputPosition))
</ins><span class="cx">                 ++matchAmount;
</span><span class="cx">             backTrack-&gt;matchAmount = matchAmount;
</span><span class="cx">             return true;
</span><span class="lines">@@ -647,7 +647,7 @@
</span><span class="cx"> 
</span><span class="cx">         switch (term.atom.quantityType) {
</span><span class="cx">         case QuantifierFixedCount:
</span><del>-            // for quantityCount == 1, could rewind.
</del><ins>+            // for quantityMaxCount == 1, could rewind.
</ins><span class="cx">             input.setPos(backTrack-&gt;begin);
</span><span class="cx">             break;
</span><span class="cx"> 
</span><span class="lines">@@ -660,7 +660,7 @@
</span><span class="cx">             break;
</span><span class="cx"> 
</span><span class="cx">         case QuantifierNonGreedy:
</span><del>-            if ((backTrack-&gt;matchAmount &lt; term.atom.quantityCount) &amp;&amp; tryConsumeBackReference(matchBegin, matchEnd, term.inputPosition)) {
</del><ins>+            if ((backTrack-&gt;matchAmount &lt; term.atom.quantityMaxCount) &amp;&amp; tryConsumeBackReference(matchBegin, matchEnd, term.inputPosition)) {
</ins><span class="cx">                 ++backTrack-&gt;matchAmount;
</span><span class="cx">                 return true;
</span><span class="cx">             }
</span><span class="lines">@@ -708,7 +708,7 @@
</span><span class="cx">     bool matchParenthesesOnceBegin(ByteTerm&amp; term, DisjunctionContext* context)
</span><span class="cx">     {
</span><span class="cx">         ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternOnceBegin);
</span><del>-        ASSERT(term.atom.quantityCount == 1);
</del><ins>+        ASSERT(term.atom.quantityMaxCount == 1);
</ins><span class="cx"> 
</span><span class="cx">         BackTrackInfoParenthesesOnce* backTrack = reinterpret_cast&lt;BackTrackInfoParenthesesOnce*&gt;(context-&gt;frame + term.frameLocation);
</span><span class="cx"> 
</span><span class="lines">@@ -738,7 +738,7 @@
</span><span class="cx">     bool matchParenthesesOnceEnd(ByteTerm&amp; term, DisjunctionContext* context)
</span><span class="cx">     {
</span><span class="cx">         ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternOnceEnd);
</span><del>-        ASSERT(term.atom.quantityCount == 1);
</del><ins>+        ASSERT(term.atom.quantityMaxCount == 1);
</ins><span class="cx"> 
</span><span class="cx">         if (term.capture()) {
</span><span class="cx">             unsigned subpatternId = term.atom.subpatternId;
</span><span class="lines">@@ -755,7 +755,7 @@
</span><span class="cx">     bool backtrackParenthesesOnceBegin(ByteTerm&amp; term, DisjunctionContext* context)
</span><span class="cx">     {
</span><span class="cx">         ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternOnceBegin);
</span><del>-        ASSERT(term.atom.quantityCount == 1);
</del><ins>+        ASSERT(term.atom.quantityMaxCount == 1);
</ins><span class="cx"> 
</span><span class="cx">         BackTrackInfoParenthesesOnce* backTrack = reinterpret_cast&lt;BackTrackInfoParenthesesOnce*&gt;(context-&gt;frame + term.frameLocation);
</span><span class="cx"> 
</span><span class="lines">@@ -785,7 +785,7 @@
</span><span class="cx">     bool backtrackParenthesesOnceEnd(ByteTerm&amp; term, DisjunctionContext* context)
</span><span class="cx">     {
</span><span class="cx">         ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternOnceEnd);
</span><del>-        ASSERT(term.atom.quantityCount == 1);
</del><ins>+        ASSERT(term.atom.quantityMaxCount == 1);
</ins><span class="cx"> 
</span><span class="cx">         BackTrackInfoParenthesesOnce* backTrack = reinterpret_cast&lt;BackTrackInfoParenthesesOnce*&gt;(context-&gt;frame + term.frameLocation);
</span><span class="cx"> 
</span><span class="lines">@@ -823,7 +823,7 @@
</span><span class="cx">     {
</span><span class="cx">         ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternTerminalBegin);
</span><span class="cx">         ASSERT(term.atom.quantityType == QuantifierGreedy);
</span><del>-        ASSERT(term.atom.quantityCount == quantifyInfinite);
</del><ins>+        ASSERT(term.atom.quantityMaxCount == quantifyInfinite);
</ins><span class="cx">         ASSERT(!term.capture());
</span><span class="cx"> 
</span><span class="cx">         BackTrackInfoParenthesesTerminal* backTrack = reinterpret_cast&lt;BackTrackInfoParenthesesTerminal*&gt;(context-&gt;frame + term.frameLocation);
</span><span class="lines">@@ -849,7 +849,7 @@
</span><span class="cx">     {
</span><span class="cx">         ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternTerminalBegin);
</span><span class="cx">         ASSERT(term.atom.quantityType == QuantifierGreedy);
</span><del>-        ASSERT(term.atom.quantityCount == quantifyInfinite);
</del><ins>+        ASSERT(term.atom.quantityMaxCount == quantifyInfinite);
</ins><span class="cx">         ASSERT(!term.capture());
</span><span class="cx"> 
</span><span class="cx">         // If we backtrack to this point, we have failed to match this iteration of the parens.
</span><span class="lines">@@ -869,7 +869,7 @@
</span><span class="cx">     bool matchParentheticalAssertionBegin(ByteTerm&amp; term, DisjunctionContext* context)
</span><span class="cx">     {
</span><span class="cx">         ASSERT(term.type == ByteTerm::TypeParentheticalAssertionBegin);
</span><del>-        ASSERT(term.atom.quantityCount == 1);
</del><ins>+        ASSERT(term.atom.quantityMaxCount == 1);
</ins><span class="cx"> 
</span><span class="cx">         BackTrackInfoParentheticalAssertion* backTrack = reinterpret_cast&lt;BackTrackInfoParentheticalAssertion*&gt;(context-&gt;frame + term.frameLocation);
</span><span class="cx"> 
</span><span class="lines">@@ -880,7 +880,7 @@
</span><span class="cx">     bool matchParentheticalAssertionEnd(ByteTerm&amp; term, DisjunctionContext* context)
</span><span class="cx">     {
</span><span class="cx">         ASSERT(term.type == ByteTerm::TypeParentheticalAssertionEnd);
</span><del>-        ASSERT(term.atom.quantityCount == 1);
</del><ins>+        ASSERT(term.atom.quantityMaxCount == 1);
</ins><span class="cx"> 
</span><span class="cx">         BackTrackInfoParentheticalAssertion* backTrack = reinterpret_cast&lt;BackTrackInfoParentheticalAssertion*&gt;(context-&gt;frame + term.frameLocation);
</span><span class="cx"> 
</span><span class="lines">@@ -898,7 +898,7 @@
</span><span class="cx">     bool backtrackParentheticalAssertionBegin(ByteTerm&amp; term, DisjunctionContext* context)
</span><span class="cx">     {
</span><span class="cx">         ASSERT(term.type == ByteTerm::TypeParentheticalAssertionBegin);
</span><del>-        ASSERT(term.atom.quantityCount == 1);
</del><ins>+        ASSERT(term.atom.quantityMaxCount == 1);
</ins><span class="cx"> 
</span><span class="cx">         // We've failed to match parens; if they are inverted, this is win!
</span><span class="cx">         if (term.invert()) {
</span><span class="lines">@@ -912,7 +912,7 @@
</span><span class="cx">     bool backtrackParentheticalAssertionEnd(ByteTerm&amp; term, DisjunctionContext* context)
</span><span class="cx">     {
</span><span class="cx">         ASSERT(term.type == ByteTerm::TypeParentheticalAssertionEnd);
</span><del>-        ASSERT(term.atom.quantityCount == 1);
</del><ins>+        ASSERT(term.atom.quantityMaxCount == 1);
</ins><span class="cx"> 
</span><span class="cx">         BackTrackInfoParentheticalAssertion* backTrack = reinterpret_cast&lt;BackTrackInfoParentheticalAssertion*&gt;(context-&gt;frame + term.frameLocation);
</span><span class="cx"> 
</span><span class="lines">@@ -932,22 +932,27 @@
</span><span class="cx">         backTrack-&gt;matchAmount = 0;
</span><span class="cx">         backTrack-&gt;lastContext = 0;
</span><span class="cx"> 
</span><del>-        switch (term.atom.quantityType) {
-        case QuantifierFixedCount: {
</del><ins>+        ASSERT(term.atom.quantityType != QuantifierFixedCount || term.atom.quantityMinCount == term.atom.quantityMaxCount);
+
+        unsigned minimumMatchCount = term.atom.quantityMinCount;
+        JSRegExpResult fixedMatchResult;
+
+        // Handle fixed matches and the minimum part of a variable length match.
+        if (minimumMatchCount) {
</ins><span class="cx">             // While we haven't yet reached our fixed limit,
</span><del>-            while (backTrack-&gt;matchAmount &lt; term.atom.quantityCount) {
</del><ins>+            while (backTrack-&gt;matchAmount &lt; minimumMatchCount) {
</ins><span class="cx">                 // Try to do a match, and it it succeeds, add it to the list.
</span><span class="cx">                 ParenthesesDisjunctionContext* context = allocParenthesesDisjunctionContext(disjunctionBody, output, term);
</span><del>-                JSRegExpResult result = matchDisjunction(disjunctionBody, context-&gt;getDisjunctionContext(term));
-                if (result == JSRegExpMatch)
</del><ins>+                fixedMatchResult = matchDisjunction(disjunctionBody, context-&gt;getDisjunctionContext(term));
+                if (fixedMatchResult == JSRegExpMatch)
</ins><span class="cx">                     appendParenthesesDisjunctionContext(backTrack, context);
</span><span class="cx">                 else {
</span><span class="cx">                     // The match failed; try to find an alternate point to carry on from.
</span><span class="cx">                     resetMatches(term, context);
</span><span class="cx">                     freeParenthesesDisjunctionContext(context);
</span><del>-
-                    if (result != JSRegExpNoMatch)
-                        return result;
</del><ins>+                    
+                    if (fixedMatchResult != JSRegExpNoMatch)
+                        return fixedMatchResult;
</ins><span class="cx">                     JSRegExpResult backtrackResult = parenthesesDoBacktrack(term, backTrack);
</span><span class="cx">                     if (backtrackResult != JSRegExpMatch)
</span><span class="cx">                         return backtrackResult;
</span><span class="lines">@@ -954,14 +959,18 @@
</span><span class="cx">                 }
</span><span class="cx">             }
</span><span class="cx"> 
</span><del>-            ASSERT(backTrack-&gt;matchAmount == term.atom.quantityCount);
</del><span class="cx">             ParenthesesDisjunctionContext* context = backTrack-&gt;lastContext;
</span><span class="cx">             recordParenthesesMatch(term, context);
</span><ins>+        }
+
+        switch (term.atom.quantityType) {
+        case QuantifierFixedCount: {
+            ASSERT(backTrack-&gt;matchAmount == term.atom.quantityMaxCount);
</ins><span class="cx">             return JSRegExpMatch;
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         case QuantifierGreedy: {
</span><del>-            while (backTrack-&gt;matchAmount &lt; term.atom.quantityCount) {
</del><ins>+            while (backTrack-&gt;matchAmount &lt; term.atom.quantityMaxCount) {
</ins><span class="cx">                 ParenthesesDisjunctionContext* context = allocParenthesesDisjunctionContext(disjunctionBody, output, term);
</span><span class="cx">                 JSRegExpResult result = matchNonZeroDisjunction(disjunctionBody, context-&gt;getDisjunctionContext(term));
</span><span class="cx">                 if (result == JSRegExpMatch)
</span><span class="lines">@@ -1011,7 +1020,7 @@
</span><span class="cx"> 
</span><span class="cx">         switch (term.atom.quantityType) {
</span><span class="cx">         case QuantifierFixedCount: {
</span><del>-            ASSERT(backTrack-&gt;matchAmount == term.atom.quantityCount);
</del><ins>+            ASSERT(backTrack-&gt;matchAmount == term.atom.quantityMaxCount);
</ins><span class="cx"> 
</span><span class="cx">             ParenthesesDisjunctionContext* context = 0;
</span><span class="cx">             JSRegExpResult result = parenthesesDoBacktrack(term, backTrack);
</span><span class="lines">@@ -1020,7 +1029,7 @@
</span><span class="cx">                 return result;
</span><span class="cx"> 
</span><span class="cx">             // While we haven't yet reached our fixed limit,
</span><del>-            while (backTrack-&gt;matchAmount &lt; term.atom.quantityCount) {
</del><ins>+            while (backTrack-&gt;matchAmount &lt; term.atom.quantityMaxCount) {
</ins><span class="cx">                 // Try to do a match, and it it succeeds, add it to the list.
</span><span class="cx">                 context = allocParenthesesDisjunctionContext(disjunctionBody, output, term);
</span><span class="cx">                 result = matchDisjunction(disjunctionBody, context-&gt;getDisjunctionContext(term));
</span><span class="lines">@@ -1040,7 +1049,7 @@
</span><span class="cx">                 }
</span><span class="cx">             }
</span><span class="cx"> 
</span><del>-            ASSERT(backTrack-&gt;matchAmount == term.atom.quantityCount);
</del><ins>+            ASSERT(backTrack-&gt;matchAmount == term.atom.quantityMaxCount);
</ins><span class="cx">             context = backTrack-&gt;lastContext;
</span><span class="cx">             recordParenthesesMatch(term, context);
</span><span class="cx">             return JSRegExpMatch;
</span><span class="lines">@@ -1053,7 +1062,7 @@
</span><span class="cx">             ParenthesesDisjunctionContext* context = backTrack-&gt;lastContext;
</span><span class="cx">             JSRegExpResult result = matchNonZeroDisjunction(disjunctionBody, context-&gt;getDisjunctionContext(term), true);
</span><span class="cx">             if (result == JSRegExpMatch) {
</span><del>-                while (backTrack-&gt;matchAmount &lt; term.atom.quantityCount) {
</del><ins>+                while (backTrack-&gt;matchAmount &lt; term.atom.quantityMaxCount) {
</ins><span class="cx">                     ParenthesesDisjunctionContext* context = allocParenthesesDisjunctionContext(disjunctionBody, output, term);
</span><span class="cx">                     JSRegExpResult parenthesesResult = matchNonZeroDisjunction(disjunctionBody, context-&gt;getDisjunctionContext(term));
</span><span class="cx">                     if (parenthesesResult == JSRegExpMatch)
</span><span class="lines">@@ -1086,7 +1095,7 @@
</span><span class="cx"> 
</span><span class="cx">         case QuantifierNonGreedy: {
</span><span class="cx">             // If we've not reached the limit, try to add one more match.
</span><del>-            if (backTrack-&gt;matchAmount &lt; term.atom.quantityCount) {
</del><ins>+            if (backTrack-&gt;matchAmount &lt; term.atom.quantityMaxCount) {
</ins><span class="cx">                 ParenthesesDisjunctionContext* context = allocParenthesesDisjunctionContext(disjunctionBody, output, term);
</span><span class="cx">                 JSRegExpResult result = matchNonZeroDisjunction(disjunctionBody, context-&gt;getDisjunctionContext(term));
</span><span class="cx">                 if (result == JSRegExpMatch) {
</span><span class="lines">@@ -1223,7 +1232,7 @@
</span><span class="cx">         case ByteTerm::TypePatternCharacterFixed: {
</span><span class="cx">             if (unicode) {
</span><span class="cx">                 if (!U_IS_BMP(currentTerm().atom.patternCharacter)) {
</span><del>-                    for (unsigned matchAmount = 0; matchAmount &lt; currentTerm().atom.quantityCount; ++matchAmount) {
</del><ins>+                    for (unsigned matchAmount = 0; matchAmount &lt; currentTerm().atom.quantityMaxCount; ++matchAmount) {
</ins><span class="cx">                         if (!checkSurrogatePair(currentTerm().atom.patternCharacter, currentTerm().inputPosition - 2 * matchAmount)) {
</span><span class="cx">                             BACKTRACK();
</span><span class="cx">                         }
</span><span class="lines">@@ -1233,7 +1242,7 @@
</span><span class="cx">             }
</span><span class="cx">             unsigned position = input.getPos(); // May need to back out reading a surrogate pair.
</span><span class="cx"> 
</span><del>-            for (unsigned matchAmount = 0; matchAmount &lt; currentTerm().atom.quantityCount; ++matchAmount) {
</del><ins>+            for (unsigned matchAmount = 0; matchAmount &lt; currentTerm().atom.quantityMaxCount; ++matchAmount) {
</ins><span class="cx">                 if (!checkCharacter(currentTerm().atom.patternCharacter, currentTerm().inputPosition - matchAmount)) {
</span><span class="cx">                     input.setPos(position);
</span><span class="cx">                     BACKTRACK();
</span><span class="lines">@@ -1245,7 +1254,7 @@
</span><span class="cx">             BackTrackInfoPatternCharacter* backTrack = reinterpret_cast&lt;BackTrackInfoPatternCharacter*&gt;(context-&gt;frame + currentTerm().frameLocation);
</span><span class="cx">             unsigned matchAmount = 0;
</span><span class="cx">             unsigned position = input.getPos(); // May need to back out reading a surrogate pair.
</span><del>-            while ((matchAmount &lt; currentTerm().atom.quantityCount) &amp;&amp; input.checkInput(1)) {
</del><ins>+            while ((matchAmount &lt; currentTerm().atom.quantityMaxCount) &amp;&amp; input.checkInput(1)) {
</ins><span class="cx">                 if (!checkCharacter(currentTerm().atom.patternCharacter, currentTerm().inputPosition + 1)) {
</span><span class="cx">                     input.setPos(position);
</span><span class="cx">                     break;
</span><span class="lines">@@ -1272,7 +1281,7 @@
</span><span class="cx"> 
</span><span class="cx">                 unsigned position = input.getPos(); // May need to back out reading a surrogate pair.
</span><span class="cx">                 
</span><del>-                for (unsigned matchAmount = 0; matchAmount &lt; currentTerm().atom.quantityCount; ++matchAmount) {
</del><ins>+                for (unsigned matchAmount = 0; matchAmount &lt; currentTerm().atom.quantityMaxCount; ++matchAmount) {
</ins><span class="cx">                     if (!checkCasedCharacter(currentTerm().atom.casedCharacter.lo, currentTerm().atom.casedCharacter.hi, currentTerm().inputPosition - matchAmount)) {
</span><span class="cx">                         input.setPos(position);
</span><span class="cx">                         BACKTRACK();
</span><span class="lines">@@ -1281,7 +1290,7 @@
</span><span class="cx">                 MATCH_NEXT();
</span><span class="cx">             }
</span><span class="cx"> 
</span><del>-            for (unsigned matchAmount = 0; matchAmount &lt; currentTerm().atom.quantityCount; ++matchAmount) {
</del><ins>+            for (unsigned matchAmount = 0; matchAmount &lt; currentTerm().atom.quantityMaxCount; ++matchAmount) {
</ins><span class="cx">                 if (!checkCasedCharacter(currentTerm().atom.casedCharacter.lo, currentTerm().atom.casedCharacter.hi, currentTerm().inputPosition - matchAmount))
</span><span class="cx">                     BACKTRACK();
</span><span class="cx">             }
</span><span class="lines">@@ -1294,7 +1303,7 @@
</span><span class="cx">             ASSERT(!unicode || U_IS_BMP(currentTerm().atom.patternCharacter));
</span><span class="cx"> 
</span><span class="cx">             unsigned matchAmount = 0;
</span><del>-            while ((matchAmount &lt; currentTerm().atom.quantityCount) &amp;&amp; input.checkInput(1)) {
</del><ins>+            while ((matchAmount &lt; currentTerm().atom.quantityMaxCount) &amp;&amp; input.checkInput(1)) {
</ins><span class="cx">                 if (!checkCasedCharacter(currentTerm().atom.casedCharacter.lo, currentTerm().atom.casedCharacter.hi, currentTerm().inputPosition + 1)) {
</span><span class="cx">                     input.uncheckInput(1);
</span><span class="cx">                     break;
</span><span class="lines">@@ -1622,7 +1631,7 @@
</span><span class="cx">         m_bodyDisjunction-&gt;terms.append(ByteTerm::WordBoundary(invert, inputPosition));
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    void atomPatternCharacter(UChar32 ch, unsigned inputPosition, unsigned frameLocation, Checked&lt;unsigned&gt; quantityCount, QuantifierType quantityType)
</del><ins>+    void atomPatternCharacter(UChar32 ch, unsigned inputPosition, unsigned frameLocation, Checked&lt;unsigned&gt; quantityMaxCount, QuantifierType quantityType)
</ins><span class="cx">     {
</span><span class="cx">         if (m_pattern.ignoreCase()) {
</span><span class="cx">             UChar32 lo = u_tolower(ch);
</span><span class="lines">@@ -1629,30 +1638,30 @@
</span><span class="cx">             UChar32 hi = u_toupper(ch);
</span><span class="cx"> 
</span><span class="cx">             if (lo != hi) {
</span><del>-                m_bodyDisjunction-&gt;terms.append(ByteTerm(lo, hi, inputPosition, frameLocation, quantityCount, quantityType));
</del><ins>+                m_bodyDisjunction-&gt;terms.append(ByteTerm(lo, hi, inputPosition, frameLocation, quantityMaxCount, quantityType));
</ins><span class="cx">                 return;
</span><span class="cx">             }
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        m_bodyDisjunction-&gt;terms.append(ByteTerm(ch, inputPosition, frameLocation, quantityCount, quantityType));
</del><ins>+        m_bodyDisjunction-&gt;terms.append(ByteTerm(ch, inputPosition, frameLocation, quantityMaxCount, quantityType));
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    void atomCharacterClass(CharacterClass* characterClass, bool invert, unsigned inputPosition, unsigned frameLocation, Checked&lt;unsigned&gt; quantityCount, QuantifierType quantityType)
</del><ins>+    void atomCharacterClass(CharacterClass* characterClass, bool invert, unsigned inputPosition, unsigned frameLocation, Checked&lt;unsigned&gt; quantityMaxCount, QuantifierType quantityType)
</ins><span class="cx">     {
</span><span class="cx">         m_bodyDisjunction-&gt;terms.append(ByteTerm(characterClass, invert, inputPosition));
</span><span class="cx"> 
</span><del>-        m_bodyDisjunction-&gt;terms[m_bodyDisjunction-&gt;terms.size() - 1].atom.quantityCount = quantityCount.unsafeGet();
</del><ins>+        m_bodyDisjunction-&gt;terms[m_bodyDisjunction-&gt;terms.size() - 1].atom.quantityMaxCount = quantityMaxCount.unsafeGet();
</ins><span class="cx">         m_bodyDisjunction-&gt;terms[m_bodyDisjunction-&gt;terms.size() - 1].atom.quantityType = quantityType;
</span><span class="cx">         m_bodyDisjunction-&gt;terms[m_bodyDisjunction-&gt;terms.size() - 1].frameLocation = frameLocation;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    void atomBackReference(unsigned subpatternId, unsigned inputPosition, unsigned frameLocation, Checked&lt;unsigned&gt; quantityCount, QuantifierType quantityType)
</del><ins>+    void atomBackReference(unsigned subpatternId, unsigned inputPosition, unsigned frameLocation, Checked&lt;unsigned&gt; quantityMaxCount, QuantifierType quantityType)
</ins><span class="cx">     {
</span><span class="cx">         ASSERT(subpatternId);
</span><span class="cx"> 
</span><span class="cx">         m_bodyDisjunction-&gt;terms.append(ByteTerm::BackReference(subpatternId, inputPosition));
</span><span class="cx"> 
</span><del>-        m_bodyDisjunction-&gt;terms[m_bodyDisjunction-&gt;terms.size() - 1].atom.quantityCount = quantityCount.unsafeGet();
</del><ins>+        m_bodyDisjunction-&gt;terms[m_bodyDisjunction-&gt;terms.size() - 1].atom.quantityMaxCount = quantityMaxCount.unsafeGet();
</ins><span class="cx">         m_bodyDisjunction-&gt;terms[m_bodyDisjunction-&gt;terms.size() - 1].atom.quantityType = quantityType;
</span><span class="cx">         m_bodyDisjunction-&gt;terms[m_bodyDisjunction-&gt;terms.size() - 1].frameLocation = frameLocation;
</span><span class="cx">     }
</span><span class="lines">@@ -1713,7 +1722,7 @@
</span><span class="cx">         m_currentAlternativeIndex = beginTerm + 1;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    void atomParentheticalAssertionEnd(unsigned inputPosition, unsigned frameLocation, Checked&lt;unsigned&gt; quantityCount, QuantifierType quantityType)
</del><ins>+    void atomParentheticalAssertionEnd(unsigned inputPosition, unsigned frameLocation, Checked&lt;unsigned&gt; quantityMaxCount, QuantifierType quantityType)
</ins><span class="cx">     {
</span><span class="cx">         unsigned beginTerm = popParenthesesStack();
</span><span class="cx">         closeAlternative(beginTerm + 1);
</span><span class="lines">@@ -1729,9 +1738,9 @@
</span><span class="cx">         m_bodyDisjunction-&gt;terms[endTerm].atom.parenthesesWidth = endTerm - beginTerm;
</span><span class="cx">         m_bodyDisjunction-&gt;terms[endTerm].frameLocation = frameLocation;
</span><span class="cx"> 
</span><del>-        m_bodyDisjunction-&gt;terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet();
</del><ins>+        m_bodyDisjunction-&gt;terms[beginTerm].atom.quantityMaxCount = quantityMaxCount.unsafeGet();
</ins><span class="cx">         m_bodyDisjunction-&gt;terms[beginTerm].atom.quantityType = quantityType;
</span><del>-        m_bodyDisjunction-&gt;terms[endTerm].atom.quantityCount = quantityCount.unsafeGet();
</del><ins>+        m_bodyDisjunction-&gt;terms[endTerm].atom.quantityMaxCount = quantityMaxCount.unsafeGet();
</ins><span class="cx">         m_bodyDisjunction-&gt;terms[endTerm].atom.quantityType = quantityType;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -1811,7 +1820,7 @@
</span><span class="cx">         m_bodyDisjunction-&gt;terms[endIndex].frameLocation = frameLocation;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    void atomParenthesesSubpatternEnd(unsigned lastSubpatternId, unsigned inputPosition, unsigned frameLocation, Checked&lt;unsigned&gt; quantityCount, QuantifierType quantityType, unsigned callFrameSize = 0)
</del><ins>+    void atomParenthesesSubpatternEnd(unsigned lastSubpatternId, unsigned inputPosition, unsigned frameLocation, Checked&lt;unsigned&gt; quantityMinCount, Checked&lt;unsigned&gt; quantityMaxCount, QuantifierType quantityType, unsigned callFrameSize = 0)
</ins><span class="cx">     {
</span><span class="cx">         unsigned beginTerm = popParenthesesStack();
</span><span class="cx">         closeAlternative(beginTerm + 1);
</span><span class="lines">@@ -1840,12 +1849,13 @@
</span><span class="cx">         m_bodyDisjunction-&gt;terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpattern, subpatternId, parenthesesDisjunction.get(), capture, inputPosition));
</span><span class="cx">         m_allParenthesesInfo.append(WTFMove(parenthesesDisjunction));
</span><span class="cx"> 
</span><del>-        m_bodyDisjunction-&gt;terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet();
</del><ins>+        m_bodyDisjunction-&gt;terms[beginTerm].atom.quantityMinCount = quantityMinCount.unsafeGet();
+        m_bodyDisjunction-&gt;terms[beginTerm].atom.quantityMaxCount = quantityMaxCount.unsafeGet();
</ins><span class="cx">         m_bodyDisjunction-&gt;terms[beginTerm].atom.quantityType = quantityType;
</span><span class="cx">         m_bodyDisjunction-&gt;terms[beginTerm].frameLocation = frameLocation;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    void atomParenthesesOnceEnd(unsigned inputPosition, unsigned frameLocation, Checked&lt;unsigned&gt; quantityCount, QuantifierType quantityType)
</del><ins>+    void atomParenthesesOnceEnd(unsigned inputPosition, unsigned frameLocation, Checked&lt;unsigned&gt; quantityMinCount, Checked&lt;unsigned&gt; quantityMaxCount, QuantifierType quantityType)
</ins><span class="cx">     {
</span><span class="cx">         unsigned beginTerm = popParenthesesStack();
</span><span class="cx">         closeAlternative(beginTerm + 1);
</span><span class="lines">@@ -1861,13 +1871,15 @@
</span><span class="cx">         m_bodyDisjunction-&gt;terms[endTerm].atom.parenthesesWidth = endTerm - beginTerm;
</span><span class="cx">         m_bodyDisjunction-&gt;terms[endTerm].frameLocation = frameLocation;
</span><span class="cx"> 
</span><del>-        m_bodyDisjunction-&gt;terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet();
</del><ins>+        m_bodyDisjunction-&gt;terms[beginTerm].atom.quantityMinCount = quantityMinCount.unsafeGet();
+        m_bodyDisjunction-&gt;terms[beginTerm].atom.quantityMaxCount = quantityMaxCount.unsafeGet();
</ins><span class="cx">         m_bodyDisjunction-&gt;terms[beginTerm].atom.quantityType = quantityType;
</span><del>-        m_bodyDisjunction-&gt;terms[endTerm].atom.quantityCount = quantityCount.unsafeGet();
</del><ins>+        m_bodyDisjunction-&gt;terms[endTerm].atom.quantityMinCount = quantityMinCount.unsafeGet();
+        m_bodyDisjunction-&gt;terms[endTerm].atom.quantityMaxCount = quantityMaxCount.unsafeGet();
</ins><span class="cx">         m_bodyDisjunction-&gt;terms[endTerm].atom.quantityType = quantityType;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    void atomParenthesesTerminalEnd(unsigned inputPosition, unsigned frameLocation, Checked&lt;unsigned&gt; quantityCount, QuantifierType quantityType)
</del><ins>+    void atomParenthesesTerminalEnd(unsigned inputPosition, unsigned frameLocation, Checked&lt;unsigned&gt; quantityMinCount, Checked&lt;unsigned&gt; quantityMaxCount, QuantifierType quantityType)
</ins><span class="cx">     {
</span><span class="cx">         unsigned beginTerm = popParenthesesStack();
</span><span class="cx">         closeAlternative(beginTerm + 1);
</span><span class="lines">@@ -1883,9 +1895,11 @@
</span><span class="cx">         m_bodyDisjunction-&gt;terms[endTerm].atom.parenthesesWidth = endTerm - beginTerm;
</span><span class="cx">         m_bodyDisjunction-&gt;terms[endTerm].frameLocation = frameLocation;
</span><span class="cx"> 
</span><del>-        m_bodyDisjunction-&gt;terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet();
</del><ins>+        m_bodyDisjunction-&gt;terms[beginTerm].atom.quantityMinCount = quantityMinCount.unsafeGet();
+        m_bodyDisjunction-&gt;terms[beginTerm].atom.quantityMaxCount = quantityMaxCount.unsafeGet();
</ins><span class="cx">         m_bodyDisjunction-&gt;terms[beginTerm].atom.quantityType = quantityType;
</span><del>-        m_bodyDisjunction-&gt;terms[endTerm].atom.quantityCount = quantityCount.unsafeGet();
</del><ins>+        m_bodyDisjunction-&gt;terms[endTerm].atom.quantityMinCount = quantityMinCount.unsafeGet();
+        m_bodyDisjunction-&gt;terms[endTerm].atom.quantityMaxCount = quantityMaxCount.unsafeGet();
</ins><span class="cx">         m_bodyDisjunction-&gt;terms[endTerm].atom.quantityType = quantityType;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -1960,15 +1974,15 @@
</span><span class="cx">                     break;
</span><span class="cx"> 
</span><span class="cx">                 case PatternTerm::TypePatternCharacter:
</span><del>-                    atomPatternCharacter(term.patternCharacter, currentCountAlreadyChecked - term.inputPosition, term.frameLocation, term.quantityCount, term.quantityType);
</del><ins>+                    atomPatternCharacter(term.patternCharacter, currentCountAlreadyChecked - term.inputPosition, term.frameLocation, term.quantityMaxCount, term.quantityType);
</ins><span class="cx">                     break;
</span><span class="cx"> 
</span><span class="cx">                 case PatternTerm::TypeCharacterClass:
</span><del>-                    atomCharacterClass(term.characterClass, term.invert(), currentCountAlreadyChecked- term.inputPosition, term.frameLocation, term.quantityCount, term.quantityType);
</del><ins>+                    atomCharacterClass(term.characterClass, term.invert(), currentCountAlreadyChecked- term.inputPosition, term.frameLocation, term.quantityMaxCount, term.quantityType);
</ins><span class="cx">                     break;
</span><span class="cx"> 
</span><span class="cx">                 case PatternTerm::TypeBackReference:
</span><del>-                    atomBackReference(term.backReferenceSubpatternId, currentCountAlreadyChecked - term.inputPosition, term.frameLocation, term.quantityCount, term.quantityType);
</del><ins>+                    atomBackReference(term.backReferenceSubpatternId, currentCountAlreadyChecked - term.inputPosition, term.frameLocation, term.quantityMaxCount, term.quantityType);
</ins><span class="cx">                         break;
</span><span class="cx"> 
</span><span class="cx">                 case PatternTerm::TypeForwardReference:
</span><span class="lines">@@ -1976,7 +1990,7 @@
</span><span class="cx"> 
</span><span class="cx">                 case PatternTerm::TypeParenthesesSubpattern: {
</span><span class="cx">                     unsigned disjunctionAlreadyCheckedCount = 0;
</span><del>-                    if (term.quantityCount == 1 &amp;&amp; !term.parentheses.isCopy) {
</del><ins>+                    if (term.quantityMaxCount == 1 &amp;&amp; !term.parentheses.isCopy) {
</ins><span class="cx">                         unsigned alternativeFrameLocation = term.frameLocation;
</span><span class="cx">                         // For QuantifierFixedCount we pre-check the minimum size; for greedy/non-greedy we reserve a slot in the frame.
</span><span class="cx">                         if (term.quantityType == QuantifierFixedCount)
</span><span class="lines">@@ -1987,19 +2001,19 @@
</span><span class="cx">                         unsigned delegateEndInputOffset = currentCountAlreadyChecked - term.inputPosition;
</span><span class="cx">                         atomParenthesesOnceBegin(term.parentheses.subpatternId, term.capture(), disjunctionAlreadyCheckedCount + delegateEndInputOffset, term.frameLocation, alternativeFrameLocation);
</span><span class="cx">                         emitDisjunction(term.parentheses.disjunction, currentCountAlreadyChecked, disjunctionAlreadyCheckedCount);
</span><del>-                        atomParenthesesOnceEnd(delegateEndInputOffset, term.frameLocation, term.quantityCount, term.quantityType);
</del><ins>+                        atomParenthesesOnceEnd(delegateEndInputOffset, term.frameLocation, term.quantityMinCount, term.quantityMaxCount, term.quantityType);
</ins><span class="cx">                     } else if (term.parentheses.isTerminal) {
</span><span class="cx">                         ASSERT(currentCountAlreadyChecked &gt;= term.inputPosition);
</span><span class="cx">                         unsigned delegateEndInputOffset = currentCountAlreadyChecked - term.inputPosition;
</span><span class="cx">                         atomParenthesesTerminalBegin(term.parentheses.subpatternId, term.capture(), disjunctionAlreadyCheckedCount + delegateEndInputOffset, term.frameLocation, term.frameLocation + YarrStackSpaceForBackTrackInfoParenthesesOnce);
</span><span class="cx">                         emitDisjunction(term.parentheses.disjunction, currentCountAlreadyChecked, disjunctionAlreadyCheckedCount);
</span><del>-                        atomParenthesesTerminalEnd(delegateEndInputOffset, term.frameLocation, term.quantityCount, term.quantityType);
</del><ins>+                        atomParenthesesTerminalEnd(delegateEndInputOffset, term.frameLocation, term.quantityMinCount, term.quantityMaxCount, term.quantityType);
</ins><span class="cx">                     } else {
</span><span class="cx">                         ASSERT(currentCountAlreadyChecked &gt;= term.inputPosition);
</span><span class="cx">                         unsigned delegateEndInputOffset = currentCountAlreadyChecked - term.inputPosition;
</span><span class="cx">                         atomParenthesesSubpatternBegin(term.parentheses.subpatternId, term.capture(), disjunctionAlreadyCheckedCount + delegateEndInputOffset, term.frameLocation, 0);
</span><span class="cx">                         emitDisjunction(term.parentheses.disjunction, currentCountAlreadyChecked, 0);
</span><del>-                        atomParenthesesSubpatternEnd(term.parentheses.lastSubpatternId, delegateEndInputOffset, term.frameLocation, term.quantityCount, term.quantityType, term.parentheses.disjunction-&gt;m_callFrameSize);
</del><ins>+                        atomParenthesesSubpatternEnd(term.parentheses.lastSubpatternId, delegateEndInputOffset, term.frameLocation, term.quantityMinCount, term.quantityMaxCount, term.quantityType, term.parentheses.disjunction-&gt;m_callFrameSize);
</ins><span class="cx">                     }
</span><span class="cx">                     break;
</span><span class="cx">                 }
</span><span class="lines">@@ -2018,7 +2032,7 @@
</span><span class="cx"> 
</span><span class="cx">                     atomParentheticalAssertionBegin(term.parentheses.subpatternId, term.invert(), term.frameLocation, alternativeFrameLocation);
</span><span class="cx">                     emitDisjunction(term.parentheses.disjunction, currentCountAlreadyChecked, positiveInputOffset - uncheckAmount);
</span><del>-                    atomParentheticalAssertionEnd(0, term.frameLocation, term.quantityCount, term.quantityType);
</del><ins>+                    atomParentheticalAssertionEnd(0, term.frameLocation, term.quantityMaxCount, term.quantityType);
</ins><span class="cx">                     if (uncheckAmount) {
</span><span class="cx">                         checkInput(uncheckAmount);
</span><span class="cx">                         currentCountAlreadyChecked += uncheckAmount;
</span></span></pre></div>
<a id="branchessafari603branchSourceJavaScriptCoreyarrYarrInterpreterh"></a>
<div class="modfile"><h4>Modified: branches/safari-603-branch/Source/JavaScriptCore/yarr/YarrInterpreter.h (210853 => 210854)</h4>
<pre class="diff"><span>
<span class="info">--- branches/safari-603-branch/Source/JavaScriptCore/yarr/YarrInterpreter.h        2017-01-18 18:37:30 UTC (rev 210853)
+++ branches/safari-603-branch/Source/JavaScriptCore/yarr/YarrInterpreter.h        2017-01-18 18:45:12 UTC (rev 210854)
</span><span class="lines">@@ -87,7 +87,8 @@
</span><span class="cx">                 unsigned parenthesesWidth;
</span><span class="cx">             };
</span><span class="cx">             QuantifierType quantityType;
</span><del>-            unsigned quantityCount;
</del><ins>+            unsigned quantityMinCount;
+            unsigned quantityMaxCount;
</ins><span class="cx">         } atom;
</span><span class="cx">         struct {
</span><span class="cx">             int next;
</span><span class="lines">@@ -110,6 +111,12 @@
</span><span class="cx">         , m_capture(false)
</span><span class="cx">         , m_invert(false)
</span><span class="cx">     {
</span><ins>+        atom.patternCharacter = ch;
+        atom.quantityType = quantityType;
+        atom.quantityMinCount = quantityCount.unsafeGet();
+        atom.quantityMaxCount = quantityCount.unsafeGet();
+        inputPosition = inputPos;
+
</ins><span class="cx">         switch (quantityType) {
</span><span class="cx">         case QuantifierFixedCount:
</span><span class="cx">             type = (quantityCount == 1) ? ByteTerm::TypePatternCharacterOnce : ByteTerm::TypePatternCharacterFixed;
</span><span class="lines">@@ -121,11 +128,6 @@
</span><span class="cx">             type = ByteTerm::TypePatternCharacterNonGreedy;
</span><span class="cx">             break;
</span><span class="cx">         }
</span><del>-
-        atom.patternCharacter = ch;
-        atom.quantityType = quantityType;
-        atom.quantityCount = quantityCount.unsafeGet();
-        inputPosition = inputPos;
</del><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     ByteTerm(UChar32 lo, UChar32 hi, unsigned inputPos, unsigned frameLocation, Checked&lt;unsigned&gt; quantityCount, QuantifierType quantityType)
</span><span class="lines">@@ -148,7 +150,8 @@
</span><span class="cx">         atom.casedCharacter.lo = lo;
</span><span class="cx">         atom.casedCharacter.hi = hi;
</span><span class="cx">         atom.quantityType = quantityType;
</span><del>-        atom.quantityCount = quantityCount.unsafeGet();
</del><ins>+        atom.quantityMinCount = quantityCount.unsafeGet();
+        atom.quantityMaxCount = quantityCount.unsafeGet();
</ins><span class="cx">         inputPosition = inputPos;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -159,7 +162,8 @@
</span><span class="cx">     {
</span><span class="cx">         atom.characterClass = characterClass;
</span><span class="cx">         atom.quantityType = QuantifierFixedCount;
</span><del>-        atom.quantityCount = 1;
</del><ins>+        atom.quantityMinCount = 1;
+        atom.quantityMaxCount = 1;
</ins><span class="cx">         inputPosition = inputPos;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -171,7 +175,8 @@
</span><span class="cx">         atom.subpatternId = subpatternId;
</span><span class="cx">         atom.parenthesesDisjunction = parenthesesInfo;
</span><span class="cx">         atom.quantityType = QuantifierFixedCount;
</span><del>-        atom.quantityCount = 1;
</del><ins>+        atom.quantityMinCount = 1;
+        atom.quantityMaxCount = 1;
</ins><span class="cx">         inputPosition = inputPos;
</span><span class="cx">     }
</span><span class="cx">     
</span><span class="lines">@@ -181,7 +186,8 @@
</span><span class="cx">         , m_invert(invert)
</span><span class="cx">     {
</span><span class="cx">         atom.quantityType = QuantifierFixedCount;
</span><del>-        atom.quantityCount = 1;
</del><ins>+        atom.quantityMinCount = 1;
+        atom.quantityMaxCount = 1;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     ByteTerm(Type type, unsigned subpatternId, bool capture, bool invert, unsigned inputPos)
</span><span class="lines">@@ -191,7 +197,8 @@
</span><span class="cx">     {
</span><span class="cx">         atom.subpatternId = subpatternId;
</span><span class="cx">         atom.quantityType = QuantifierFixedCount;
</span><del>-        atom.quantityCount = 1;
</del><ins>+        atom.quantityMinCount = 1;
+        atom.quantityMaxCount = 1;
</ins><span class="cx">         inputPosition = inputPos;
</span><span class="cx">     }
</span><span class="cx"> 
</span></span></pre></div>
<a id="branchessafari603branchSourceJavaScriptCoreyarrYarrJITcpp"></a>
<div class="modfile"><h4>Modified: branches/safari-603-branch/Source/JavaScriptCore/yarr/YarrJIT.cpp (210853 => 210854)</h4>
<pre class="diff"><span>
<span class="info">--- branches/safari-603-branch/Source/JavaScriptCore/yarr/YarrJIT.cpp        2017-01-18 18:37:30 UTC (rev 210853)
+++ branches/safari-603-branch/Source/JavaScriptCore/yarr/YarrJIT.cpp        2017-01-18 18:45:12 UTC (rev 210854)
</span><span class="lines">@@ -464,7 +464,7 @@
</span><span class="cx">         OpSimpleNestedAlternativeBegin,
</span><span class="cx">         OpSimpleNestedAlternativeNext,
</span><span class="cx">         OpSimpleNestedAlternativeEnd,
</span><del>-        // Used to wrap 'Once' subpattern matches (quantityCount == 1).
</del><ins>+        // Used to wrap 'Once' subpattern matches (quantityMaxCount == 1).
</ins><span class="cx">         OpParenthesesSubpatternOnceBegin,
</span><span class="cx">         OpParenthesesSubpatternOnceEnd,
</span><span class="cx">         // Used to wrap 'Terminal' subpattern matches (at the end of the regexp).
</span><span class="lines">@@ -841,7 +841,7 @@
</span><span class="cx">             
</span><span class="cx">             if (nextTerm-&gt;type != PatternTerm::TypePatternCharacter
</span><span class="cx">                 || nextTerm-&gt;quantityType != QuantifierFixedCount
</span><del>-                || nextTerm-&gt;quantityCount != 1
</del><ins>+                || nextTerm-&gt;quantityMaxCount != 1
</ins><span class="cx">                 || nextTerm-&gt;inputPosition != (startTermPosition + numberCharacters))
</span><span class="cx">                 break;
</span><span class="cx"> 
</span><span class="lines">@@ -924,10 +924,10 @@
</span><span class="cx">         const RegisterID countRegister = regT1;
</span><span class="cx"> 
</span><span class="cx">         move(index, countRegister);
</span><del>-        sub32(Imm32(term-&gt;quantityCount.unsafeGet()), countRegister);
</del><ins>+        sub32(Imm32(term-&gt;quantityMaxCount.unsafeGet()), countRegister);
</ins><span class="cx"> 
</span><span class="cx">         Label loop(this);
</span><del>-        readCharacter(m_checkedOffset - term-&gt;inputPosition - term-&gt;quantityCount, character, countRegister);
</del><ins>+        readCharacter(m_checkedOffset - term-&gt;inputPosition - term-&gt;quantityMaxCount, character, countRegister);
</ins><span class="cx">         // For case-insesitive compares, non-ascii characters that have different
</span><span class="cx">         // upper &amp; lower case representations are converted to a character class.
</span><span class="cx">         ASSERT(!m_pattern.ignoreCase() || isASCIIAlpha(ch) || isCanonicallyUnique(ch));
</span><span class="lines">@@ -965,10 +965,10 @@
</span><span class="cx"> 
</span><span class="cx">             add32(TrustedImm32(1), countRegister);
</span><span class="cx">             add32(TrustedImm32(1), index);
</span><del>-            if (term-&gt;quantityCount == quantifyInfinite)
</del><ins>+            if (term-&gt;quantityMaxCount == quantifyInfinite)
</ins><span class="cx">                 jump(loop);
</span><span class="cx">             else
</span><del>-                branch32(NotEqual, countRegister, Imm32(term-&gt;quantityCount.unsafeGet())).linkTo(loop, this);
</del><ins>+                branch32(NotEqual, countRegister, Imm32(term-&gt;quantityMaxCount.unsafeGet())).linkTo(loop, this);
</ins><span class="cx"> 
</span><span class="cx">             failures.link(this);
</span><span class="cx">         }
</span><span class="lines">@@ -1020,8 +1020,8 @@
</span><span class="cx">         if (!((ch &gt; 0xff) &amp;&amp; (m_charSize == Char8))) {
</span><span class="cx">             JumpList nonGreedyFailures;
</span><span class="cx">             nonGreedyFailures.append(atEndOfInput());
</span><del>-            if (term-&gt;quantityCount != quantifyInfinite)
-                nonGreedyFailures.append(branch32(Equal, countRegister, Imm32(term-&gt;quantityCount.unsafeGet())));
</del><ins>+            if (term-&gt;quantityMaxCount != quantifyInfinite)
+                nonGreedyFailures.append(branch32(Equal, countRegister, Imm32(term-&gt;quantityMaxCount.unsafeGet())));
</ins><span class="cx">             nonGreedyFailures.append(jumpIfCharNotEquals(ch, m_checkedOffset - term-&gt;inputPosition, character));
</span><span class="cx"> 
</span><span class="cx">             add32(TrustedImm32(1), countRegister);
</span><span class="lines">@@ -1067,11 +1067,11 @@
</span><span class="cx">         const RegisterID countRegister = regT1;
</span><span class="cx"> 
</span><span class="cx">         move(index, countRegister);
</span><del>-        sub32(Imm32(term-&gt;quantityCount.unsafeGet()), countRegister);
</del><ins>+        sub32(Imm32(term-&gt;quantityMaxCount.unsafeGet()), countRegister);
</ins><span class="cx"> 
</span><span class="cx">         Label loop(this);
</span><span class="cx">         JumpList matchDest;
</span><del>-        readCharacter(m_checkedOffset - term-&gt;inputPosition - term-&gt;quantityCount, character, countRegister);
</del><ins>+        readCharacter(m_checkedOffset - term-&gt;inputPosition - term-&gt;quantityMaxCount, character, countRegister);
</ins><span class="cx">         matchCharacterClass(character, matchDest, term-&gt;characterClass);
</span><span class="cx"> 
</span><span class="cx">         if (term-&gt;invert())
</span><span class="lines">@@ -1116,8 +1116,8 @@
</span><span class="cx"> 
</span><span class="cx">         add32(TrustedImm32(1), countRegister);
</span><span class="cx">         add32(TrustedImm32(1), index);
</span><del>-        if (term-&gt;quantityCount != quantifyInfinite) {
-            branch32(NotEqual, countRegister, Imm32(term-&gt;quantityCount.unsafeGet())).linkTo(loop, this);
</del><ins>+        if (term-&gt;quantityMaxCount != quantifyInfinite) {
+            branch32(NotEqual, countRegister, Imm32(term-&gt;quantityMaxCount.unsafeGet())).linkTo(loop, this);
</ins><span class="cx">             failures.append(jump());
</span><span class="cx">         } else
</span><span class="cx">             jump(loop);
</span><span class="lines">@@ -1169,7 +1169,7 @@
</span><span class="cx">         loadFromFrame(term-&gt;frameLocation, countRegister);
</span><span class="cx"> 
</span><span class="cx">         nonGreedyFailures.append(atEndOfInput());
</span><del>-        nonGreedyFailures.append(branch32(Equal, countRegister, Imm32(term-&gt;quantityCount.unsafeGet())));
</del><ins>+        nonGreedyFailures.append(branch32(Equal, countRegister, Imm32(term-&gt;quantityMaxCount.unsafeGet())));
</ins><span class="cx"> 
</span><span class="cx">         JumpList matchDest;
</span><span class="cx">         readCharacter(m_checkedOffset - term-&gt;inputPosition, character);
</span><span class="lines">@@ -1265,7 +1265,7 @@
</span><span class="cx">         case PatternTerm::TypePatternCharacter:
</span><span class="cx">             switch (term-&gt;quantityType) {
</span><span class="cx">             case QuantifierFixedCount:
</span><del>-                if (term-&gt;quantityCount == 1)
</del><ins>+                if (term-&gt;quantityMaxCount == 1)
</ins><span class="cx">                     generatePatternCharacterOnce(opIndex);
</span><span class="cx">                 else
</span><span class="cx">                     generatePatternCharacterFixed(opIndex);
</span><span class="lines">@@ -1282,7 +1282,7 @@
</span><span class="cx">         case PatternTerm::TypeCharacterClass:
</span><span class="cx">             switch (term-&gt;quantityType) {
</span><span class="cx">             case QuantifierFixedCount:
</span><del>-                if (term-&gt;quantityCount == 1)
</del><ins>+                if (term-&gt;quantityMaxCount == 1)
</ins><span class="cx">                     generateCharacterClassOnce(opIndex);
</span><span class="cx">                 else
</span><span class="cx">                     generateCharacterClassFixed(opIndex);
</span><span class="lines">@@ -1331,7 +1331,7 @@
</span><span class="cx">         case PatternTerm::TypePatternCharacter:
</span><span class="cx">             switch (term-&gt;quantityType) {
</span><span class="cx">             case QuantifierFixedCount:
</span><del>-                if (term-&gt;quantityCount == 1)
</del><ins>+                if (term-&gt;quantityMaxCount == 1)
</ins><span class="cx">                     backtrackPatternCharacterOnce(opIndex);
</span><span class="cx">                 else
</span><span class="cx">                     backtrackPatternCharacterFixed(opIndex);
</span><span class="lines">@@ -1348,7 +1348,7 @@
</span><span class="cx">         case PatternTerm::TypeCharacterClass:
</span><span class="cx">             switch (term-&gt;quantityType) {
</span><span class="cx">             case QuantifierFixedCount:
</span><del>-                if (term-&gt;quantityCount == 1)
</del><ins>+                if (term-&gt;quantityMaxCount == 1)
</ins><span class="cx">                     backtrackCharacterClassOnce(opIndex);
</span><span class="cx">                 else
</span><span class="cx">                     backtrackCharacterClassFixed(opIndex);
</span><span class="lines">@@ -1617,7 +1617,7 @@
</span><span class="cx">                 PatternTerm* term = op.m_term;
</span><span class="cx">                 unsigned parenthesesFrameLocation = term-&gt;frameLocation;
</span><span class="cx">                 const RegisterID indexTemporary = regT0;
</span><del>-                ASSERT(term-&gt;quantityCount == 1);
</del><ins>+                ASSERT(term-&gt;quantityMaxCount == 1);
</ins><span class="cx"> 
</span><span class="cx">                 // Upon entry to a Greedy quantified set of parenthese store the index.
</span><span class="cx">                 // We'll use this for two purposes:
</span><span class="lines">@@ -1664,7 +1664,7 @@
</span><span class="cx">             case OpParenthesesSubpatternOnceEnd: {
</span><span class="cx">                 PatternTerm* term = op.m_term;
</span><span class="cx">                 const RegisterID indexTemporary = regT0;
</span><del>-                ASSERT(term-&gt;quantityCount == 1);
</del><ins>+                ASSERT(term-&gt;quantityMaxCount == 1);
</ins><span class="cx"> 
</span><span class="cx">                 // Runtime ASSERT to make sure that the nested alternative handled the
</span><span class="cx">                 // &quot;no input consumed&quot; check.
</span><span class="lines">@@ -1707,7 +1707,7 @@
</span><span class="cx">             case OpParenthesesSubpatternTerminalBegin: {
</span><span class="cx">                 PatternTerm* term = op.m_term;
</span><span class="cx">                 ASSERT(term-&gt;quantityType == QuantifierGreedy);
</span><del>-                ASSERT(term-&gt;quantityCount == quantifyInfinite);
</del><ins>+                ASSERT(term-&gt;quantityMaxCount == quantifyInfinite);
</ins><span class="cx">                 ASSERT(!term-&gt;capture());
</span><span class="cx"> 
</span><span class="cx">                 // Upon entry set a label to loop back to.
</span><span class="lines">@@ -2191,7 +2191,7 @@
</span><span class="cx">             // matching start, depending of whether the match is Greedy or NonGreedy.
</span><span class="cx">             case OpParenthesesSubpatternOnceBegin: {
</span><span class="cx">                 PatternTerm* term = op.m_term;
</span><del>-                ASSERT(term-&gt;quantityCount == 1);
</del><ins>+                ASSERT(term-&gt;quantityMaxCount == 1);
</ins><span class="cx"> 
</span><span class="cx">                 // We only need to backtrack to thispoint if capturing or greedy.
</span><span class="cx">                 if ((term-&gt;capture() &amp;&amp; compileMode == IncludeSubpatterns) || term-&gt;quantityType == QuantifierGreedy) {
</span><span class="lines">@@ -2330,7 +2330,7 @@
</span><span class="cx">     // Emits ops for a subpattern (set of parentheses). These consist
</span><span class="cx">     // of a set of alternatives wrapped in an outer set of nodes for
</span><span class="cx">     // the parentheses.
</span><del>-    // Supported types of parentheses are 'Once' (quantityCount == 1)
</del><ins>+    // Supported types of parentheses are 'Once' (quantityMaxCount == 1)
</ins><span class="cx">     // and 'Terminal' (non-capturing parentheses quantified as greedy
</span><span class="cx">     // and infinite).
</span><span class="cx">     // Alternatives will use the 'Simple' set of ops if either the
</span><span class="lines">@@ -2351,7 +2351,10 @@
</span><span class="cx">         // comes where the subpattern is capturing, in which case we would
</span><span class="cx">         // need to restore the capture from the first subpattern upon a
</span><span class="cx">         // failure in the second.
</span><del>-        if (term-&gt;quantityCount == 1 &amp;&amp; !term-&gt;parentheses.isCopy) {
</del><ins>+        if (term-&gt;quantityMinCount &amp;&amp; term-&gt;quantityMinCount != term-&gt;quantityMaxCount) {
+            m_shouldFallBack = true;
+            return;
+        } if (term-&gt;quantityMaxCount == 1 &amp;&amp; !term-&gt;parentheses.isCopy) {
</ins><span class="cx">             // Select the 'Once' nodes.
</span><span class="cx">             parenthesesBeginOpCode = OpParenthesesSubpatternOnceBegin;
</span><span class="cx">             parenthesesEndOpCode = OpParenthesesSubpatternOnceEnd;
</span></span></pre></div>
<a id="branchessafari603branchSourceJavaScriptCoreyarrYarrPatterncpp"></a>
<div class="modfile"><h4>Modified: branches/safari-603-branch/Source/JavaScriptCore/yarr/YarrPattern.cpp (210853 => 210854)</h4>
<pre class="diff"><span>
<span class="info">--- branches/safari-603-branch/Source/JavaScriptCore/yarr/YarrPattern.cpp        2017-01-18 18:37:30 UTC (rev 210853)
+++ branches/safari-603-branch/Source/JavaScriptCore/yarr/YarrPattern.cpp        2017-01-18 18:45:12 UTC (rev 210854)
</span><span class="lines">@@ -522,6 +522,7 @@
</span><span class="cx">         
</span><span class="cx">         PatternTerm termCopy = term;
</span><span class="cx">         termCopy.parentheses.disjunction = copyDisjunction(termCopy.parentheses.disjunction, filterStartsWithBOL);
</span><ins>+        m_pattern.m_hasCopiedParenSubexpressions = true;
</ins><span class="cx">         return termCopy;
</span><span class="cx">     }
</span><span class="cx">     
</span><span class="lines">@@ -537,7 +538,7 @@
</span><span class="cx"> 
</span><span class="cx">         PatternTerm&amp; term = m_alternative-&gt;lastTerm();
</span><span class="cx">         ASSERT(term.type &gt; PatternTerm::TypeAssertionWordBoundary);
</span><del>-        ASSERT((term.quantityCount == 1) &amp;&amp; (term.quantityType == QuantifierFixedCount));
</del><ins>+        ASSERT(term.quantityMinCount == 1 &amp;&amp; term.quantityMaxCount == 1 &amp;&amp; term.quantityType == QuantifierFixedCount);
</ins><span class="cx"> 
</span><span class="cx">         if (term.type == PatternTerm::TypeParentheticalAssertion) {
</span><span class="cx">             // If an assertion is quantified with a minimum count of zero, it can simply be removed.
</span><span class="lines">@@ -559,12 +560,12 @@
</span><span class="cx">             return;
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        if (min == 0)
-            term.quantify(max, greedy   ? QuantifierGreedy : QuantifierNonGreedy);
-        else if (min == max)
-            term.quantify(min, QuantifierFixedCount);
</del><ins>+        if (min == max)
+            term.quantify(min, max, QuantifierFixedCount);
+        else if (!min || (term.type == PatternTerm::TypeParenthesesSubpattern &amp;&amp; m_pattern.m_hasCopiedParenSubexpressions))
+            term.quantify(min, max, greedy ? QuantifierGreedy : QuantifierNonGreedy);
</ins><span class="cx">         else {
</span><del>-            term.quantify(min, QuantifierFixedCount);
</del><ins>+            term.quantify(min, min, QuantifierFixedCount);
</ins><span class="cx">             m_alternative-&gt;m_terms.append(copyTerm(term));
</span><span class="cx">             // NOTE: this term is interesting from an analysis perspective, in that it can be ignored.....
</span><span class="cx">             m_alternative-&gt;lastTerm().quantify((max == quantifyInfinite) ? max : max - min, greedy ? QuantifierGreedy : QuantifierNonGreedy);
</span><span class="lines">@@ -614,9 +615,9 @@
</span><span class="cx">                     currentCallFrameSize += YarrStackSpaceForBackTrackInfoPatternCharacter;
</span><span class="cx">                     alternative-&gt;m_hasFixedSize = false;
</span><span class="cx">                 } else if (m_pattern.unicode()) {
</span><del>-                    currentInputPosition += U16_LENGTH(term.patternCharacter) * term.quantityCount;
</del><ins>+                    currentInputPosition += U16_LENGTH(term.patternCharacter) * term.quantityMaxCount;
</ins><span class="cx">                 } else
</span><del>-                    currentInputPosition += term.quantityCount;
</del><ins>+                    currentInputPosition += term.quantityMaxCount;
</ins><span class="cx">                 break;
</span><span class="cx"> 
</span><span class="cx">             case PatternTerm::TypeCharacterClass:
</span><span class="lines">@@ -628,16 +629,16 @@
</span><span class="cx">                 } else if (m_pattern.unicode()) {
</span><span class="cx">                     term.frameLocation = currentCallFrameSize;
</span><span class="cx">                     currentCallFrameSize += YarrStackSpaceForBackTrackInfoCharacterClass;
</span><del>-                    currentInputPosition += term.quantityCount;
</del><ins>+                    currentInputPosition += term.quantityMaxCount;
</ins><span class="cx">                     alternative-&gt;m_hasFixedSize = false;
</span><span class="cx">                 } else
</span><del>-                    currentInputPosition += term.quantityCount;
</del><ins>+                    currentInputPosition += term.quantityMaxCount;
</ins><span class="cx">                 break;
</span><span class="cx"> 
</span><span class="cx">             case PatternTerm::TypeParenthesesSubpattern:
</span><span class="cx">                 // Note: for fixed once parentheses we will ensure at least the minimum is available; others are on their own.
</span><span class="cx">                 term.frameLocation = currentCallFrameSize;
</span><del>-                if (term.quantityCount == 1 &amp;&amp; !term.parentheses.isCopy) {
</del><ins>+                if (term.quantityMaxCount == 1 &amp;&amp; !term.parentheses.isCopy) {
</ins><span class="cx">                     if (term.quantityType != QuantifierFixedCount)
</span><span class="cx">                         currentCallFrameSize += YarrStackSpaceForBackTrackInfoParenthesesOnce;
</span><span class="cx">                     error = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition.unsafeGet(), currentCallFrameSize);
</span><span class="lines">@@ -754,7 +755,8 @@
</span><span class="cx">                 PatternTerm&amp; term = terms.last();
</span><span class="cx">                 if (term.type == PatternTerm::TypeParenthesesSubpattern
</span><span class="cx">                     &amp;&amp; term.quantityType == QuantifierGreedy
</span><del>-                    &amp;&amp; term.quantityCount == quantifyInfinite
</del><ins>+                    &amp;&amp; term.quantityMinCount == 0
+                    &amp;&amp; term.quantityMaxCount == quantifyInfinite
</ins><span class="cx">                     &amp;&amp; !term.capture())
</span><span class="cx">                     term.parentheses.isTerminal = true;
</span><span class="cx">             }
</span><span class="lines">@@ -955,6 +957,7 @@
</span><span class="cx">     : m_containsBackreferences(false)
</span><span class="cx">     , m_containsBOL(false)
</span><span class="cx">     , m_containsUnsignedLengthPattern(false)
</span><ins>+    , m_hasCopiedParenSubexpressions(false)
</ins><span class="cx">     , m_flags(flags)
</span><span class="cx">     , m_numSubpatterns(0)
</span><span class="cx">     , m_maxBackReference(0)
</span></span></pre></div>
<a id="branchessafari603branchSourceJavaScriptCoreyarrYarrPatternh"></a>
<div class="modfile"><h4>Modified: branches/safari-603-branch/Source/JavaScriptCore/yarr/YarrPattern.h (210853 => 210854)</h4>
<pre class="diff"><span>
<span class="info">--- branches/safari-603-branch/Source/JavaScriptCore/yarr/YarrPattern.h        2017-01-18 18:37:30 UTC (rev 210853)
+++ branches/safari-603-branch/Source/JavaScriptCore/yarr/YarrPattern.h        2017-01-18 18:45:12 UTC (rev 210854)
</span><span class="lines">@@ -108,7 +108,8 @@
</span><span class="cx">         } anchors;
</span><span class="cx">     };
</span><span class="cx">     QuantifierType quantityType;
</span><del>-    Checked&lt;unsigned&gt; quantityCount;
</del><ins>+    Checked&lt;unsigned&gt; quantityMinCount;
+    Checked&lt;unsigned&gt; quantityMaxCount;
</ins><span class="cx">     unsigned inputPosition;
</span><span class="cx">     unsigned frameLocation;
</span><span class="cx"> 
</span><span class="lines">@@ -119,7 +120,7 @@
</span><span class="cx">     {
</span><span class="cx">         patternCharacter = ch;
</span><span class="cx">         quantityType = QuantifierFixedCount;
</span><del>-        quantityCount = 1;
</del><ins>+        quantityMinCount = quantityMaxCount = 1;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     PatternTerm(CharacterClass* charClass, bool invert)
</span><span class="lines">@@ -129,7 +130,7 @@
</span><span class="cx">     {
</span><span class="cx">         characterClass = charClass;
</span><span class="cx">         quantityType = QuantifierFixedCount;
</span><del>-        quantityCount = 1;
</del><ins>+        quantityMinCount = quantityMaxCount = 1;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     PatternTerm(Type type, unsigned subpatternId, PatternDisjunction* disjunction, bool capture = false, bool invert = false)
</span><span class="lines">@@ -142,7 +143,7 @@
</span><span class="cx">         parentheses.isCopy = false;
</span><span class="cx">         parentheses.isTerminal = false;
</span><span class="cx">         quantityType = QuantifierFixedCount;
</span><del>-        quantityCount = 1;
</del><ins>+        quantityMinCount = quantityMaxCount = 1;
</ins><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     PatternTerm(Type type, bool invert = false)
</span><span class="lines">@@ -151,7 +152,7 @@
</span><span class="cx">         , m_invert(invert)
</span><span class="cx">     {
</span><span class="cx">         quantityType = QuantifierFixedCount;
</span><del>-        quantityCount = 1;
</del><ins>+        quantityMinCount = quantityMaxCount = 1;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     PatternTerm(unsigned spatternId)
</span><span class="lines">@@ -161,7 +162,7 @@
</span><span class="cx">     {
</span><span class="cx">         backReferenceSubpatternId = spatternId;
</span><span class="cx">         quantityType = QuantifierFixedCount;
</span><del>-        quantityCount = 1;
</del><ins>+        quantityMinCount = quantityMaxCount = 1;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     PatternTerm(bool bolAnchor, bool eolAnchor)
</span><span class="lines">@@ -172,7 +173,7 @@
</span><span class="cx">         anchors.bolAnchor = bolAnchor;
</span><span class="cx">         anchors.eolAnchor = eolAnchor;
</span><span class="cx">         quantityType = QuantifierFixedCount;
</span><del>-        quantityCount = 1;
</del><ins>+        quantityMinCount = quantityMaxCount = 1;
</ins><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     static PatternTerm ForwardReference()
</span><span class="lines">@@ -207,9 +208,20 @@
</span><span class="cx">     
</span><span class="cx">     void quantify(unsigned count, QuantifierType type)
</span><span class="cx">     {
</span><del>-        quantityCount = count;
</del><ins>+        quantityMinCount = 0;
+        quantityMaxCount = count;
</ins><span class="cx">         quantityType = type;
</span><span class="cx">     }
</span><ins>+
+    void quantify(unsigned minCount, unsigned maxCount, QuantifierType type)
+    {
+        // Currently only Parentheses can specify a non-zero min with a different max.
+        ASSERT(this-&gt;type == TypeParenthesesSubpattern || !minCount || minCount == maxCount);
+        ASSERT(minCount &lt;= maxCount);
+        quantityMinCount = minCount;
+        quantityMaxCount = maxCount;
+        quantityType = type;
+    }
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> struct PatternAlternative {
</span><span class="lines">@@ -334,6 +346,7 @@
</span><span class="cx">         m_containsBackreferences = false;
</span><span class="cx">         m_containsBOL = false;
</span><span class="cx">         m_containsUnsignedLengthPattern = false;
</span><ins>+        m_hasCopiedParenSubexpressions = false;
</ins><span class="cx"> 
</span><span class="cx">         newlineCached = 0;
</span><span class="cx">         digitsCached = 0;
</span><span class="lines">@@ -439,7 +452,8 @@
</span><span class="cx"> 
</span><span class="cx">     bool m_containsBackreferences : 1;
</span><span class="cx">     bool m_containsBOL : 1;
</span><del>-    bool m_containsUnsignedLengthPattern : 1; 
</del><ins>+    bool m_containsUnsignedLengthPattern : 1;
+    bool m_hasCopiedParenSubexpressions : 1;
</ins><span class="cx">     RegExpFlags m_flags;
</span><span class="cx">     unsigned m_numSubpatterns;
</span><span class="cx">     unsigned m_maxBackReference;
</span></span></pre>
</div>
</div>

</body>
</html>