Skip to content

Commit

Permalink
[Yarr] Regex Lookbehinds differs from v8
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=273254
rdar://127440248

Reviewed by Yusuke Suzuki.

Updated the checked offset calculation for lookbehinds.  The count of characters we need for a lookbehind should not be
dependent of the count for the surrounding expression.  We now use the minimum checked size selected from either the already
checked characters or the parenthesis checked characters (if they are different).  The prior code would check the already checked
parenthesis count first and then the disjunction's minimum size.  Now we take the lessor of the two minimums to compute the
amount we need to check when trying to match the lookbehind.  We still need to uncheck the appropriate amount based on either
the disjunction minimum or parenthesis when we are done handling the lookbehind.  That is now handled with the new variable,
backwardUncheckAmount.

Deleted some dead code discovered while fixing this issue.

New test added.

* JSTests/stress/regexp-lookbehind.js:
* Source/JavaScriptCore/yarr/YarrInterpreter.cpp:
(JSC::Yarr::ByteCompiler::emitDisjunction):

Canonical link: https://commits.webkit.org/278863@main
  • Loading branch information
msaboff authored and dylan-conway committed May 20, 2024
1 parent 7c97f5e commit 2c4f31e
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 16 deletions.
3 changes: 3 additions & 0 deletions JSTests/stress/regexp-lookbehind.js
Original file line number Diff line number Diff line change
Expand Up @@ -232,3 +232,6 @@ testRegExp(/(?<!(ab*?))c/i, "A", null);
testRegExp(/(?<!(AB*?))c/, "A", null);
testRegExp(/(?<!(ab*))c/i, "A", null);
testRegExp(/(?<!(AB*))c/, "A", null);

// Test 101
testRegExp(/(?<=Result = |<)(\w+(?:Error|Response))/, "<Result = LoadFinishedResponse>(", ["LoadFinishedResponse", "LoadFinishedResponse"]);
31 changes: 15 additions & 16 deletions Source/JavaScriptCore/yarr/YarrInterpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2625,15 +2625,22 @@ class ByteCompiler {
ASSERT(matchDirection == Backward || minimumSize >= parenthesesInputCountAlreadyChecked);

unsigned countToCheck = 0;
unsigned backwardUncheckAmount = 0;

if (matchDirection == Forward)
countToCheck = minimumSize - parenthesesInputCountAlreadyChecked;
else if (minimumSize > parenthesesInputCountAlreadyChecked) {
countToCheck = minimumSize - parenthesesInputCountAlreadyChecked;
haveCheckedInput(minimumSize);
} else if (minimumSize > disjunction->m_minimumSize) {
countToCheck = minimumSize - disjunction->m_minimumSize;
haveCheckedInput(currentCountAlreadyChecked);
else {
// Backward case
unsigned minAlreadyChecked = std::min(disjunction->m_minimumSize, parenthesesInputCountAlreadyChecked);
if (minimumSize > minAlreadyChecked) {
countToCheck = minimumSize - minAlreadyChecked;
haveCheckedInput(countToCheck + currentCountAlreadyChecked);

if (minimumSize > disjunction->m_minimumSize)
backwardUncheckAmount = countToCheck;
else
backwardUncheckAmount = minimumSize;
}
}

if (countToCheck) {
Expand Down Expand Up @@ -2737,7 +2744,6 @@ class ByteCompiler {
return ErrorCode::OffsetTooLarge;
}
} else { // Backward
unsigned uncheckAmount = 0;
CheckedUint32 checkedCountForLookbehind = currentCountAlreadyChecked;
ASSERT(checkedCountForLookbehind >= term.inputPosition);
checkedCountForLookbehind -= term.inputPosition;
Expand All @@ -2758,13 +2764,6 @@ class ByteCompiler {
if (auto error = emitDisjunction(term.parentheses.disjunction, checkedCountForLookbehind, positiveInputOffset + minimumSize, term.matchDirection()))
return error;
atomParentheticalAssertionEnd(term.parentheses.lastSubpatternId, term.frameLocation, term.quantityMaxCount, term.quantityType);

if (uncheckAmount) {
checkInput(uncheckAmount);
currentCountAlreadyChecked += uncheckAmount;
if (currentCountAlreadyChecked.hasOverflowed())
return ErrorCode::OffsetTooLarge;
}
}
break;
}
Expand All @@ -2775,8 +2774,8 @@ class ByteCompiler {
}
}

if (matchDirection == Backward && countToCheck)
uncheckInput(countToCheck);
if (matchDirection == Backward && backwardUncheckAmount)
uncheckInput(backwardUncheckAmount);
}
return std::nullopt;
}
Expand Down

0 comments on commit 2c4f31e

Please sign in to comment.