Refactor YARR Stack Overflow Checks
authormsaboff@apple.com <msaboff@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 26 Mar 2020 23:27:57 +0000 (23:27 +0000)
committermsaboff@apple.com <msaboff@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 26 Mar 2020 23:27:57 +0000 (23:27 +0000)
https://bugs.webkit.org/show_bug.cgi?id=209435
rdar://problem/58988252

Reviewed by Mark Lam.

JSTests:

Added a new test and removed a now obsolete test.

* stress/regexp-compile-oom.js: Removed because the test is no longer valid.
Previously when therer where different stack check mechanisims we failed different.
This test was based on the different failure modes.  With these changes, most of
the contain subtests no longer throw as this test expects.
* stress/regexp-huge-oom.js: Added.
(shouldBe):
(shouldThrow):

Source/JavaScriptCore:

Refactored stack checks in YARR code including adding a stack check to the YARR JIT'ed code.
The C++ code including the parser, byte code compiler and interpreter now all use StackCheck.
The JIT'ed code needs a stack limit passed via a parameter since the JIT'ed code can be
called from the compiler thread when compiling DFG / FTL code.

Instead of adding a new parameter, consolidated the two pattern context buffer values, buffer
pointer and size, with the new stack limit into a new MatchingContextHolder, an RAII object.
The MatchingContextHolder constructor uses either the VM stack limit or the current thread's
stack limit depending on how it is called.

* runtime/RegExp.cpp:
(JSC::RegExp::finishCreation):
(JSC::RegExp::byteCodeCompileIfNecessary):
(JSC::RegExp::compile):
(JSC::RegExp::matchConcurrently):
(JSC::RegExp::compileMatchOnly):
* runtime/RegExp.h:
* runtime/RegExpInlines.h:
(JSC::RegExp::matchInline):
(JSC::PatternContextBufferHolder::PatternContextBufferHolder): Deleted.
(JSC::PatternContextBufferHolder::~PatternContextBufferHolder): Deleted.
(JSC::PatternContextBufferHolder::buffer): Deleted.
(JSC::PatternContextBufferHolder::size): Deleted.
(): Deleted.
* yarr/Yarr.h:
* yarr/YarrInterpreter.cpp:
(JSC::Yarr::Interpreter::matchDisjunction):
(JSC::Yarr::Interpreter::isSafeToRecurse):
* yarr/YarrJIT.cpp:
(JSC::Yarr::MatchingContextHolder::MatchingContextHolder):
(JSC::Yarr::MatchingContextHolder::~MatchingContextHolder):
(JSC::Yarr::YarrGenerator::initParenContextFreeList):
(JSC::Yarr::YarrGenerator::alignCallFrameSizeInBytes):
(JSC::Yarr::YarrGenerator::compile):
(JSC::Yarr::YarrGenerator::initCallFrame): Deleted.
* yarr/YarrJIT.h:
(JSC::Yarr::MatchingContextHolder::offsetOfStackLimit):
(JSC::Yarr::MatchingContextHolder::offsetOfPatternContextBuffer):
(JSC::Yarr::MatchingContextHolder::offsetOfPatternContextBufferSize):
(JSC::Yarr::YarrCodeBlock::execute):
* yarr/YarrPattern.cpp:
(JSC::Yarr::YarrPatternConstructor::YarrPatternConstructor):
(JSC::Yarr::YarrPatternConstructor::isSafeToRecurse):
(JSC::Yarr::YarrPattern::compile):
(JSC::Yarr::YarrPattern::YarrPattern):
(JSC::Yarr::YarrPatternConstructor::isSafeToRecurse const): Deleted.
* yarr/YarrPattern.h:

LayoutTests:

Updated test for improved stack overflow checking.

* js/script-tests/stack-overflow-regexp.js:
(shouldThrow.recursiveCall):
(shouldThrow):
(recursiveCall):
* js/stack-overflow-regexp-expected.txt:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@259092 268f45cc-cd09-0410-ab3c-d52691b4dbfc

16 files changed:
JSTests/ChangeLog
JSTests/stress/regexp-compile-oom.js [deleted file]
JSTests/stress/regexp-huge-oom.js [new file with mode: 0644]
LayoutTests/ChangeLog
LayoutTests/js/script-tests/stack-overflow-regexp.js
LayoutTests/js/stack-overflow-regexp-expected.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/runtime/RegExp.cpp
Source/JavaScriptCore/runtime/RegExp.h
Source/JavaScriptCore/runtime/RegExpInlines.h
Source/JavaScriptCore/yarr/Yarr.h
Source/JavaScriptCore/yarr/YarrInterpreter.cpp
Source/JavaScriptCore/yarr/YarrJIT.cpp
Source/JavaScriptCore/yarr/YarrJIT.h
Source/JavaScriptCore/yarr/YarrPattern.cpp
Source/JavaScriptCore/yarr/YarrPattern.h

index ec0f955..70415a5 100644 (file)
@@ -1,3 +1,21 @@
+2020-03-26  Michael Saboff  <msaboff@apple.com>
+
+        Refactor YARR Stack Overflow Checks
+        https://bugs.webkit.org/show_bug.cgi?id=209435
+        rdar://problem/58988252
+
+        Reviewed by Mark Lam.
+
+        Added a new test and removed a now obsolete test.
+
+        * stress/regexp-compile-oom.js: Removed because the test is no longer valid.
+        Previously when therer where different stack check mechanisims we failed different.
+        This test was based on the different failure modes.  With these changes, most of
+        the contain subtests no longer throw as this test expects.
+        * stress/regexp-huge-oom.js: Added.
+        (shouldBe):
+        (shouldThrow):
+
 2020-03-26  Keith Miller  <keith_miller@apple.com>
 
         TypedArrays should more gracefully handle OOM during slowDownAndWasteMemory
