diff --git a/src/pcre2_match.c b/src/pcre2_match.c index e4f8f551c..72635e6a1 100644 --- a/src/pcre2_match.c +++ b/src/pcre2_match.c @@ -932,28 +932,32 @@ match(PCRE2_SPTR start_eptr, PCRE2_SPTR start_ecode, uint16_t top_bracket, PCRE2 where we know the starting frame is at the top of the chained frames, in this case we have to search back for the relevant frame in case other types of group that use chained frames have intervened. Multiple OP_CLOSEs always - come innermost first, which matches the chain order. We can ignore this in - a recursion, because captures are not passed out of recursions. */ + come innermost first, which matches the chain order. + + In a recursion, the closed groups may be returned to the caller by a + capture list. Only groups that were opened within the recursion are closed, + so the search stops at the recursion's frame. */ case OP_CLOSE: - if (Fcurrent_recurse == RECURSE_UNSET) + number = GET2(Fecode, 1); + offset = Flast_group_offset; + for (;;) { - number = GET2(Fecode, 1); - offset = Flast_group_offset; - for (;;) - { - /* Corrupted heapframes?. Trigger an assert and return an error */ - PCRE2_ASSERT(offset != PCRE2_UNSET); - if (offset == PCRE2_UNSET) - return PCRE2_ERROR_INTERNAL; + /* Corrupted heapframes?. Trigger an assert and return an error */ + PCRE2_ASSERT(offset != PCRE2_UNSET); + if (offset == PCRE2_UNSET) + return PCRE2_ERROR_INTERNAL; - N = (heapframe *)((char *)match_data->heapframes + offset); - P = (heapframe *)((char *)N - frame_size); - if (N->group_frame_type == (GF_CAPTURE | number)) - break; - offset = P->last_group_offset; - } + N = (heapframe *)((char *)match_data->heapframes + offset); + P = (heapframe *)((char *)N - frame_size); + if (N->group_frame_type == (GF_CAPTURE | number) || + GF_IDMASK(N->group_frame_type) == GF_RECURSE) + break; + offset = P->last_group_offset; + } + if (N->group_frame_type == (GF_CAPTURE | number)) + { offset = (number << 1) - 2; Fcapture_last = number; Fovector[offset] = P->eptr - mb->start_subject; @@ -1005,13 +1009,27 @@ match(PCRE2_SPTR start_eptr, PCRE2_SPTR start_ecode, uint16_t top_bracket, PCRE2 /* N is now the frame of the recursion; the previous frame is at the OP_RECURSE position. Go back there, copying the current subject position and mark, and the start_match position (\K might have changed it), and - then move on past the OP_RECURSE. */ + then move on past the OP_RECURSE. + + If the OP_RECURSE is followed by a list of capture groups to return, + build the caller's captures in the current frame, as is done when the + end of the recursion is reached, and copy them into the previous frame. + This also moves past the OP_CREF list. */ P->eptr = Feptr; P->mark = Fmark; P->start_match = Fstart_match; + + Fecode = P->ecode + 1 + LINK_SIZE; + if (*Fecode == OP_CREF) + { + recurse_update_offsets(F, P); + memcpy(P->ovector, F->ovector, Foffset_top * sizeof(PCRE2_SIZE)); + P->offset_top = Foffset_top; + } + P->ecode = Fecode; + F = P; - Fecode += 1 + LINK_SIZE; continue; } diff --git a/testdata/testinput2 b/testdata/testinput2 index 812d0b8e6..6fb189952 100644 --- a/testdata/testinput2 +++ b/testdata/testinput2 @@ -8275,6 +8275,57 @@ a)"xI /(?(DEFINE)((Sat)(urday)))(?1(2,3)),\2,\3/ Saturday,Sat,urday +# (*ACCEPT) in a called group must return the listed captures to the caller + +/(?&r(2))b(?(DEFINE)(?(a)(*ACCEPT)x))/ + ab + +/(a)(?2(3))\1\3(?(DEFINE)(b(c)(*ACCEPT)x))/ + abcac +\= Expect no match + abcab + +/^(?1(2,3))(?(DEFINE)((a)(*ACCEPT)(b)))/ + ab + +/(?1(2))(?1(3))(?(DEFINE)((a)(*ACCEPT)|(b)))!/ + ab! + +/(?(R)(a)(*ACCEPT)x|(?R(1))b\1)/ + aba +\= Expect no match + abx + +/(?1(2))c(?(DEFINE)((a)(*ACCEPT)|(ab)))/ + ac +\= Expect no match + abc + +# Groups that are still open at (*ACCEPT) in a called group are closed before +# the listed captures are returned, but groups opened outside the called group +# are not. JIT does not yet close the open groups. + +/(*NO_JIT)^(?&r(2))b\2$(?(DEFINE)(?(a(*ACCEPT)x)))/ + aba +\= Expect no match + abb + +/(*NO_JIT)^(?&r(2,3))\2\3$(?(DEFINE)(?(a(b(*ACCEPT)c))))/ + ababb + +/(*NO_JIT)^(?&r(3))\3(?(2)x|y)$(?(DEFINE)(?(a(b(*ACCEPT)c))))/ + abby +\= Expect no match + abbx + +/(*NO_JIT)(?(R)(a(*ACCEPT)x)|(?R(1))b\1)/ + aba + +/^(x(?a(*ACCEPT)b){0}(?&r(1))(?(1)y|z))/ + xaz +\= Expect no match + xay + # -------------- # Test that a memory leak has been fixed diff --git a/testdata/testoutput2 b/testdata/testoutput2 index 6ffa12c6c..734ec5de8 100644 --- a/testdata/testoutput2 +++ b/testdata/testoutput2 @@ -23617,6 +23617,97 @@ Failed: error 217 at offset 27: expected capture group number or name 2: Sat 3: urday +# (*ACCEPT) in a called group must return the listed captures to the caller + +/(?&r(2))b(?(DEFINE)(?(a)(*ACCEPT)x))/ + ab + 0: ab + 1: + 2: a + +/(a)(?2(3))\1\3(?(DEFINE)(b(c)(*ACCEPT)x))/ + abcac + 0: abcac + 1: a + 2: + 3: c +\= Expect no match + abcab +No match + +/^(?1(2,3))(?(DEFINE)((a)(*ACCEPT)(b)))/ + ab + 0: a + 1: + 2: a + +/(?1(2))(?1(3))(?(DEFINE)((a)(*ACCEPT)|(b)))!/ + ab! + 0: ab! + 1: + 2: a + 3: b + +/(?(R)(a)(*ACCEPT)x|(?R(1))b\1)/ + aba + 0: aba + 1: a +\= Expect no match + abx +No match + +/(?1(2))c(?(DEFINE)((a)(*ACCEPT)|(ab)))/ + ac + 0: ac + 1: + 2: a +\= Expect no match + abc +No match + +# Groups that are still open at (*ACCEPT) in a called group are closed before +# the listed captures are returned, but groups opened outside the called group +# are not. JIT does not yet close the open groups. + +/(*NO_JIT)^(?&r(2))b\2$(?(DEFINE)(?(a(*ACCEPT)x)))/ + aba + 0: aba + 1: + 2: a +\= Expect no match + abb +No match + +/(*NO_JIT)^(?&r(2,3))\2\3$(?(DEFINE)(?(a(b(*ACCEPT)c))))/ + ababb + 0: ababb + 1: + 2: ab + 3: b + +/(*NO_JIT)^(?&r(3))\3(?(2)x|y)$(?(DEFINE)(?(a(b(*ACCEPT)c))))/ + abby + 0: abby + 1: + 2: + 3: b +\= Expect no match + abbx +No match + +/(*NO_JIT)(?(R)(a(*ACCEPT)x)|(?R(1))b\1)/ + aba + 0: aba + 1: a + +/^(x(?a(*ACCEPT)b){0}(?&r(1))(?(1)y|z))/ + xaz + 0: xaz + 1: xaz +\= Expect no match + xay +No match + # -------------- # Test that a memory leak has been fixed