Branch: refs/heads/main
  Home:   https://github.com/WebKit/WebKit
  Commit: 753fe26f5b01d2474fc482e1e98e33a9da26941f
      
https://github.com/WebKit/WebKit/commit/753fe26f5b01d2474fc482e1e98e33a9da26941f
  Author: Yusuke Suzuki <[email protected]>
  Date:   2026-05-06 (Wed, 06 May 2026)

  Changed paths:
    A JSTests/stress/regexp-jit-fixedcount-multialt-assertion-end-fallthrough.js
    M Source/JavaScriptCore/yarr/YarrJIT.cpp

  Log Message:
  -----------
  [YARR] ParentheticalAssertionEnd.bt should propagate the failures to 
ParentheticalAssertionBegin.bt
https://bugs.webkit.org/show_bug.cgi?id=314249
rdar://176255579

Reviewed by Sosuke Suzuki.

The following code crashes.

    /(^(?!(X|Y))c|Zc){2}/.exec("cccccc")

The pattern is in this way.

    RegExp pattern for /(^(?!(X|Y))c|Zc){2}/:
        callframe size: 9
        alternative #0: minimum size: 0,once through,contains ^
          <   > captured inputPosition 0 subpattern #1 {2},frame location 0
            alternative list,frame location 4
            alternative #0: minimum size: 1,fixed size,starts with ^,contains ^
              <   > BOL
              <   > inputPosition 0 inverted assertion,frame location 5
                minimum size: 1, last alternative
                <   > captured inputPosition 1 subpattern #2,frame location 6
                  alternative list,frame location 8
                  alternative #0: minimum size: 1,fixed size
                    <   > character inputPosition 0 'X'
                  alternative #1: minimum size: 1,fixed size, last alternative
                    <   > character inputPosition 0 'Y'
              <   > character inputPosition 0 'c'
            alternative #1: minimum size: 2,fixed size, last alternative
              <   > character inputPosition 0 'Z'
              <   > character inputPosition 1 'c'
        alternative #1: minimum size: 0
          <   > captured inputPosition 0 subpattern #1 {2},frame location 0
            minimum size: 2,fixed size
            <   > character inputPosition 0 'Z'
            <   > character inputPosition 1 'c'

Problematic part is inverted-assertion `(?!(X|Y))`. When you failed to
match in the 2nd iteration of `(^(?!(X|Y))c|Zc){2}`, we do backtracking
to find a way to fit in the 1st iteration. And backtracking code in `(?!(X|Y))` 
is generated
in this way.

---- NestedAlternativeNext[0].m_contentBacktrackEntryLabel = HERE (P) ----
    ParentheticalAssertionEnd backtrack     ; (no code emitted for 
inverted/no-captures)
    SimpleNestedAlternativeEnd backtrack    ; (no code emitted)
    ParenthesesSubpatternOnceEnd backtrack  ; (no code emitted for FixedCount)
    NestedAlternativeEnd backtrack (inner disjunction):
        ldr x16, [fp - inner_returnAddressIndex]
        br  x16

But we have never write any continuation jump target inside `(X|Y)`
alternatives. The reason is this is negative-assertion: so `(X|Y)` is
just failing completely so there is no point to resume backtracking.

And that's right because assertion is *atomic*: it is not consuming any
characters and it is just look-ahead. This means that any backtracking
inside assertion does not change character position, thus it needs to
skip the backtracking completely. We enter ParentheticalAssertionEnd.bt
only when we once complete the assertion, and in this case, any change in
the ParentheticalAssertion have no effect since it does not consume any
characters, so the subsequent patterns will not see any state change.

Thus, we just immediately propagate the failure to the 
ParentheticalAssertionBegin.bt.

Test: JSTests/stress/regexp-jit-fixedcount-multialt-assertion-end-fallthrough.js

* JSTests/stress/regexp-jit-fixedcount-multialt-assertion-end-fallthrough.js: 
Added.
(shouldBe):
(i.shouldBe):
(i.shouldBe.X.Y.c):
(i.shouldBe.d.X.Y.c):
* Source/JavaScriptCore/yarr/YarrJIT.cpp:

Canonical link: https://commits.webkit.org/312767@main



To unsubscribe from these emails, change your notification settings at 
https://github.com/WebKit/WebKit/settings/notifications

Reply via email to