diff --git a/JSTests/stress/regexp-compile-oom.js b/JSTests/stress/regexp-compile-oom.js
deleted file mode 100644 (file)
index 1a08b8e..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-//@ skip if $hostOS != "darwin" or $architecture == "arm" or $architecture == "x86" or not $jitTests
-// Test that throw an OOM exception when compiling a pathological, but valid nested RegExp.
-
-var failures = [];
-
-class TestAndExpectedException
-{
-    constructor(func, exception)
-    {
-        this.func = func;
-        this.exception = exception;
-    }
-
-    runTest()
-    {
-        try {
-            this.func();
-            failures.push("Running " + this.func + ", expected OOM exception, but didn't get one");
-        } catch (e) {
-            let errStr = e.toString();
-            if (errStr != this.exception)
-                failures.push("Running " + this.func + ", expected: \"" + this.exception + "\" but got \"" + errStr + "\"");
-        }       
-    }
-}
-
-function recurseAndTest(depth, testList)
-{
-    // Probe stack depth
-    try {
-        let result = recurseAndTest(depth + 1, testList);
-        if (result == 0) {
-            // Call the test functions with a nearly full stack.
-            for (const test of testList)
-                test.runTest();
-
-            return 1;
-        } else if (result < 0)
-            return result + 1;
-        else
-            return result;
-    } catch (e) {
-        // Go up a several frames and then call the test functions
-        return -24;
-    }
-
-    return 1;
-}
-
-let deepRE = new RegExp("((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((x))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))");
-let deepGlobalRE = new RegExp(deepRE, "g");
-
-let matchLen = 401; // The number of parens plus 1 for the whole match.
-
-let regExpOOMError = "Error: Out of memory: Invalid regular expression: too many nested disjunctions";
-
-testList = [];
-
-// Test that all RegExp related APIs that compile RE's properly handle OOM.
-testList.push(new TestAndExpectedException(() => { deepRE.exec("x"); }, regExpOOMError));
-testList.push(new TestAndExpectedException(() => { deepRE.test("x"); }, regExpOOMError));
-testList.push(new TestAndExpectedException(() => { "x".match(deepRE); }, regExpOOMError));
-testList.push(new TestAndExpectedException(() => { "x".match(deepGlobalRE); }, regExpOOMError));
-testList.push(new TestAndExpectedException(() => { "x".replace(deepGlobalRE, ""); }, regExpOOMError));
-testList.push(new TestAndExpectedException(() => { "x".replace(deepGlobalRE, "X"); }, regExpOOMError));
-testList.push(new TestAndExpectedException(() => { "x".replace(deepGlobalRE, () => { return "X" }); }, regExpOOMError));
-testList.push(new TestAndExpectedException(() => { "x".search(deepRE); }, regExpOOMError));
-
-recurseAndTest(1, testList);
-
-if (failures.length) {
-    print("Got the following failures:");
-    for (const failure of failures)
-        print(failure);
-    throw "Got failures";
-}
-
-// Test that the RegExp works correctly with RegExp.exec() and RegExp.test() when there is sufficient stack space to compile it.
-let m = deepRE.exec("x");
-let matched = true;
-if (m.length != matchLen)
-    matched = false
-else {
-    for (i = 0; i < matchLen; i++) {
-        if (m[i] != "x")
-            matched = false;
-    }
-}
-
-if (!matched) {
-    let expectedMatch = [];
-    for (i = 0; i < matchLen; i++)
-        expectedMatch[i] = "x";
-
-    throw "Expected RegExp.exec(...) to be [" + expectedMatch + "] but got [" + m + "]";
-}
-
-if (!deepRE.test("x"))
-    throw "Expected RegExp.test(...) to be true, but was false";
diff --git a/JSTests/stress/regexp-huge-oom.js b/JSTests/stress/regexp-huge-oom.js
new file mode 100644 (file)
index 0000000..548f1e1
--- /dev/null
@@ -0,0 +1,33 @@
+//@ skip if $memoryLimited
+// Test that throw an OOM exception when compiling / executing a pathologically large RegExp's
+
+function shouldBe(actual, expected)
+{
+    if (actual !== expected)
+        throw new Error("Bad value: , expected \"" + expected + "\", but got \"" + actual + "\"");
+}
+
+function shouldThrow(run, errorType)
+{
+    let actual;
+    var hadError = false;
+
+    try {
+        actual = run();
+    } catch (e) {
+        hadError = true;
+        actual = e;
+    }
+
+    if (!hadError)
+        throw new Error("Expected " + run + "() to throw " + errorType.name + ", but did not throw.");
+    if (!(actual instanceof errorType))
+        throw new Error("Expeced " + run + "() to throw " + errorType.name + " , but threw '" + actual + "'");
+}
+
+// This should throw during pattern compilation.
+shouldThrow(() => RegExp('a?'.repeat(2**19) + 'b').exec('x'), SyntaxError);
+
+// This test should fail execution in the JIT'ed code and then fall back to the interpreter ans succeed.
+shouldBe(RegExp('a?'.repeat(2**19)).exec('x')[0], "");
+
index 8ceab8d..487ae3a 100644 (file)
@@ -1,3 +1,19 @@
+2020-03-26  Michael Saboff  <msaboff@apple.com>
+
+        Refactor YARR Stack Overflow Checks
+        https://bugs.webkit.org/show_bug.cgi?id=209435
+        rdar://problem/58988252
+
+        Reviewed by Mark Lam.
+
+       Updated test for improved stack overflow checking.
+
+        * js/script-tests/stack-overflow-regexp.js:
+        (shouldThrow.recursiveCall):
+        (shouldThrow):
+        (recursiveCall):
+        * js/stack-overflow-regexp-expected.txt:
+
 2020-03-26  Jason Lawrence  <lawrence.j@apple.com>
 
         [ Mac wk2 Release ] tiled-drawing/scrolling/fixed/four-bars-zoomed.html is flaky failing.
index 30c39f1..90aeca7 100644 (file)
@@ -1,15 +1,16 @@
 // https://bugs.webkit.org/show_bug.cgi?id=190755
 //@ skip if $architecture == "arm" and $hostOS == "linux"
+//  &&&&
 description('Test that we do not overflow the stack while handling regular expressions');
 
 // Base case.
