<!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>[210837] 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/210837">210837</a></dd>
<dt>Author</dt> <dd>msaboff@apple.com</dd>
<dt>Date</dt> <dd>2017-01-17 17:27:04 -0800 (Tue, 17 Jan 2017)</dd>
</dl>
<h3>Log Message</h3>
<pre>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.
JSTests:
* 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.
Source/JavaScriptCore:
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):</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkJSTestsChangeLog">trunk/JSTests/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreyarrYarrInterpretercpp">trunk/Source/JavaScriptCore/yarr/YarrInterpreter.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreyarrYarrInterpreterh">trunk/Source/JavaScriptCore/yarr/YarrInterpreter.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreyarrYarrJITcpp">trunk/Source/JavaScriptCore/yarr/YarrJIT.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreyarrYarrPatterncpp">trunk/Source/JavaScriptCore/yarr/YarrPattern.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreyarrYarrPatternh">trunk/Source/JavaScriptCore/yarr/YarrPattern.h</a></li>
</ul>
<h3>Added Paths</h3>
<ul>
<li><a href="#trunkJSTestsmicrobenchmarksregexpnestednonzeromincountedparensjs">trunk/JSTests/microbenchmarks/regexp-nested-nonzero-min-counted-parens.js</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkJSTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/JSTests/ChangeLog (210836 => 210837)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/ChangeLog        2017-01-18 01:21:16 UTC (rev 210836)
+++ trunk/JSTests/ChangeLog        2017-01-18 01:27:04 UTC (rev 210837)
</span><span class="lines">@@ -1,3 +1,14 @@
</span><ins>+2017-01-17 Michael Saboff <msaboff@apple.com>
+
+ 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-14 Yusuke Suzuki <utatane.tea@gmail.com>
</span><span class="cx">
</span><span class="cx"> Annotate large string tests with largeHeap
</span></span></pre></div>
<a id="trunkJSTestsmicrobenchmarksregexpnestednonzeromincountedparensjs"></a>
<div class="addfile"><h4>Added: trunk/JSTests/microbenchmarks/regexp-nested-nonzero-min-counted-parens.js (0 => 210837)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/microbenchmarks/regexp-nested-nonzero-min-counted-parens.js         (rev 0)
+++ trunk/JSTests/microbenchmarks/regexp-nested-nonzero-min-counted-parens.js        2017-01-18 01:27:04 UTC (rev 210837)
</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("a");
+
+/$(?:$(?:$(?:$(?:$(?:$(?:$(?:$(?:$(?:$(?:$(?:$(?:$(?:$(?:$(?:$(?:$(?:$(?:$(?:$(?:${-2,16}+)+)+)+)+)+)+)+)+)+)+)+)+)+)+)+)+)+)+)+)+/.exec("a");
+
+/$(?=$(?=$(?=$(?=$(?=$(?=$(?=$(?=$(?=$(?=$(?=$(?=$(?=$(?=$(?=$(?=$(?=$(?=$(?=$(?=${-2,16}+)+)+)+)+)+)+)+)+)+)+)+)+)+)+)+)+)+)+)+)+/.exec("a");
+
+
+
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (210836 => 210837)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2017-01-18 01:21:16 UTC (rev 210836)
+++ trunk/Source/JavaScriptCore/ChangeLog        2017-01-18 01:27:04 UTC (rev 210837)
</span><span class="lines">@@ -1,3 +1,79 @@
</span><ins>+2017-01-17 Michael Saboff <msaboff@apple.com>
+
+ 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-17 Joseph Pecoraro <pecoraro@apple.com>
</span><span class="cx">
</span><span class="cx"> ENABLE(USER_TIMING) Not Defined for Apple Windows or OS X Ports
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreyarrYarrInterpretercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/yarr/YarrInterpreter.cpp (210836 => 210837)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/yarr/YarrInterpreter.cpp        2017-01-18 01:21:16 UTC (rev 210836)
+++ trunk/Source/JavaScriptCore/yarr/YarrInterpreter.cpp        2017-01-18 01:27:04 UTC (rev 210837)
</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->matchAmount < term.atom.quantityCount) && input.checkInput(1)) {
</del><ins>+ if ((backTrack->matchAmount < term.atom.quantityMaxCount) && input.checkInput(1)) {
</ins><span class="cx"> ++backTrack->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->matchAmount < term.atom.quantityCount) && input.checkInput(1)) {
</del><ins>+ if ((backTrack->matchAmount < term.atom.quantityMaxCount) && input.checkInput(1)) {
</ins><span class="cx"> ++backTrack->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->begin = input.getPos();
</span><span class="cx"> unsigned matchAmount = 0;
</span><del>- for (matchAmount = 0; matchAmount < term.atom.quantityCount; ++matchAmount) {
</del><ins>+ for (matchAmount = 0; matchAmount < 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->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 < term.atom.quantityCount; ++matchAmount) {
</del><ins>+ for (unsigned matchAmount = 0; matchAmount < 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->begin = position;
</span><span class="cx"> unsigned matchAmount = 0;
</span><del>- while ((matchAmount < term.atom.quantityCount) && input.checkInput(1)) {
</del><ins>+ while ((matchAmount < term.atom.quantityMaxCount) && 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->matchAmount < term.atom.quantityCount) && input.checkInput(1)) {
</del><ins>+ if ((backTrack->matchAmount < term.atom.quantityMaxCount) && input.checkInput(1)) {
</ins><span class="cx"> ++backTrack->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->begin = input.getPos();
</span><del>- for (unsigned matchAmount = 0; matchAmount < term.atom.quantityCount; ++matchAmount) {
</del><ins>+ for (unsigned matchAmount = 0; matchAmount < term.atom.quantityMaxCount; ++matchAmount) {
</ins><span class="cx"> if (!tryConsumeBackReference(matchBegin, matchEnd, term.inputPosition)) {
</span><span class="cx"> input.setPos(backTrack->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 < term.atom.quantityCount) && tryConsumeBackReference(matchBegin, matchEnd, term.inputPosition))
</del><ins>+ while ((matchAmount < term.atom.quantityMaxCount) && tryConsumeBackReference(matchBegin, matchEnd, term.inputPosition))
</ins><span class="cx"> ++matchAmount;
</span><span class="cx"> backTrack->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->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->matchAmount < term.atom.quantityCount) && tryConsumeBackReference(matchBegin, matchEnd, term.inputPosition)) {
</del><ins>+ if ((backTrack->matchAmount < term.atom.quantityMaxCount) && tryConsumeBackReference(matchBegin, matchEnd, term.inputPosition)) {
</ins><span class="cx"> ++backTrack->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& 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<BackTrackInfoParenthesesOnce*>(context->frame + term.frameLocation);
</span><span class="cx">
</span><span class="lines">@@ -738,7 +738,7 @@
</span><span class="cx"> bool matchParenthesesOnceEnd(ByteTerm& 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& 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<BackTrackInfoParenthesesOnce*>(context->frame + term.frameLocation);
</span><span class="cx">
</span><span class="lines">@@ -785,7 +785,7 @@
</span><span class="cx"> bool backtrackParenthesesOnceEnd(ByteTerm& 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<BackTrackInfoParenthesesOnce*>(context->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<BackTrackInfoParenthesesTerminal*>(context->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& 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<BackTrackInfoParentheticalAssertion*>(context->frame + term.frameLocation);
</span><span class="cx">
</span><span class="lines">@@ -880,7 +880,7 @@
</span><span class="cx"> bool matchParentheticalAssertionEnd(ByteTerm& 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<BackTrackInfoParentheticalAssertion*>(context->frame + term.frameLocation);
</span><span class="cx">
</span><span class="lines">@@ -898,7 +898,7 @@
</span><span class="cx"> bool backtrackParentheticalAssertionBegin(ByteTerm& 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& 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<BackTrackInfoParentheticalAssertion*>(context->frame + term.frameLocation);
</span><span class="cx">
</span><span class="lines">@@ -932,22 +932,27 @@
</span><span class="cx"> backTrack->matchAmount = 0;
</span><span class="cx"> backTrack->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->matchAmount < term.atom.quantityCount) {
</del><ins>+ while (backTrack->matchAmount < 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->getDisjunctionContext(term));
- if (result == JSRegExpMatch)
</del><ins>+ fixedMatchResult = matchDisjunction(disjunctionBody, context->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->matchAmount == term.atom.quantityCount);
</del><span class="cx"> ParenthesesDisjunctionContext* context = backTrack->lastContext;
</span><span class="cx"> recordParenthesesMatch(term, context);
</span><ins>+ }
+
+ switch (term.atom.quantityType) {
+ case QuantifierFixedCount: {
+ ASSERT(backTrack->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->matchAmount < term.atom.quantityCount) {
</del><ins>+ while (backTrack->matchAmount < term.atom.quantityMaxCount) {
</ins><span class="cx"> ParenthesesDisjunctionContext* context = allocParenthesesDisjunctionContext(disjunctionBody, output, term);
</span><span class="cx"> JSRegExpResult result = matchNonZeroDisjunction(disjunctionBody, context->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->matchAmount == term.atom.quantityCount);
</del><ins>+ ASSERT(backTrack->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->matchAmount < term.atom.quantityCount) {
</del><ins>+ while (backTrack->matchAmount < 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->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->matchAmount == term.atom.quantityCount);
</del><ins>+ ASSERT(backTrack->matchAmount == term.atom.quantityMaxCount);
</ins><span class="cx"> context = backTrack->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->lastContext;
</span><span class="cx"> JSRegExpResult result = matchNonZeroDisjunction(disjunctionBody, context->getDisjunctionContext(term), true);
</span><span class="cx"> if (result == JSRegExpMatch) {
</span><del>- while (backTrack->matchAmount < term.atom.quantityCount) {
</del><ins>+ while (backTrack->matchAmount < term.atom.quantityMaxCount) {
</ins><span class="cx"> ParenthesesDisjunctionContext* context = allocParenthesesDisjunctionContext(disjunctionBody, output, term);
</span><span class="cx"> JSRegExpResult parenthesesResult = matchNonZeroDisjunction(disjunctionBody, context->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->matchAmount < term.atom.quantityCount) {
</del><ins>+ if (backTrack->matchAmount < term.atom.quantityMaxCount) {
</ins><span class="cx"> ParenthesesDisjunctionContext* context = allocParenthesesDisjunctionContext(disjunctionBody, output, term);
</span><span class="cx"> JSRegExpResult result = matchNonZeroDisjunction(disjunctionBody, context->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 < currentTerm().atom.quantityCount; ++matchAmount) {
</del><ins>+ for (unsigned matchAmount = 0; matchAmount < 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 < currentTerm().atom.quantityCount; ++matchAmount) {
</del><ins>+ for (unsigned matchAmount = 0; matchAmount < 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<BackTrackInfoPatternCharacter*>(context->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 < currentTerm().atom.quantityCount) && input.checkInput(1)) {
</del><ins>+ while ((matchAmount < currentTerm().atom.quantityMaxCount) && 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 < currentTerm().atom.quantityCount; ++matchAmount) {
</del><ins>+ for (unsigned matchAmount = 0; matchAmount < 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 < currentTerm().atom.quantityCount; ++matchAmount) {
</del><ins>+ for (unsigned matchAmount = 0; matchAmount < 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 < currentTerm().atom.quantityCount) && input.checkInput(1)) {
</del><ins>+ while ((matchAmount < currentTerm().atom.quantityMaxCount) && 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->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<unsigned> quantityCount, QuantifierType quantityType)
</del><ins>+ void atomPatternCharacter(UChar32 ch, unsigned inputPosition, unsigned frameLocation, Checked<unsigned> 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->terms.append(ByteTerm(lo, hi, inputPosition, frameLocation, quantityCount, quantityType));
</del><ins>+ m_bodyDisjunction->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->terms.append(ByteTerm(ch, inputPosition, frameLocation, quantityCount, quantityType));
</del><ins>+ m_bodyDisjunction->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<unsigned> quantityCount, QuantifierType quantityType)
</del><ins>+ void atomCharacterClass(CharacterClass* characterClass, bool invert, unsigned inputPosition, unsigned frameLocation, Checked<unsigned> quantityMaxCount, QuantifierType quantityType)
</ins><span class="cx"> {
</span><span class="cx"> m_bodyDisjunction->terms.append(ByteTerm(characterClass, invert, inputPosition));
</span><span class="cx">
</span><del>- m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityCount = quantityCount.unsafeGet();
</del><ins>+ m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityMaxCount = quantityMaxCount.unsafeGet();
</ins><span class="cx"> m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityType = quantityType;
</span><span class="cx"> m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation;
</span><span class="cx"> }
</span><span class="cx">
</span><del>- void atomBackReference(unsigned subpatternId, unsigned inputPosition, unsigned frameLocation, Checked<unsigned> quantityCount, QuantifierType quantityType)
</del><ins>+ void atomBackReference(unsigned subpatternId, unsigned inputPosition, unsigned frameLocation, Checked<unsigned> quantityMaxCount, QuantifierType quantityType)
</ins><span class="cx"> {
</span><span class="cx"> ASSERT(subpatternId);
</span><span class="cx">
</span><span class="cx"> m_bodyDisjunction->terms.append(ByteTerm::BackReference(subpatternId, inputPosition));
</span><span class="cx">
</span><del>- m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityCount = quantityCount.unsafeGet();
</del><ins>+ m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityMaxCount = quantityMaxCount.unsafeGet();
</ins><span class="cx"> m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityType = quantityType;
</span><span class="cx"> m_bodyDisjunction->terms[m_bodyDisjunction->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<unsigned> quantityCount, QuantifierType quantityType)
</del><ins>+ void atomParentheticalAssertionEnd(unsigned inputPosition, unsigned frameLocation, Checked<unsigned> 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->terms[endTerm].atom.parenthesesWidth = endTerm - beginTerm;
</span><span class="cx"> m_bodyDisjunction->terms[endTerm].frameLocation = frameLocation;
</span><span class="cx">
</span><del>- m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet();
</del><ins>+ m_bodyDisjunction->terms[beginTerm].atom.quantityMaxCount = quantityMaxCount.unsafeGet();
</ins><span class="cx"> m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType;
</span><del>- m_bodyDisjunction->terms[endTerm].atom.quantityCount = quantityCount.unsafeGet();
</del><ins>+ m_bodyDisjunction->terms[endTerm].atom.quantityMaxCount = quantityMaxCount.unsafeGet();
</ins><span class="cx"> m_bodyDisjunction->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->terms[endIndex].frameLocation = frameLocation;
</span><span class="cx"> }
</span><span class="cx">
</span><del>- void atomParenthesesSubpatternEnd(unsigned lastSubpatternId, unsigned inputPosition, unsigned frameLocation, Checked<unsigned> quantityCount, QuantifierType quantityType, unsigned callFrameSize = 0)
</del><ins>+ void atomParenthesesSubpatternEnd(unsigned lastSubpatternId, unsigned inputPosition, unsigned frameLocation, Checked<unsigned> quantityMinCount, Checked<unsigned> 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->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->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet();
</del><ins>+ m_bodyDisjunction->terms[beginTerm].atom.quantityMinCount = quantityMinCount.unsafeGet();
+ m_bodyDisjunction->terms[beginTerm].atom.quantityMaxCount = quantityMaxCount.unsafeGet();
</ins><span class="cx"> m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType;
</span><span class="cx"> m_bodyDisjunction->terms[beginTerm].frameLocation = frameLocation;
</span><span class="cx"> }
</span><span class="cx">
</span><del>- void atomParenthesesOnceEnd(unsigned inputPosition, unsigned frameLocation, Checked<unsigned> quantityCount, QuantifierType quantityType)
</del><ins>+ void atomParenthesesOnceEnd(unsigned inputPosition, unsigned frameLocation, Checked<unsigned> quantityMinCount, Checked<unsigned> 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->terms[endTerm].atom.parenthesesWidth = endTerm - beginTerm;
</span><span class="cx"> m_bodyDisjunction->terms[endTerm].frameLocation = frameLocation;
</span><span class="cx">
</span><del>- m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet();
</del><ins>+ m_bodyDisjunction->terms[beginTerm].atom.quantityMinCount = quantityMinCount.unsafeGet();
+ m_bodyDisjunction->terms[beginTerm].atom.quantityMaxCount = quantityMaxCount.unsafeGet();
</ins><span class="cx"> m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType;
</span><del>- m_bodyDisjunction->terms[endTerm].atom.quantityCount = quantityCount.unsafeGet();
</del><ins>+ m_bodyDisjunction->terms[endTerm].atom.quantityMinCount = quantityMinCount.unsafeGet();
+ m_bodyDisjunction->terms[endTerm].atom.quantityMaxCount = quantityMaxCount.unsafeGet();
</ins><span class="cx"> m_bodyDisjunction->terms[endTerm].atom.quantityType = quantityType;
</span><span class="cx"> }
</span><span class="cx">
</span><del>- void atomParenthesesTerminalEnd(unsigned inputPosition, unsigned frameLocation, Checked<unsigned> quantityCount, QuantifierType quantityType)
</del><ins>+ void atomParenthesesTerminalEnd(unsigned inputPosition, unsigned frameLocation, Checked<unsigned> quantityMinCount, Checked<unsigned> 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->terms[endTerm].atom.parenthesesWidth = endTerm - beginTerm;
</span><span class="cx"> m_bodyDisjunction->terms[endTerm].frameLocation = frameLocation;
</span><span class="cx">
</span><del>- m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet();
</del><ins>+ m_bodyDisjunction->terms[beginTerm].atom.quantityMinCount = quantityMinCount.unsafeGet();
+ m_bodyDisjunction->terms[beginTerm].atom.quantityMaxCount = quantityMaxCount.unsafeGet();
</ins><span class="cx"> m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType;
</span><del>- m_bodyDisjunction->terms[endTerm].atom.quantityCount = quantityCount.unsafeGet();
</del><ins>+ m_bodyDisjunction->terms[endTerm].atom.quantityMinCount = quantityMinCount.unsafeGet();
+ m_bodyDisjunction->terms[endTerm].atom.quantityMaxCount = quantityMaxCount.unsafeGet();
</ins><span class="cx"> m_bodyDisjunction->terms[endTerm].atom.quantityType = quantityType;
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -1958,15 +1972,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">@@ -1974,7 +1988,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 && !term.parentheses.isCopy) {
</del><ins>+ if (term.quantityMaxCount == 1 && !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">@@ -1985,19 +1999,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 >= 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 >= 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->m_callFrameSize);
</del><ins>+ atomParenthesesSubpatternEnd(term.parentheses.lastSubpatternId, delegateEndInputOffset, term.frameLocation, term.quantityMinCount, term.quantityMaxCount, term.quantityType, term.parentheses.disjunction->m_callFrameSize);
</ins><span class="cx"> }
</span><span class="cx"> break;
</span><span class="cx"> }
</span><span class="lines">@@ -2016,7 +2030,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="trunkSourceJavaScriptCoreyarrYarrInterpreterh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/yarr/YarrInterpreter.h (210836 => 210837)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/yarr/YarrInterpreter.h        2017-01-18 01:21:16 UTC (rev 210836)
+++ trunk/Source/JavaScriptCore/yarr/YarrInterpreter.h        2017-01-18 01:27:04 UTC (rev 210837)
</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<unsigned> 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="trunkSourceJavaScriptCoreyarrYarrJITcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/yarr/YarrJIT.cpp (210836 => 210837)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/yarr/YarrJIT.cpp        2017-01-18 01:21:16 UTC (rev 210836)
+++ trunk/Source/JavaScriptCore/yarr/YarrJIT.cpp        2017-01-18 01:27:04 UTC (rev 210837)
</span><span class="lines">@@ -453,7 +453,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">@@ -830,7 +830,7 @@
</span><span class="cx">
</span><span class="cx"> if (nextTerm->type != PatternTerm::TypePatternCharacter
</span><span class="cx"> || nextTerm->quantityType != QuantifierFixedCount
</span><del>- || nextTerm->quantityCount != 1
</del><ins>+ || nextTerm->quantityMaxCount != 1
</ins><span class="cx"> || nextTerm->inputPosition != (startTermPosition + numberCharacters))
</span><span class="cx"> break;
</span><span class="cx">
</span><span class="lines">@@ -913,10 +913,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->quantityCount.unsafeGet()), countRegister);
</del><ins>+ sub32(Imm32(term->quantityMaxCount.unsafeGet()), countRegister);
</ins><span class="cx">
</span><span class="cx"> Label loop(this);
</span><del>- readCharacter(m_checkedOffset - term->inputPosition - term->quantityCount, character, countRegister);
</del><ins>+ readCharacter(m_checkedOffset - term->inputPosition - term->quantityMaxCount, character, countRegister);
</ins><span class="cx"> // For case-insesitive compares, non-ascii characters that have different
</span><span class="cx"> // upper & 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">@@ -954,10 +954,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->quantityCount == quantifyInfinite)
</del><ins>+ if (term->quantityMaxCount == quantifyInfinite)
</ins><span class="cx"> jump(loop);
</span><span class="cx"> else
</span><del>- branch32(NotEqual, countRegister, Imm32(term->quantityCount.unsafeGet())).linkTo(loop, this);
</del><ins>+ branch32(NotEqual, countRegister, Imm32(term->quantityMaxCount.unsafeGet())).linkTo(loop, this);
</ins><span class="cx">
</span><span class="cx"> failures.link(this);
</span><span class="cx"> }
</span><span class="lines">@@ -1009,8 +1009,8 @@
</span><span class="cx"> if (!((ch > 0xff) && (m_charSize == Char8))) {
</span><span class="cx"> JumpList nonGreedyFailures;
</span><span class="cx"> nonGreedyFailures.append(atEndOfInput());
</span><del>- if (term->quantityCount != quantifyInfinite)
- nonGreedyFailures.append(branch32(Equal, countRegister, Imm32(term->quantityCount.unsafeGet())));
</del><ins>+ if (term->quantityMaxCount != quantifyInfinite)
+ nonGreedyFailures.append(branch32(Equal, countRegister, Imm32(term->quantityMaxCount.unsafeGet())));
</ins><span class="cx"> nonGreedyFailures.append(jumpIfCharNotEquals(ch, m_checkedOffset - term->inputPosition, character));
</span><span class="cx">
</span><span class="cx"> add32(TrustedImm32(1), countRegister);
</span><span class="lines">@@ -1056,11 +1056,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->quantityCount.unsafeGet()), countRegister);
</del><ins>+ sub32(Imm32(term->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->inputPosition - term->quantityCount, character, countRegister);
</del><ins>+ readCharacter(m_checkedOffset - term->inputPosition - term->quantityMaxCount, character, countRegister);
</ins><span class="cx"> matchCharacterClass(character, matchDest, term->characterClass);
</span><span class="cx">
</span><span class="cx"> if (term->invert())
</span><span class="lines">@@ -1105,8 +1105,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->quantityCount != quantifyInfinite) {
- branch32(NotEqual, countRegister, Imm32(term->quantityCount.unsafeGet())).linkTo(loop, this);
</del><ins>+ if (term->quantityMaxCount != quantifyInfinite) {
+ branch32(NotEqual, countRegister, Imm32(term->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">@@ -1158,7 +1158,7 @@
</span><span class="cx"> loadFromFrame(term->frameLocation, countRegister);
</span><span class="cx">
</span><span class="cx"> nonGreedyFailures.append(atEndOfInput());
</span><del>- nonGreedyFailures.append(branch32(Equal, countRegister, Imm32(term->quantityCount.unsafeGet())));
</del><ins>+ nonGreedyFailures.append(branch32(Equal, countRegister, Imm32(term->quantityMaxCount.unsafeGet())));
</ins><span class="cx">
</span><span class="cx"> JumpList matchDest;
</span><span class="cx"> readCharacter(m_checkedOffset - term->inputPosition, character);
</span><span class="lines">@@ -1254,7 +1254,7 @@
</span><span class="cx"> case PatternTerm::TypePatternCharacter:
</span><span class="cx"> switch (term->quantityType) {
</span><span class="cx"> case QuantifierFixedCount:
</span><del>- if (term->quantityCount == 1)
</del><ins>+ if (term->quantityMaxCount == 1)
</ins><span class="cx"> generatePatternCharacterOnce(opIndex);
</span><span class="cx"> else
</span><span class="cx"> generatePatternCharacterFixed(opIndex);
</span><span class="lines">@@ -1271,7 +1271,7 @@
</span><span class="cx"> case PatternTerm::TypeCharacterClass:
</span><span class="cx"> switch (term->quantityType) {
</span><span class="cx"> case QuantifierFixedCount:
</span><del>- if (term->quantityCount == 1)
</del><ins>+ if (term->quantityMaxCount == 1)
</ins><span class="cx"> generateCharacterClassOnce(opIndex);
</span><span class="cx"> else
</span><span class="cx"> generateCharacterClassFixed(opIndex);
</span><span class="lines">@@ -1320,7 +1320,7 @@
</span><span class="cx"> case PatternTerm::TypePatternCharacter:
</span><span class="cx"> switch (term->quantityType) {
</span><span class="cx"> case QuantifierFixedCount:
</span><del>- if (term->quantityCount == 1)
</del><ins>+ if (term->quantityMaxCount == 1)
</ins><span class="cx"> backtrackPatternCharacterOnce(opIndex);
</span><span class="cx"> else
</span><span class="cx"> backtrackPatternCharacterFixed(opIndex);
</span><span class="lines">@@ -1337,7 +1337,7 @@
</span><span class="cx"> case PatternTerm::TypeCharacterClass:
</span><span class="cx"> switch (term->quantityType) {
</span><span class="cx"> case QuantifierFixedCount:
</span><del>- if (term->quantityCount == 1)
</del><ins>+ if (term->quantityMaxCount == 1)
</ins><span class="cx"> backtrackCharacterClassOnce(opIndex);
</span><span class="cx"> else
</span><span class="cx"> backtrackCharacterClassFixed(opIndex);
</span><span class="lines">@@ -1606,7 +1606,7 @@
</span><span class="cx"> PatternTerm* term = op.m_term;
</span><span class="cx"> unsigned parenthesesFrameLocation = term->frameLocation;
</span><span class="cx"> const RegisterID indexTemporary = regT0;
</span><del>- ASSERT(term->quantityCount == 1);
</del><ins>+ ASSERT(term->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">@@ -1653,7 +1653,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->quantityCount == 1);
</del><ins>+ ASSERT(term->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"> // "no input consumed" check.
</span><span class="lines">@@ -1696,7 +1696,7 @@
</span><span class="cx"> case OpParenthesesSubpatternTerminalBegin: {
</span><span class="cx"> PatternTerm* term = op.m_term;
</span><span class="cx"> ASSERT(term->quantityType == QuantifierGreedy);
</span><del>- ASSERT(term->quantityCount == quantifyInfinite);
</del><ins>+ ASSERT(term->quantityMaxCount == quantifyInfinite);
</ins><span class="cx"> ASSERT(!term->capture());
</span><span class="cx">
</span><span class="cx"> // Upon entry set a label to loop back to.
</span><span class="lines">@@ -2180,7 +2180,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->quantityCount == 1);
</del><ins>+ ASSERT(term->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->capture() && compileMode == IncludeSubpatterns) || term->quantityType == QuantifierGreedy) {
</span><span class="lines">@@ -2319,7 +2319,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">@@ -2340,7 +2340,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->quantityCount == 1 && !term->parentheses.isCopy) {
</del><ins>+ if (term->quantityMinCount && term->quantityMinCount != term->quantityMaxCount) {
+ m_shouldFallBack = true;
+ return;
+ } if (term->quantityMaxCount == 1 && !term->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="trunkSourceJavaScriptCoreyarrYarrPatterncpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/yarr/YarrPattern.cpp (210836 => 210837)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/yarr/YarrPattern.cpp        2017-01-18 01:21:16 UTC (rev 210836)
+++ trunk/Source/JavaScriptCore/yarr/YarrPattern.cpp        2017-01-18 01:27:04 UTC (rev 210837)
</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& term = m_alternative->lastTerm();
</span><span class="cx"> ASSERT(term.type > PatternTerm::TypeAssertionWordBoundary);
</span><del>- ASSERT((term.quantityCount == 1) && (term.quantityType == QuantifierFixedCount));
</del><ins>+ ASSERT(term.quantityMinCount == 1 && term.quantityMaxCount == 1 && 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 && 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->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->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->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->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 && !term.parentheses.isCopy) {
</del><ins>+ if (term.quantityMaxCount == 1 && !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& term = terms.last();
</span><span class="cx"> if (term.type == PatternTerm::TypeParenthesesSubpattern
</span><span class="cx"> && term.quantityType == QuantifierGreedy
</span><del>- && term.quantityCount == quantifyInfinite
</del><ins>+ && term.quantityMinCount == 0
+ && term.quantityMaxCount == quantifyInfinite
</ins><span class="cx"> && !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="trunkSourceJavaScriptCoreyarrYarrPatternh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/yarr/YarrPattern.h (210836 => 210837)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/yarr/YarrPattern.h        2017-01-18 01:21:16 UTC (rev 210836)
+++ trunk/Source/JavaScriptCore/yarr/YarrPattern.h        2017-01-18 01:27:04 UTC (rev 210837)
</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<unsigned> quantityCount;
</del><ins>+ Checked<unsigned> quantityMinCount;
+ Checked<unsigned> 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->type == TypeParenthesesSubpattern || !minCount || minCount == maxCount);
+ ASSERT(minCount <= 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>