[JSC] Implement Variable Count Parentheses in YarrJIT
// JSTests/stress/regexp-greedy-nested-quantifier-backtrack.js
test(/((a+){2,3}){2,3}$/, "aaaaaa", "aaaaaa");
test(/((a+){2,4}){2,3}$/, "aaaaaa", "aaaaaa");
test(/(((a+){2}){2}){1,2}$/, "aaaa", "aaaa");
test(/((a+){2,3}){2,3}$/, "aaa", null); // must correctly fail
YarrJIT compiles ECMAScript regular expressions directly to native machine code. It operates on ParenContext objects that save and restore capture group state during backtracking. Previously, variable-count groups with a non-zero minimum (e.g., {3,5}) triggered a JIT compile failure, silently routing execution to the slower interpreter.
This commit extends YarrJIT to JIT-compile {m,n} parentheses quantifiers with m > 0. The new count-enforcement backtracking path: when the iteration count drops below the minimum during backtracking, the engine re-enters the latest iteration's content at End.contentBacktrackEntryLabel to try alternative branches rather than immediately failing. A zero-length-match guard punts back to the interpreter. A parallel correctness fix is applied to YarrInterpreter.cpp.
Significance
New JIT-generated native code replaces a safe interpreter fallback for a non-trivial backtracking state machine — historically one of the most productive areas to find correctness bugs and occasionally memory-safety issues in regex engines.
Audit directions
count < min → contentBacktrackEntryis a new invariant enforced only by JIT-emitted branch logic. An off-by-one in the count register, or a path that reachesEnd.reentry(accept-fewer) whencount < min, would produce matches with fewer iterations than the pattern requires, silently bypassing regex-based allow/deny filters.ParenContextsave/restore across the content-backtrack path. When the engine rewinds into an earlier iteration's content, capture group slot indices must refer to the correct save frame; a stale frame pointer or wrong frame index in JIT register allocation would produce wrong$1..$Nvalues with no visible error.- Zero-length match detection. The JIT punts to the interpreter when an iteration matches zero characters, but the handoff must correctly transfer count and capture state — any inconsistency is a divergence bug.
- Nested quantifiers (
(((a+){2,3}){2,3})) give each level its ownParenContextand count register; clobbering across levels during register allocation is a classic source of silent mismatch bugs and has historically led to type confusion in other engines. - Parallel
YarrInterpreter.cppfix tobacktrackParenthesesindicates the same logical error existed in both engines — diffing interpreter versus JIT behavior on crafted edge-case inputs may surface remaining divergences.