-shouldThrow('new RegExp(Array(50000).join("(") + "a" + Array(50000).join(")"))', '"Error: Out of memory: Invalid regular expression: too many nested disjunctions"');
+shouldThrow('new RegExp(Array(500000).join("(") + "a" + Array(500000).join(")"))', '"Error: Out of memory: Invalid regular expression: too many nested disjunctions"');
 
 { // Verify that a deep JS stack does not help avoiding the error.
     function recursiveCall(depth) {
         if (!(depth % 10)) {
             debug("Creating RegExp at depth " + depth);
-            shouldThrow('new RegExp(Array(50000).join("(") + "a" + Array(50000).join(")"))', '"Error: Out of memory: Invalid regular expression: too many nested disjunctions"');
+            shouldThrow('new RegExp(Array(500000).join("(") + "a" + Array(500000).join(")"))', '"Error: Out of memory: Invalid regular expression: too many nested disjunctions"');
         }
         if (depth < 100) {
             recursiveCall(depth + 1);
@@ -20,12 +21,12 @@ shouldThrow('new RegExp(Array(50000).join("(") + "a" + Array(50000).join(")"))',
 
 { // Have the deepest nested subpattern surrounded by other expressions.
     var expression = "";
-    for (let i = 0; i < 50000; ++i) {
+    for (let i = 0; i < 500000; ++i) {
         expression += "((a)(";
     }
     expression += "b";
-    for (let i = 0; i < 50000; ++i) {
+    for (let i = 0; i < 500000; ++i) {
         expression += ")(c))";
     }
-    shouldThrow('new RegExp(expression)', '"Error: Out of memory: Invalid regular expression: too many nested disjunctions"');
+    shouldThrow('new RegExp(expression)', '"SyntaxError: Invalid regular expression: regular expression too large"');
 }
index c7b5b7f..6590001 100644 (file)
@@ -3,30 +3,30 @@ Test that we do not overflow the stack while handling regular expressions
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
-PASS new RegExp(Array(50000).join("(") + "a" + Array(50000).join(")")) threw exception Error: Out of memory: Invalid regular expression: too many nested disjunctions.
+PASS new RegExp(Array(500000).join("(") + "a" + Array(500000).join(")")) threw exception Error: Out of memory: Invalid regular expression: too many nested disjunctions.
 Creating RegExp at depth 0
-PASS new RegExp(Array(50000).join("(") + "a" + Array(50000).join(")")) threw exception Error: Out of memory: Invalid regular expression: too many nested disjunctions.
+PASS new RegExp(Array(500000).join("(") + "a" + Array(500000).join(")")) threw exception Error: Out of memory: Invalid regular expression: too many nested disjunctions.
 Creating RegExp at depth 10
-PASS new RegExp(Array(50000).join("(") + "a" + Array(50000).join(")")) threw exception Error: Out of memory: Invalid regular expression: too many nested disjunctions.
+PASS new RegExp(Array(500000).join("(") + "a" + Array(500000).join(")")) threw exception Error: Out of memory: Invalid regular expression: too many nested disjunctions.
 Creating RegExp at depth 20
-PASS new RegExp(Array(50000).join("(") + "a" + Array(50000).join(")")) threw exception Error: Out of memory: Invalid regular expression: too many nested disjunctions.
+PASS new RegExp(Array(500000).join("(") + "a" + Array(500000).join(")")) threw exception Error: Out of memory: Invalid regular expression: too many nested disjunctions.
 Creating RegExp at depth 30
-PASS new RegExp(Array(50000).join("(") + "a" + Array(50000).join(")")) threw exception Error: Out of memory: Invalid regular expression: too many nested disjunctions.
+PASS new RegExp(Array(500000).join("(") + "a" + Array(500000).join(")")) threw exception Error: Out of memory: Invalid regular expression: too many nested disjunctions.
 Creating RegExp at depth 40
-PASS new RegExp(Array(50000).join("(") + "a" + Array(50000).join(")")) threw exception Error: Out of memory: Invalid regular expression: too many nested disjunctions.
+PASS new RegExp(Array(500000).join("(") + "a" + Array(500000).join(")")) threw exception Error: Out of memory: Invalid regular expression: too many nested disjunctions.
 Creating RegExp at depth 50
-PASS new RegExp(Array(50000).join("(") + "a" + Array(50000).join(")")) threw exception Error: Out of memory: Invalid regular expression: too many nested disjunctions.
+PASS new RegExp(Array(500000).join("(") + "a" + Array(500000).join(")")) threw exception Error: Out of memory: Invalid regular expression: too many nested disjunctions.
 Creating RegExp at depth 60
-PASS new RegExp(Array(50000).join("(") + "a" + Array(50000).join(")")) threw exception Error: Out of memory: Invalid regular expression: too many nested disjunctions.
+PASS new RegExp(Array(500000).join("(") + "a" + Array(500000).join(")")) threw exception Error: Out of memory: Invalid regular expression: too many nested disjunctions.
 Creating RegExp at depth 70
-PASS new RegExp(Array(50000).join("(") + "a" + Array(50000).join(")")) threw exception Error: Out of memory: Invalid regular expression: too many nested disjunctions.
+PASS new RegExp(Array(500000).join("(") + "a" + Array(500000).join(")")) threw exception Error: Out of memory: Invalid regular expression: too many nested disjunctions.
 Creating RegExp at depth 80
-PASS new RegExp(Array(50000).join("(") + "a" + Array(50000).join(")")) threw exception Error: Out of memory: Invalid regular expression: too many nested disjunctions.
+PASS new RegExp(Array(500000).join("(") + "a" + Array(500000).join(")")) threw exception Error: Out of memory: Invalid regular expression: too many nested disjunctions.
 Creating RegExp at depth 90
-PASS new RegExp(Array(50000).join("(") + "a" + Array(50000).join(")")) threw exception Error: Out of memory: Invalid regular expression: too many nested disjunctions.
+PASS new RegExp(Array(500000).join("(") + "a" + Array(500000).join(")")) threw exception Error: Out of memory: Invalid regular expression: too many nested disjunctions.
 Creating RegExp at depth 100
-PASS new RegExp(Array(50000).join("(") + "a" + Array(50000).join(")")) threw exception Error: Out of memory: Invalid regular expression: too many nested disjunctions.
-PASS new RegExp(expression) threw exception Error: Out of memory: Invalid regular expression: too many nested disjunctions.
+PASS new RegExp(Array(500000).join("(") + "a" + Array(500000).join(")")) threw exception Error: Out of memory: Invalid regular expression: too many nested disjunctions.
+PASS new RegExp(expression) threw exception SyntaxError: Invalid regular expression: regular expression too large.
 PASS successfullyParsed is true
 
 TEST COMPLETE
index df2e611..5af22eb 100644 (file)
@@ -1,3 +1,59 @@
+2020-03-26  Michael Saboff  <msaboff@apple.com>
+
+        Refactor YARR Stack Overflow Checks
+        https://bugs.webkit.org/show_bug.cgi?id=209435
+        rdar://problem/58988252
+
+        Reviewed by Mark Lam.
+
+        Refactored stack checks in YARR code including adding a stack check to the YARR JIT'ed code.
+        The C++ code including the parser, byte code compiler and interpreter now all use StackCheck.
+        The JIT'ed code needs a stack limit passed via a parameter since the JIT'ed code can be 
+        called from the compiler thread when compiling DFG / FTL code.
+
+        Instead of adding a new parameter, consolidated the two pattern context buffer values, buffer
+        pointer and size, with the new stack limit into a new MatchingContextHolder, an RAII object.
+        The MatchingContextHolder constructor uses either the VM stack limit or the current thread's
+        stack limit depending on how it is called.
+
+        * runtime/RegExp.cpp:
+        (JSC::RegExp::finishCreation):
+        (JSC::RegExp::byteCodeCompileIfNecessary):
+        (JSC::RegExp::compile):
+        (JSC::RegExp::matchConcurrently):
+        (JSC::RegExp::compileMatchOnly):
+        * runtime/RegExp.h:
+        * runtime/RegExpInlines.h:
+        (JSC::RegExp::matchInline):
+        (JSC::PatternContextBufferHolder::PatternContextBufferHolder): Deleted.
+        (JSC::PatternContextBufferHolder::~PatternContextBufferHolder): Deleted.
+        (JSC::PatternContextBufferHolder::buffer): Deleted.
+        (JSC::PatternContextBufferHolder::size): Deleted.
+        (): Deleted.
+        * yarr/Yarr.h:
+        * yarr/YarrInterpreter.cpp:
+        (JSC::Yarr::Interpreter::matchDisjunction):
+        (JSC::Yarr::Interpreter::isSafeToRecurse):
+        * yarr/YarrJIT.cpp:
+        (JSC::Yarr::MatchingContextHolder::MatchingContextHolder):
+        (JSC::Yarr::MatchingContextHolder::~MatchingContextHolder):
+        (JSC::Yarr::YarrGenerator::initParenContextFreeList):
+        (JSC::Yarr::YarrGenerator::alignCallFrameSizeInBytes):
+        (JSC::Yarr::YarrGenerator::compile):
+        (JSC::Yarr::YarrGenerator::initCallFrame): Deleted.
+        * yarr/YarrJIT.h:
+        (JSC::Yarr::MatchingContextHolder::offsetOfStackLimit):
+        (JSC::Yarr::MatchingContextHolder::offsetOfPatternContextBuffer):
+        (JSC::Yarr::MatchingContextHolder::offsetOfPatternContextBufferSize):
+        (JSC::Yarr::YarrCodeBlock::execute):
+        * yarr/YarrPattern.cpp:
+        (JSC::Yarr::YarrPatternConstructor::YarrPatternConstructor):
+        (JSC::Yarr::YarrPatternConstructor::isSafeToRecurse):
+        (JSC::Yarr::YarrPattern::compile):
+        (JSC::Yarr::YarrPattern::YarrPattern):
+        (JSC::Yarr::YarrPatternConstructor::isSafeToRecurse const): Deleted.
+        * yarr/YarrPattern.h:
+
 2020-03-26  Keith Miller  <keith_miller@apple.com>
 
         TypedArrays should more gracefully handle OOM during slowDownAndWasteMemory
index 70e593d..b41d4d5 100644 (file)
@@ -170,7 +170,7 @@ RegExp::RegExp(VM& vm, const String& patternString, OptionSet<Yarr::Flags> flags
 void RegExp::finishCreation(VM& vm)
 {
     Base::finishCreation(vm);
-    Yarr::YarrPattern pattern(m_patternString, m_flags, m_constructionErrorCode, vm.stackLimit());
+    Yarr::YarrPattern pattern(m_patternString, m_flags, m_constructionErrorCode);
     if (!isValid()) {
         m_state = ParseError;
         return;
@@ -227,13 +227,10 @@ void RegExp::byteCodeCompileIfNecessary(VM* vm)
     if (m_regExpBytecode)
         return;
 
-    Yarr::YarrPattern pattern(m_patternString, m_flags, m_constructionErrorCode, vm->stackLimit());
+    Yarr::YarrPattern pattern(m_patternString, m_flags, m_constructionErrorCode);
     if (hasError(m_constructionErrorCode)) {
-        RELEASE_ASSERT_NOT_REACHED();
-#if COMPILER_QUIRK(CONSIDERS_UNREACHABLE_CODE)
         m_state = ParseError;
         return;
-#endif
     }
     ASSERT(m_numSubpatterns == pattern.m_numSubpatterns);
 
@@ -248,7 +245,7 @@ void RegExp::compile(VM* vm, Yarr::YarrCharSize charSize)
 {
     auto locker = holdLock(cellLock());
     
-    Yarr::YarrPattern pattern(m_patternString, m_flags, m_constructionErrorCode, vm->stackLimit());
+    Yarr::YarrPattern pattern(m_patternString, m_flags, m_constructionErrorCode);
     if (hasError(m_constructionErrorCode)) {
         m_state = ParseError;
         return;
@@ -302,7 +299,9 @@ bool RegExp::matchConcurrently(
     if (!hasCodeFor(s.is8Bit() ? Yarr::Char8 : Yarr::Char16))
         return false;
 
-    position = match(vm, s, startOffset, ovector);
+    position = matchInline<Vector<int>&, Yarr::MatchFrom::CompilerThread>(vm, s, startOffset, ovector);
+    if (m_state == ParseError)
+        return false;
     return true;
 }
 
@@ -310,7 +309,7 @@ void RegExp::compileMatchOnly(VM* vm, Yarr::YarrCharSize charSize)
 {
     auto locker = holdLock(cellLock());
     
-    Yarr::YarrPattern pattern(m_patternString, m_flags, m_constructionErrorCode, vm->stackLimit());
+    Yarr::YarrPattern pattern(m_patternString, m_flags, m_constructionErrorCode);
     if (hasError(m_constructionErrorCode)) {
         m_state = ParseError;
         return;
@@ -363,7 +362,7 @@ bool RegExp::matchConcurrently(VM& vm, const String& s, unsigned startOffset, Ma
     if (!hasMatchOnlyCodeFor(s.is8Bit() ? Yarr::Char8 : Yarr::Char16))
         return false;
 
-    result = match(vm, s, startOffset);
+    result = matchInline<Yarr::MatchFrom::CompilerThread>(vm, s, startOffset);
     return true;
 }
 
index 7d026bf..fd418ec 100644 (file)
@@ -86,8 +86,9 @@ public:
     bool matchConcurrently(VM&, const String&, unsigned startOffset, MatchResult&);
 
     // Call these versions of the match functions if you're desperate for performance.
-    template<typename VectorType>
+    template<typename VectorType, Yarr::MatchFrom thread = Yarr::MatchFrom::VMThread>
     int matchInline(VM&, const String&, unsigned startOffset, VectorType& ovector);
+    template<Yarr::MatchFrom thread = Yarr::MatchFrom::VMThread>
     MatchResult matchInline(VM&, const String&, unsigned startOffset);
     
     unsigned numSubpatterns() const { return m_numSubpatterns; }
index f7d9190..8aee85b 100644 (file)
@@ -86,38 +86,6 @@ ALWAYS_INLINE bool RegExp::hasCodeFor(Yarr::YarrCharSize charSize)
     return false;
 }
 
-class PatternContextBufferHolder {
-    WTF_FORBID_HEAP_ALLOCATION;
-public:
-    PatternContextBufferHolder(VM& vm, bool needBuffer)
-        : m_vm(vm)
-    {
-#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
-        if (needBuffer)
-            m_buffer = m_vm.acquireRegExpPatternContexBuffer();
-#else
-        UNUSED_PARAM(needBuffer);
-#endif
-    }
-
-    ~PatternContextBufferHolder()
-    {
-#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
-        if (buffer())
-            m_vm.releaseRegExpPatternContexBuffer();
-#else
-        UNUSED_PARAM(m_vm);
-#endif
-    }
-
-    void* buffer() { return m_buffer; }
-    unsigned size() { return buffer() ? VM::patternContextBufferSize : 0; }
-
-private:
-    VM& m_vm;
-    void* m_buffer { nullptr };
-};
-
 ALWAYS_INLINE void RegExp::compileIfNecessary(VM& vm, Yarr::YarrCharSize charSize)
 {
     if (hasCodeFor(charSize))
@@ -129,7 +97,7 @@ ALWAYS_INLINE void RegExp::compileIfNecessary(VM& vm, Yarr::YarrCharSize charSiz
     compile(&vm, charSize);
 }
 
-template<typename VectorType>
+template<typename VectorType, Yarr::MatchFrom matchFrom>
 ALWAYS_INLINE int RegExp::matchInline(VM& vm, const String& s, unsigned startOffset, VectorType& ovector)
 {
 #if ENABLE(REGEXP_TRACING)
@@ -162,19 +130,22 @@ ALWAYS_INLINE int RegExp::matchInline(VM& vm, const String& s, unsigned startOff
     if (m_state == JITCode) {
         {
             ASSERT(m_regExpJITCode);
-            PatternContextBufferHolder patternContextBufferHolder(vm, m_regExpJITCode->usesPatternContextBuffer());
+            Yarr::MatchingContextHolder regExpContext(vm, m_regExpJITCode.get(), matchFrom);
 
             if (s.is8Bit())
-                result = m_regExpJITCode->execute(s.characters8(), startOffset, s.length(), offsetVector, patternContextBufferHolder.buffer(), patternContextBufferHolder.size()).start;
+                result = m_regExpJITCode->execute(s.characters8(), startOffset, s.length(), offsetVector, regExpContext).start;
             else
-                result = m_regExpJITCode->execute(s.characters16(), startOffset, s.length(), offsetVector, patternContextBufferHolder.buffer(), patternContextBufferHolder.size()).start;
+                result = m_regExpJITCode->execute(s.characters16(), startOffset, s.length(), offsetVector, regExpContext).start;
         }
 
         if (result == Yarr::JSRegExpJITCodeFailure) {
             // JIT'ed code couldn't handle expression, so punt back to the interpreter.
             byteCodeCompileIfNecessary(&vm);
-            if (m_state == ParseError)
+            if (m_state == ParseError) {
+                if (matchFrom == Yarr::MatchFrom::CompilerThread)
+                    return -1;
                 return throwError();
+            }
             result = Yarr::interpret(m_regExpBytecode.get(), s, startOffset, reinterpret_cast<unsigned*>(offsetVector));
         }
 
@@ -260,6 +231,7 @@ ALWAYS_INLINE void RegExp::compileIfNecessaryMatchOnly(VM& vm, Yarr::YarrCharSiz
     compileMatchOnly(&vm, charSize);
 }
 
+template<Yarr::MatchFrom matchFrom>
 ALWAYS_INLINE MatchResult RegExp::matchInline(VM& vm, const String& s, unsigned startOffset)
 {
 #if ENABLE(REGEXP_TRACING)
@@ -289,11 +261,12 @@ ALWAYS_INLINE MatchResult RegExp::matchInline(VM& vm, const String& s, unsigned
     if (m_state == JITCode) {
         {
             ASSERT(m_regExpJITCode);
-            PatternContextBufferHolder patternContextBufferHolder(vm, m_regExpJITCode->usesPatternContextBuffer());
+            Yarr::MatchingContextHolder regExpContext(vm, m_regExpJITCode.get(), matchFrom);
+
             if (s.is8Bit())
-                result = m_regExpJITCode->execute(s.characters8(), startOffset, s.length(), patternContextBufferHolder.buffer(), patternContextBufferHolder.size());
+                result = m_regExpJITCode->execute(s.characters8(), startOffset, s.length(), regExpContext);
             else
-                result = m_regExpJITCode->execute(s.characters16(), startOffset, s.length(), patternContextBufferHolder.buffer(), patternContextBufferHolder.size());
+                result = m_regExpJITCode->execute(s.characters16(), startOffset, s.length(), regExpContext);
         }
 
 #if ENABLE(REGEXP_TRACING)
index 942b24b..163986a 100644 (file)
@@ -49,6 +49,8 @@ static constexpr unsigned offsetNoMatch = std::numeric_limits<unsigned>::max();
 // avoid spending exponential time on complex regular expressions.
 static constexpr unsigned matchLimit = 1000000;
 
+enum MatchFrom { VMThread, CompilerThread };
+
 enum JSRegExpResult {
     JSRegExpMatch = 1,
     JSRegExpNoMatch = 0,
index 0f33409..994a623 100644 (file)
@@ -1272,6 +1272,9 @@ public:
 #define currentTerm() (disjunction->terms[context->term])
     JSRegExpResult matchDisjunction(ByteDisjunction* disjunction, DisjunctionContext* context, bool btrack = false)
     {
+        if (UNLIKELY(!isSafeToRecurse()))
+            return JSRegExpErrorNoMemory;
+
         if (!--remainingMatchCount)
             return JSRegExpErrorHitLimit;
 
@@ -1667,10 +1670,13 @@ public:
     }
 
 private:
+    inline bool isSafeToRecurse() { return m_stackCheck.isSafeToRecurse(); }
+
     BytecodePattern* pattern;
     bool unicode;
     unsigned* output;
     InputStream input;
+    StackCheck m_stackCheck;
     WTF::BumpPointerPool* allocatorPool { nullptr };
     unsigned startOffset;
     unsigned remainingMatchCount;
index 629bbe4..8b2a253 100644 (file)
 #include "config.h"
 #include "YarrJIT.h"
 
-#include <wtf/ASCIICType.h>
 #include "LinkBuffer.h"
 #include "Options.h"
 #include "VM.h"
 #include "Yarr.h"
 #include "YarrCanonicalize.h"
 #include "YarrDisassembler.h"
+#include <wtf/ASCIICType.h>
+#include <wtf/Threading.h>
+
 
 #if ENABLE(YARR_JIT)
 
 namespace JSC { namespace Yarr {
 
+MatchingContextHolder::MatchingContextHolder(VM& vm, YarrCodeBlock* yarrCodeBlock, MatchFrom matchFrom)
+    : m_vm(vm)
+{
+    if (matchFrom == MatchFrom::VMThread)
+        m_stackLimit = vm.softStackLimit();
+    else {
+        StackBounds stack = Thread::current().stack();
+        m_stackLimit = stack.recursionLimit(Options::reservedZoneSize());
+    }
+
+#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
+    if (yarrCodeBlock->usesPatternContextBuffer()) {
+        m_patternContextBuffer = m_vm.acquireRegExpPatternContexBuffer();
+        m_patternContextBufferSize = VM::patternContextBufferSize;
+    }
+#else
+    UNUSED_PARAM(yarrCodeBlock);
+#endif
+}
+
+MatchingContextHolder::~MatchingContextHolder()
+{
+#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
+    if (m_patternContextBuffer)
+        m_vm.releaseRegExpPatternContexBuffer();
+#endif
+}
+
 template<YarrJITCompileMode compileMode>
 class YarrGenerator : public YarrJITInfo, private MacroAssembler {
 
@@ -60,7 +90,8 @@ class YarrGenerator : public YarrJITInfo, private MacroAssembler {
     static const RegisterID index = ARM64Registers::x1;
     static const RegisterID length = ARM64Registers::x2;
     static const RegisterID output = ARM64Registers::x3;
-    static const RegisterID freelistRegister = ARM64Registers::x4;
+    static const RegisterID matchingContext = ARM64Registers::x4;
+    static const RegisterID freelistRegister = ARM64Registers::x4; // Loaded from the MatchingContextHolder in the prologue.
     static const RegisterID freelistSizeRegister = ARM64Registers::x5; // Only used during initialization.
 
     // Scratch registers
@@ -101,7 +132,8 @@ class YarrGenerator : public YarrJITInfo, private MacroAssembler {
     static const RegisterID index = X86Registers::esi;
     static const RegisterID length = X86Registers::edx;
     static const RegisterID output = X86Registers::ecx;
-    static const RegisterID freelistRegister = X86Registers::r8;
+    static const RegisterID matchingContext = X86Registers::r8;
+    static const RegisterID freelistRegister = X86Registers::r8; // Loaded from the MatchingContextHolder in the prologue.
     static const RegisterID freelistSizeRegister = X86Registers::r9; // Only used during initialization.
 #else
     // If the return value doesn't fit in 64bits, its destination is pointed by rcx and the parameters are shifted.
@@ -220,6 +252,9 @@ class YarrGenerator : public YarrJITInfo, private MacroAssembler {
             return;
         }
 
+        load32(Address(matchingContext, MatchingContextHolder::offsetOfPatternContextBufferSize()), freelistSizeRegister);
+        // Note that matchingContext and freelistRegister are likely the same register.
+        loadPtr(Address(matchingContext, MatchingContextHolder::offsetOfPatternContextBuffer()), freelistRegister);
         Jump emptyFreeList = branchTestPtr(Zero, freelistRegister);
         move(freelistRegister, parenContextPointer);
         addPtr(TrustedImm32(parenContextSize), freelistRegister, nextParenContextPointer);
@@ -635,12 +670,6 @@ class YarrGenerator : public YarrJITInfo, private MacroAssembler {
         callFrameSize = (callFrameSize + 0x3f) & ~0x3f;
         return callFrameSize;
     }
-    void initCallFrame()
-    {
-        unsigned callFrameSizeInBytes = alignCallFrameSizeInBytes(m_pattern.m_body->m_callFrameSize);
-        if (callFrameSizeInBytes)
-            subPtr(Imm32(callFrameSizeInBytes), stackPointerRegister);
-    }
     void removeCallFrame()
     {
         unsigned callFrameSizeInBytes = alignCallFrameSizeInBytes(m_pattern.m_body->m_callFrameSize);
@@ -3851,6 +3880,30 @@ public:
         generateFailReturn();
         hasInput.link(this);
 
+        unsigned callFrameSizeInBytes = alignCallFrameSizeInBytes(m_pattern.m_body->m_callFrameSize);
+        if (callFrameSizeInBytes) {
+            // Check stack size
+            addPtr(TrustedImm32(-callFrameSizeInBytes), stackPointerRegister, regT0);
+#if CPU(X86_64) && OS(WINDOWS)
+            // matchingContext is the 5th argument, it is found on the stack.
+            RegisterID matchingContext = regT1;
+            loadPtr(Address(X86Registers::ebp, 7 * sizeof(void*)), matchingContext);
+#elif CPU(ARM_THUMB2) || CPU(MIPS)
+            // matchingContext is the 5th argument, it is found on the stack.
+            RegisterID matchingContext = regT1;
+            loadPtr(Address(stackPointerRegister, 4 * sizeof(void*)), matchingContext);
+#endif
+            Jump stackOk = branchPtr(BelowOrEqual, Address(matchingContext, MatchingContextHolder::offsetOfStackLimit()), regT0);
+
+            // Exceeded stack limit, punt to the interpreter.
+            move(TrustedImmPtr((void*)static_cast<size_t>(JSRegExpJITCodeFailure)), returnRegister);
+            move(TrustedImm32(0), returnRegister2);
+            generateReturn();
+
+            stackOk.link(this);
+            move(regT0, stackPointerRegister);
+        }
+
 #ifdef JIT_UNICODE_EXPRESSIONS
         if (m_decodeSurrogatePairs)
             getEffectiveAddress(BaseIndex(input, length, TimesTwo), endOfStringAddress);
@@ -3869,8 +3922,6 @@ public:
         if (!m_pattern.m_body->m_hasFixedSize)
             setMatchStart(index);
 
-        initCallFrame();
-
 #if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
         if (m_containsNestedSubpatterns) {
             initParenContextFreeList();
index 5a98f1a..096d40e 100644 (file)
@@ -29,6 +29,7 @@
 
 #include "MacroAssemblerCodeRef.h"
 #include "MatchResult.h"
+#include "VM.h"
 #include "Yarr.h"
 #include "YarrPattern.h"
 
@@ -41,6 +42,8 @@ class ExecutablePool;
 
 namespace Yarr {
 
+class YarrCodeBlock;
+
 enum class JITFailureReason : uint8_t {
     DecodeSurrogatePair,
     BackReference,
@@ -52,15 +55,35 @@ enum class JITFailureReason : uint8_t {
     ExecutableMemoryAllocationFailure,
 };
 
+class MatchingContextHolder {
+    WTF_FORBID_HEAP_ALLOCATION;
+public:
+    MatchingContextHolder(VM&, YarrCodeBlock*, MatchFrom matchFrom = MatchFrom::VMThread);
+    ~MatchingContextHolder();
+
+    static ptrdiff_t offsetOfStackLimit() { return OBJECT_OFFSETOF(MatchingContextHolder, m_stackLimit); }
+#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
+    static ptrdiff_t offsetOfPatternContextBuffer() { return OBJECT_OFFSETOF(MatchingContextHolder, m_patternContextBuffer); }
+    static ptrdiff_t offsetOfPatternContextBufferSize() { return OBJECT_OFFSETOF(MatchingContextHolder, m_patternContextBufferSize); }
+#endif
+
+private:
+    VM& m_vm;
+    void* m_stackLimit;
+#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
+    void* m_patternContextBuffer { nullptr };
+    unsigned m_patternContextBufferSize { 0 };
+#endif
+};
+
 class YarrCodeBlock {
     WTF_MAKE_FAST_ALLOCATED;
     WTF_MAKE_NONCOPYABLE(YarrCodeBlock);
 
-    // Technically freeParenContext and parenContextSize are only used if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS) is set. Fortunately, all the calling conventions we support have caller save argument registers.
-    using YarrJITCode8 = EncodedMatchResult (*)(const LChar* input, unsigned start, unsigned length, int* output, void* freeParenContext, unsigned parenContextSize) YARR_CALL;
-    using YarrJITCode16 = EncodedMatchResult (*)(const UChar* input, unsigned start, unsigned length, int* output, void* freeParenContext, unsigned parenContextSize) YARR_CALL;
-    using YarrJITCodeMatchOnly8 = EncodedMatchResult (*)(const LChar* input, unsigned start, unsigned length, void*, void* freeParenContext, unsigned parenContextSize) YARR_CALL;
-    using YarrJITCodeMatchOnly16 = EncodedMatchResult (*)(const UChar* input, unsigned start, unsigned length, void*, void* freeParenContext, unsigned parenContextSize) YARR_CALL;
+    using YarrJITCode8 = EncodedMatchResult (*)(const LChar* input, unsigned start, unsigned length, int* output, MatchingContextHolder& matchingContext) YARR_CALL;
+    using YarrJITCode16 = EncodedMatchResult (*)(const UChar* input, unsigned start, unsigned length, int* output, MatchingContextHolder& matchingContext) YARR_CALL;
+    using YarrJITCodeMatchOnly8 = EncodedMatchResult (*)(const LChar* input, unsigned start, unsigned length, void*, MatchingContextHolder& matchingContext) YARR_CALL;
+    using YarrJITCodeMatchOnly16 = EncodedMatchResult (*)(const UChar* input, unsigned start, unsigned length, void*, MatchingContextHolder& matchingContext) YARR_CALL;
 
 public:
     YarrCodeBlock() = default;
@@ -83,28 +106,28 @@ public:
     void setUsesPatternContextBuffer() { m_usesPatternContextBuffer = true; }
 #endif
 
-    MatchResult execute(const LChar* input, unsigned start, unsigned length, int* output, void* freeParenContext, unsigned parenContextSize)
+    MatchResult execute(const LChar* input, unsigned start, unsigned length, int* output, MatchingContextHolder& matchingContext)
     {
         ASSERT(has8BitCode());
-        return MatchResult(untagCFunctionPtr<YarrJITCode8, Yarr8BitPtrTag>(m_ref8.code().executableAddress())(input, start, length, output, freeParenContext, parenContextSize));
+        return MatchResult(untagCFunctionPtr<YarrJITCode8, Yarr8BitPtrTag>(m_ref8.code().executableAddress())(input, start, length, output, matchingContext));
     }
 
-    MatchResult execute(const UChar* input, unsigned start, unsigned length, int* output, void* freeParenContext, unsigned parenContextSize)
+    MatchResult execute(const UChar* input, unsigned start, unsigned length, int* output, MatchingContextHolder& matchingContext)
     {
         ASSERT(has16BitCode());
-        return MatchResult(untagCFunctionPtr<YarrJITCode16, Yarr16BitPtrTag>(m_ref16.code().executableAddress())(input, start, length, output, freeParenContext, parenContextSize));
+        return MatchResult(untagCFunctionPtr<YarrJITCode16, Yarr16BitPtrTag>(m_ref16.code().executableAddress())(input, start, length, output, matchingContext));
     }
 
-    MatchResult execute(const LChar* input, unsigned start, unsigned length, void* freeParenContext, unsigned parenContextSize)
+    MatchResult execute(const LChar* input, unsigned start, unsigned length, MatchingContextHolder& matchingContext)
     {
         ASSERT(has8BitCodeMatchOnly());
-        return MatchResult(untagCFunctionPtr<YarrJITCodeMatchOnly8, YarrMatchOnly8BitPtrTag>(m_matchOnly8.code().executableAddress())(input, start, length, 0, freeParenContext, parenContextSize));
+        return MatchResult(untagCFunctionPtr<YarrJITCodeMatchOnly8, YarrMatchOnly8BitPtrTag>(m_matchOnly8.code().executableAddress())(input, start, length, 0, matchingContext));
     }
 
-    MatchResult execute(const UChar* input, unsigned start, unsigned length, void* freeParenContext, unsigned parenContextSize)
+    MatchResult execute(const UChar* input, unsigned start, unsigned length, MatchingContextHolder& matchingContext)
     {
         ASSERT(has16BitCodeMatchOnly());
-        return MatchResult(untagCFunctionPtr<YarrJITCodeMatchOnly16, YarrMatchOnly16BitPtrTag>(m_matchOnly16.code().executableAddress())(input, start, length, 0, freeParenContext, parenContextSize));
+        return MatchResult(untagCFunctionPtr<YarrJITCodeMatchOnly16, YarrMatchOnly16BitPtrTag>(m_matchOnly16.code().executableAddress())(input, start, length, 0, matchingContext));
     }
 
 #if ENABLE(REGEXP_TRACING)
index 97fb868..d0ac398 100644 (file)
@@ -33,7 +33,7 @@
 #include "YarrParser.h"
 #include <wtf/DataLog.h>
 #include <wtf/Optional.h>
-#include <wtf/StackPointer.h>
+#include <wtf/StackCheck.h>
 #include <wtf/Threading.h>
 #include <wtf/Vector.h>
 
@@ -436,10 +436,9 @@ private:
 
 class YarrPatternConstructor {
 public:
-    YarrPatternConstructor(YarrPattern& pattern, void* stackLimit)
+    YarrPatternConstructor(YarrPattern& pattern)
         : m_pattern(pattern)
         , m_characterClassConstructor(pattern.ignoreCase(), pattern.unicode() ? CanonicalMode::Unicode : CanonicalMode::UCS2)
-        , m_stackLimit(stackLimit)
     {
         auto body = makeUnique<PatternDisjunction>();
         m_pattern.m_body = body.get();
@@ -1101,27 +1100,20 @@ public:
     ErrorCode error() { return m_error; }
 
 private:
-    bool isSafeToRecurse() const
-    {
-        if (!m_stackLimit)
-            return true;
-        int8_t* curr = reinterpret_cast<int8_t*>(currentStackPointer());
-        int8_t* limit = reinterpret_cast<int8_t*>(m_stackLimit);
-        return curr >= limit;
-    }
+    inline bool isSafeToRecurse() { return m_stackCheck.isSafeToRecurse(); }
 
     YarrPattern& m_pattern;
     PatternAlternative* m_alternative;
     CharacterClassConstructor m_characterClassConstructor;
-    void* m_stackLimit;
+    StackCheck m_stackCheck;
     ErrorCode m_error { ErrorCode::NoError };
     bool m_invertCharacterClass;
     bool m_invertParentheticalAssertion { false };
 };
 
-ErrorCode YarrPattern::compile(const String& patternString, void* stackLimit)
+ErrorCode YarrPattern::compile(const String& patternString)
 {
-    YarrPatternConstructor constructor(*this, stackLimit);
+    YarrPatternConstructor constructor(*this);
 
     {
         ErrorCode error = parse(constructor, patternString, unicode());
@@ -1148,7 +1140,7 @@ ErrorCode YarrPattern::compile(const String& patternString, void* stackLimit)
     return ErrorCode::NoError;
 }
 
-YarrPattern::YarrPattern(const String& pattern, OptionSet<Flags> flags, ErrorCode& error, void* stackLimit)
+YarrPattern::YarrPattern(const String& pattern, OptionSet<Flags> flags, ErrorCode& error)
     : m_containsBackreferences(false)
     , m_containsBOL(false)
     , m_containsUnsignedLengthPattern(false)
@@ -1157,7 +1149,7 @@ YarrPattern::YarrPattern(const String& pattern, OptionSet<Flags> flags, ErrorCod
     , m_flags(flags)
 {
     ASSERT(m_flags != Flags::DeletedValue);
-    error = compile(pattern, stackLimit);
+    error = compile(pattern);
 }
 
 void indentForNestingLevel(PrintStream& out, unsigned nestingDepth)
index 277f20b..ec14af2 100644 (file)
@@ -386,7 +386,7 @@ struct TermChain {
 
 
 struct YarrPattern {
-    JS_EXPORT_PRIVATE YarrPattern(const String& pattern, OptionSet<Flags>, ErrorCode&, void* stackLimit = nullptr);
+    JS_EXPORT_PRIVATE YarrPattern(const String& pattern, OptionSet<Flags>, ErrorCode&);
 
     void resetForReparsing()
     {
@@ -543,7 +543,7 @@ struct YarrPattern {
     HashMap<String, unsigned> m_namedGroupToParenIndex;
 
 private:
-    ErrorCode compile(const String& patternString, void* stackLimit);
+    ErrorCode compile(const String& patternString);
 
     CharacterClass* anycharCached { nullptr };
     CharacterClass* newlineCached { nullptr };