• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "runtime/regexp/ecmascript/regexp_executor.h"
17 #include "runtime/regexp/ecmascript/regexp_opcode.h"
18 #include "runtime/regexp/ecmascript/mem/dyn_chunk.h"
19 #include "utils/logger.h"
20 #include "securec.h"
21 
22 namespace ark {
23 using RegExpState = RegExpExecutor::RegExpState;
Execute(const uint8_t * input,uint32_t lastIndex,uint32_t length,uint8_t * buf,bool isWideChar)24 bool RegExpExecutor::Execute(const uint8_t *input, uint32_t lastIndex, uint32_t length, uint8_t *buf, bool isWideChar)
25 {
26     DynChunk buffer(buf);
27     input_ = const_cast<uint8_t *>(input);
28     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
29     inputEnd_ = const_cast<uint8_t *>(input + length * (isWideChar ? WIDE_CHAR_SIZE : CHAR_SIZE));
30     uint32_t size = buffer.GetU32(0);
31     nCapture_ = buffer.GetU32(RegExpParser::NUM_CAPTURE__OFFSET);
32     nStack_ = buffer.GetU32(RegExpParser::NUM_STACK_OFFSET);
33     flags_ = buffer.GetU32(RegExpParser::FLAGS_OFFSET);
34     isWideChar_ = isWideChar;
35 
36     uint32_t captureResultSize = sizeof(CaptureState) * nCapture_;
37     uint32_t stackSize = sizeof(uintptr_t) * nStack_;
38     stateSize_ = sizeof(RegExpState) + captureResultSize + stackSize;
39     stateStackLen_ = 0;
40 
41     auto allocator = Runtime::GetCurrent()->GetInternalAllocator();
42 
43     if (captureResultSize != 0) {
44         allocator->DeleteArray(captureResultList_);
45         // NOLINTNEXTLINE(modernize-avoid-c-arrays)
46         captureResultList_ = allocator->New<CaptureState[]>(nCapture_);
47         if (memset_s(captureResultList_, captureResultSize, 0, captureResultSize) != EOK) {
48             LOG(FATAL, COMMON) << "memset_s failed";
49             UNREACHABLE();
50         }
51     }
52     if (stackSize != 0) {
53         allocator->DeleteArray(stack_);
54         // NOLINTNEXTLINE(modernize-avoid-c-arrays)
55         stack_ = allocator->New<uintptr_t[]>(nStack_);
56         if (memset_s(stack_, stackSize, 0, stackSize) != EOK) {
57             LOG(FATAL, COMMON) << "memset_s failed";
58             UNREACHABLE();
59         }
60     }
61     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
62     SetCurrentPtr(input + lastIndex * (isWideChar ? WIDE_CHAR_SIZE : CHAR_SIZE));
63     SetCurrentPC(RegExpParser::OP_START_OFFSET);
64 
65     // first split
66     if ((flags_ & RegExpParser::FLAG_STICKY) == 0) {
67         PushRegExpState(STATE_SPLIT, RegExpParser::OP_START_OFFSET);
68     }
69     return ExecuteInternal(buffer, size);
70 }
71 
MatchFailed(bool isMatched)72 bool RegExpExecutor::MatchFailed(bool isMatched)
73 {
74     while (true) {
75         if (stateStackLen_ == 0) {
76             return true;
77         }
78         RegExpState *state = PeekRegExpState();
79         if (state->type == StateType::STATE_SPLIT) {
80             if (!isMatched) {
81                 PopRegExpState();
82                 return false;
83             }
84         } else {
85             isMatched = (state->type == StateType::STATE_MATCH_AHEAD && isMatched) ||
86                         (state->type == StateType::STATE_NEGATIVE_MATCH_AHEAD && !isMatched);
87             if (!isMatched) {
88                 DropRegExpState();
89                 continue;
90             }
91             if (state->type == StateType::STATE_MATCH_AHEAD) {
92                 PopRegExpState(false);
93                 return false;
94             }
95             if (state->type == StateType::STATE_NEGATIVE_MATCH_AHEAD) {
96                 PopRegExpState();
97                 return false;
98             }
99         }
100         DropRegExpState();
101     }
102 
103     return true;
104 }
105 
106 // CC-OFFNXT(G.FUN.01, huge_cyclomatic_complexity, huge_method) big switch case
107 // NOLINTNEXTLINE(readability-function-size)
ExecuteInternal(const DynChunk & byteCode,uint32_t pcEnd)108 bool RegExpExecutor::ExecuteInternal(const DynChunk &byteCode, uint32_t pcEnd)
109 {
110     while (GetCurrentPC() < pcEnd) {
111         // first split
112         if (!HandleFirstSplit()) {
113             return false;
114         }
115         uint8_t opCode = byteCode.GetU8(GetCurrentPC());
116         switch (opCode) {
117             case RegExpOpCode::OP_DOTS:
118             case RegExpOpCode::OP_ALL: {
119                 if (!HandleOpAll(opCode)) {
120                     return false;
121                 }
122                 break;
123             }
124             case RegExpOpCode::OP_CHAR32:
125             case RegExpOpCode::OP_CHAR: {
126                 if (!HandleOpChar(byteCode, opCode)) {
127                     return false;
128                 }
129                 break;
130             }
131             case RegExpOpCode::OP_NOT_WORD_BOUNDARY:
132             case RegExpOpCode::OP_WORD_BOUNDARY: {
133                 if (!HandleOpWordBoundary(opCode)) {
134                     return false;
135                 }
136                 break;
137             }
138             case RegExpOpCode::OP_LINE_START: {
139                 if (!HandleOpLineStart(opCode)) {
140                     return false;
141                 }
142                 break;
143             }
144             case RegExpOpCode::OP_LINE_END: {
145                 if (!HandleOpLineEnd(opCode)) {
146                     return false;
147                 }
148                 break;
149             }
150             case RegExpOpCode::OP_SAVE_START:
151                 HandleOpSaveStart(byteCode, opCode);
152                 break;
153             case RegExpOpCode::OP_SAVE_END:
154                 HandleOpSaveEnd(byteCode, opCode);
155                 break;
156             case RegExpOpCode::OP_GOTO: {
157                 uint32_t offset = byteCode.GetU32(GetCurrentPC() + 1);
158                 Advance(opCode, offset);
159                 break;
160             }
161             case RegExpOpCode::OP_MATCH: {
162                 // jump to match ahead
163                 if (MatchFailed(true)) {
164                     return false;
165                 }
166                 break;
167             }
168             case RegExpOpCode::OP_MATCH_END:
169                 return true;
170             case RegExpOpCode::OP_SAVE_RESET:
171                 HandleOpSaveReset(byteCode, opCode);
172                 break;
173             case RegExpOpCode::OP_SPLIT_NEXT:
174             case RegExpOpCode::OP_MATCH_AHEAD:
175             case RegExpOpCode::OP_NEGATIVE_MATCH_AHEAD:
176                 HandleOpMatch(byteCode, opCode);
177                 break;
178             case RegExpOpCode::OP_SPLIT_FIRST:
179                 HandleOpSplitFirst(byteCode, opCode);
180                 break;
181             case RegExpOpCode::OP_PREV: {
182                 if (!HandleOpPrev(opCode)) {
183                     return false;
184                 }
185                 break;
186             }
187             case RegExpOpCode::OP_LOOP_GREEDY:
188             case RegExpOpCode::OP_LOOP:
189                 HandleOpLoop(byteCode, opCode);
190                 break;
191             case RegExpOpCode::OP_PUSH_CHAR: {
192                 PushStack(reinterpret_cast<uintptr_t>(GetCurrentPtr()));
193                 Advance(opCode);
194                 break;
195             }
196             case RegExpOpCode::OP_CHECK_CHAR: {
197                 if (PopStack() != reinterpret_cast<uintptr_t>(GetCurrentPtr())) {
198                     Advance(opCode);
199                 } else {
200                     uint32_t offset = byteCode.GetU32(GetCurrentPC() + 1);
201                     Advance(opCode, offset);
202                 }
203                 break;
204             }
205             case RegExpOpCode::OP_PUSH: {
206                 PushStack(0);
207                 Advance(opCode);
208                 break;
209             }
210             case RegExpOpCode::OP_POP: {
211                 PopStack();
212                 Advance(opCode);
213                 break;
214             }
215             case RegExpOpCode::OP_RANGE32: {
216                 if (!HandleOpRange32(byteCode)) {
217                     return false;
218                 }
219                 break;
220             }
221             case RegExpOpCode::OP_RANGE: {
222                 if (!HandleOpRange(byteCode)) {
223                     return false;
224                 }
225                 break;
226             }
227             case RegExpOpCode::OP_BACKREFERENCE:
228             case RegExpOpCode::OP_BACKWARD_BACKREFERENCE: {
229                 if (!HandleOpBackReference(byteCode, opCode)) {
230                     return false;
231                 }
232                 break;
233             }
234             default:
235                 UNREACHABLE();
236         }
237     }
238     // for loop match
239     return true;
240 }
241 
DumpResult(std::ostream & out) const242 void RegExpExecutor::DumpResult(std::ostream &out) const
243 {
244     out << "captures:" << std::endl;
245     for (uint32_t i = 0; i < nCapture_; i++) {
246         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
247         CaptureState *captureState = &captureResultList_[i];
248         int32_t len = captureState->captureEnd - captureState->captureStart;
249         if ((captureState->captureStart != nullptr && captureState->captureEnd != nullptr) && (len >= 0)) {
250             out << i << ":\t" << PandaString(reinterpret_cast<const char *>(captureState->captureStart), len)
251                 << std::endl;
252         } else {
253             out << i << ":\t"
254                 << "undefined" << std::endl;
255         }
256     }
257 }
258 
PushRegExpState(StateType type,uint32_t pc)259 void RegExpExecutor::PushRegExpState(StateType type, uint32_t pc)
260 {
261     ReAllocStack(stateStackLen_ + 1);
262     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
263     auto state = reinterpret_cast<RegExpState *>(stateStack_ + stateStackLen_ * stateSize_);
264     state->type = type;
265     state->currentPc = pc;
266     state->currentStack = currentStack_;
267     state->currentPtr = GetCurrentPtr();
268     size_t listSize = sizeof(CaptureState) * nCapture_;
269     if (memcpy_s(state->captureResultList, listSize, GetCaptureResultList(), listSize) != EOK) {
270         LOG(FATAL, COMMON) << "memcpy_s failed";
271         UNREACHABLE();
272     }
273     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
274     uint8_t *stackStart = reinterpret_cast<uint8_t *>(state->captureResultList) + sizeof(CaptureState) * nCapture_;
275     if (stack_ != nullptr) {
276         size_t stackSize = sizeof(uintptr_t) * nStack_;
277         if (memcpy_s(stackStart, stackSize, stack_, stackSize) != EOK) {
278             LOG(FATAL, COMMON) << "memcpy_s failed";
279             UNREACHABLE();
280         }
281     }
282     stateStackLen_++;
283 }
284 
PopRegExpState(bool copyCaptrue)285 RegExpState *RegExpExecutor::PopRegExpState(bool copyCaptrue)
286 {
287     if (stateStackLen_ != 0) {
288         auto state = PeekRegExpState();
289         size_t listSize = sizeof(CaptureState) * nCapture_;
290         if (copyCaptrue) {
291             if (memcpy_s(GetCaptureResultList(), listSize, state->captureResultList, listSize) != EOK) {
292                 LOG(FATAL, COMMON) << "memcpy_s failed";
293                 UNREACHABLE();
294             }
295         }
296         SetCurrentPtr(state->currentPtr);
297         SetCurrentPC(state->currentPc);
298         currentStack_ = state->currentStack;
299         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
300         uint8_t *stackStart = reinterpret_cast<uint8_t *>(state->captureResultList) + listSize;
301         if (stack_ != nullptr) {
302             size_t stackSize = sizeof(uintptr_t) * nStack_;
303             if (memcpy_s(stack_, stackSize, stackStart, stackSize) != EOK) {
304                 LOG(FATAL, COMMON) << "memcpy_s failed";
305                 UNREACHABLE();
306             }
307         }
308         stateStackLen_--;
309         return state;
310     }
311     return nullptr;
312 }
313 
ReAllocStack(uint32_t stackLen)314 void RegExpExecutor::ReAllocStack(uint32_t stackLen)
315 {
316     auto allocator = Runtime::GetCurrent()->GetInternalAllocator();
317     if (stackLen > stateStackSize_) {
318         uint32_t newStackSize = std::max(stateStackSize_ * 2, MIN_STACK_SIZE);  // 2: double the size
319         uint32_t stackByteSize = newStackSize * stateSize_;
320         // NOLINTNEXTLINE(modernize-avoid-c-arrays)
321         auto newStack = allocator->New<uint8_t[]>(stackByteSize);
322         if (memset_s(newStack, stackByteSize, 0, stackByteSize) != EOK) {
323             LOG(FATAL, COMMON) << "memset_s failed";
324             UNREACHABLE();
325         }
326         if (stateStack_ != nullptr) {
327             size_t stackSize = stateStackSize_ * stateSize_;
328             if (memcpy_s(newStack, stackSize, stateStack_, stackSize) != EOK) {
329                 return;
330             }
331         }
332         allocator->DeleteArray(stateStack_);
333         stateStack_ = newStack;
334         stateStackSize_ = newStackSize;
335     }
336 }
337 
GetChar(const uint8_t ** pp,const uint8_t * end) const338 uint32_t RegExpExecutor::GetChar(const uint8_t **pp, const uint8_t *end) const
339 {
340     uint32_t c;
341     const uint8_t *cptr = *pp;
342     if (!isWideChar_) {
343         c = *cptr;
344         *pp += 1;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
345     } else {
346         uint16_t c1 = *(reinterpret_cast<const uint16_t *>(cptr));
347         c = c1;
348         cptr += WIDE_CHAR_SIZE;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
349         if (U16_IS_LEAD(c) && IsUtf16() && cptr < end) {
350             c1 = *(reinterpret_cast<const uint16_t *>(cptr));
351             if (U16_IS_TRAIL(c1)) {
352                 c = static_cast<uint32_t>(U16_GET_SUPPLEMENTARY(c, c1));  // NOLINT(hicpp-signed-bitwise)
353                 cptr += WIDE_CHAR_SIZE;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
354             }
355         }
356         *pp = cptr;
357     }
358     return c;
359 }
360 
PeekChar(const uint8_t * p,const uint8_t * end) const361 uint32_t RegExpExecutor::PeekChar(const uint8_t *p, const uint8_t *end) const
362 {
363     uint32_t c;
364     const uint8_t *cptr = p;
365     if (!isWideChar_) {
366         c = *cptr;
367     } else {
368         uint16_t c1 = *reinterpret_cast<const uint16_t *>(cptr);
369         c = c1;
370         cptr += WIDE_CHAR_SIZE;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
371         if (U16_IS_LEAD(c) && IsUtf16() && cptr < end) {
372             c1 = *reinterpret_cast<const uint16_t *>(cptr);
373             if (U16_IS_TRAIL(c1)) {
374                 c = static_cast<uint32_t>(U16_GET_SUPPLEMENTARY(c, c1));  // NOLINT(hicpp-signed-bitwise)
375             }
376         }
377     }
378     return c;
379 }
380 
AdvancePtr(const uint8_t ** pp,const uint8_t * end) const381 void RegExpExecutor::AdvancePtr(const uint8_t **pp, const uint8_t *end) const
382 {
383     const uint8_t *cptr = *pp;
384     if (!isWideChar_) {
385         *pp += 1;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
386     } else {
387         uint16_t c1 = *reinterpret_cast<const uint16_t *>(cptr);
388         cptr += WIDE_CHAR_SIZE;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
389         if (U16_IS_LEAD(c1) && IsUtf16() && cptr < end) {
390             c1 = *reinterpret_cast<const uint16_t *>(cptr);
391             if (U16_IS_TRAIL(c1)) {
392                 cptr += WIDE_CHAR_SIZE;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
393             }
394         }
395         *pp = cptr;
396     }
397 }
398 
PeekPrevChar(const uint8_t * p,const uint8_t * start) const399 uint32_t RegExpExecutor::PeekPrevChar(const uint8_t *p, const uint8_t *start) const
400 {
401     uint32_t c;
402     const uint8_t *cptr = p;
403     if (!isWideChar_) {
404         c = cptr[-1];  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
405     } else {
406         cptr -= WIDE_CHAR_SIZE;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
407         uint16_t c1 = *reinterpret_cast<const uint16_t *>(cptr);
408         c = c1;
409         if (U16_IS_TRAIL(c) && IsUtf16() && cptr > start) {
410             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
411             c1 = reinterpret_cast<const uint16_t *>(cptr)[-1];
412             if (U16_IS_LEAD(c1)) {
413                 c = static_cast<uint32_t>(U16_GET_SUPPLEMENTARY(c1, c));  // NOLINT(hicpp-signed-bitwise)
414             }
415         }
416     }
417     return c;
418 }
419 
GetPrevChar(const uint8_t ** pp,const uint8_t * start) const420 uint32_t RegExpExecutor::GetPrevChar(const uint8_t **pp, const uint8_t *start) const
421 {
422     uint32_t c;
423     const uint8_t *cptr = *pp;
424     if (!isWideChar_) {
425         c = cptr[-1];  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
426         cptr -= 1;     // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
427         *pp = cptr;
428     } else {
429         cptr -= WIDE_CHAR_SIZE;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
430         uint16_t c1 = *reinterpret_cast<const uint16_t *>(cptr);
431         c = c1;
432         if (U16_IS_TRAIL(c) && IsUtf16() && cptr > start) {
433             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
434             c1 = reinterpret_cast<const uint16_t *>(cptr)[-1];
435             if (U16_IS_LEAD(c1)) {
436                 c = static_cast<uint32_t>(U16_GET_SUPPLEMENTARY(c1, c));  // NOLINT(hicpp-signed-bitwise)
437                 cptr -= WIDE_CHAR_SIZE;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
438             }
439         }
440         *pp = cptr;
441     }
442     return c;
443 }
444 
PrevPtr(const uint8_t ** pp,const uint8_t * start) const445 void RegExpExecutor::PrevPtr(const uint8_t **pp, const uint8_t *start) const
446 {
447     const uint8_t *cptr = *pp;
448     if (!isWideChar_) {
449         cptr -= 1;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
450         *pp = cptr;
451     } else {
452         cptr -= WIDE_CHAR_SIZE;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
453         uint16_t c1 = *reinterpret_cast<const uint16_t *>(cptr);
454         if (U16_IS_TRAIL(c1) && IsUtf16() && cptr > start) {
455             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
456             c1 = reinterpret_cast<const uint16_t *>(cptr)[-1];
457             if (U16_IS_LEAD(c1)) {
458                 cptr -= WIDE_CHAR_SIZE;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
459             }
460         }
461         *pp = cptr;
462     }
463 }
464 
HandleOpBackReferenceMatch(const uint8_t * captureStart,const uint8_t * captureEnd,uint8_t opCode)465 bool RegExpExecutor::HandleOpBackReferenceMatch(const uint8_t *captureStart, const uint8_t *captureEnd, uint8_t opCode)
466 {
467     const uint8_t *refCptr = captureStart;
468     bool isMatched = true;
469     while (refCptr < captureEnd) {
470         if (IsEOF()) {
471             isMatched = false;
472             break;
473         }
474         // NOLINTNEXTLINE(readability-identifier-naming)
475         uint32_t c1 = GetChar(&refCptr, captureEnd);
476         // NOLINTNEXTLINE(readability-identifier-naming)
477         uint32_t c2 = GetChar(&currentPtr_, inputEnd_);
478         if (IsIgnoreCase()) {
479             c1 = static_cast<uint32_t>(RegExpParser::Canonicalize(c1, IsUtf16()));
480             c2 = static_cast<uint32_t>(RegExpParser::Canonicalize(c2, IsUtf16()));
481         }
482         if (c1 != c2) {
483             isMatched = false;
484             break;
485         }
486     }
487     if (!isMatched) {
488         if (MatchFailed()) {
489             return false;
490         }
491     } else {
492         Advance(opCode);
493     }
494     return true;
495 }
496 
HandleOpBackwardBackReferenceMatch(const uint8_t * captureStart,const uint8_t * captureEnd,uint8_t opCode)497 bool RegExpExecutor::HandleOpBackwardBackReferenceMatch(const uint8_t *captureStart, const uint8_t *captureEnd,
498                                                         uint8_t opCode)
499 {
500     const uint8_t *refCptr = captureEnd;
501     bool isMatched = true;
502     while (refCptr > captureStart) {
503         if (GetCurrentPtr() == input_) {
504             isMatched = false;
505             break;
506         }
507         // NOLINTNEXTLINE(readability-identifier-naming)
508         uint32_t c1 = GetPrevChar(&refCptr, captureStart);
509         // NOLINTNEXTLINE(readability-identifier-naming)
510         uint32_t c2 = GetPrevChar(&currentPtr_, input_);
511         if (IsIgnoreCase()) {
512             c1 = static_cast<uint32_t>(RegExpParser::Canonicalize(c1, IsUtf16()));
513             c2 = static_cast<uint32_t>(RegExpParser::Canonicalize(c2, IsUtf16()));
514         }
515         if (c1 != c2) {
516             isMatched = false;
517             break;
518         }
519     }
520     if (!isMatched) {
521         if (MatchFailed()) {
522             return false;
523         }
524     } else {
525         Advance(opCode);
526     }
527     return true;
528 }
529 
HandleOpBackReference(const DynChunk & byteCode,uint8_t opCode)530 bool RegExpExecutor::HandleOpBackReference(const DynChunk &byteCode, uint8_t opCode)
531 {
532     uint32_t captureIndex = byteCode.GetU8(GetCurrentPC() + 1);
533     if (captureIndex >= nCapture_) {
534         return !MatchFailed();
535     }
536     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
537     const uint8_t *captureStart = captureResultList_[captureIndex].captureStart;
538     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
539     const uint8_t *captureEnd = captureResultList_[captureIndex].captureEnd;
540     if (captureStart == nullptr || captureEnd == nullptr) {
541         Advance(opCode);
542         return true;
543     }
544 
545     if (opCode == RegExpOpCode::OP_BACKREFERENCE) {
546         return HandleOpBackReferenceMatch(captureStart, captureEnd, opCode);
547     }
548     return HandleOpBackwardBackReferenceMatch(captureStart, captureEnd, opCode);
549 }
550 
551 }  // namespace ark
552