• 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 // NOLINTNEXTLINE(readability-function-size)
ExecuteInternal(const DynChunk & byteCode,uint32_t pcEnd)107 bool RegExpExecutor::ExecuteInternal(const DynChunk &byteCode, uint32_t pcEnd)
108 {
109     while (GetCurrentPC() < pcEnd) {
110         // first split
111         if (!HandleFirstSplit()) {
112             return false;
113         }
114         uint8_t opCode = byteCode.GetU8(GetCurrentPC());
115         switch (opCode) {
116             case RegExpOpCode::OP_DOTS:
117             case RegExpOpCode::OP_ALL: {
118                 if (!HandleOpAll(opCode)) {
119                     return false;
120                 }
121                 break;
122             }
123             case RegExpOpCode::OP_CHAR32:
124             case RegExpOpCode::OP_CHAR: {
125                 if (!HandleOpChar(byteCode, opCode)) {
126                     return false;
127                 }
128                 break;
129             }
130             case RegExpOpCode::OP_NOT_WORD_BOUNDARY:
131             case RegExpOpCode::OP_WORD_BOUNDARY: {
132                 if (!HandleOpWordBoundary(opCode)) {
133                     return false;
134                 }
135                 break;
136             }
137             case RegExpOpCode::OP_LINE_START: {
138                 if (!HandleOpLineStart(opCode)) {
139                     return false;
140                 }
141                 break;
142             }
143             case RegExpOpCode::OP_LINE_END: {
144                 if (!HandleOpLineEnd(opCode)) {
145                     return false;
146                 }
147                 break;
148             }
149             case RegExpOpCode::OP_SAVE_START:
150                 HandleOpSaveStart(byteCode, opCode);
151                 break;
152             case RegExpOpCode::OP_SAVE_END:
153                 HandleOpSaveEnd(byteCode, opCode);
154                 break;
155             case RegExpOpCode::OP_GOTO: {
156                 uint32_t offset = byteCode.GetU32(GetCurrentPC() + 1);
157                 Advance(opCode, offset);
158                 break;
159             }
160             case RegExpOpCode::OP_MATCH: {
161                 // jump to match ahead
162                 if (MatchFailed(true)) {
163                     return false;
164                 }
165                 break;
166             }
167             case RegExpOpCode::OP_MATCH_END:
168                 return true;
169             case RegExpOpCode::OP_SAVE_RESET:
170                 HandleOpSaveReset(byteCode, opCode);
171                 break;
172             case RegExpOpCode::OP_SPLIT_NEXT:
173             case RegExpOpCode::OP_MATCH_AHEAD:
174             case RegExpOpCode::OP_NEGATIVE_MATCH_AHEAD:
175                 HandleOpMatch(byteCode, opCode);
176                 break;
177             case RegExpOpCode::OP_SPLIT_FIRST:
178                 HandleOpSplitFirst(byteCode, opCode);
179                 break;
180             case RegExpOpCode::OP_PREV: {
181                 if (!HandleOpPrev(opCode)) {
182                     return false;
183                 }
184                 break;
185             }
186             case RegExpOpCode::OP_LOOP_GREEDY:
187             case RegExpOpCode::OP_LOOP:
188                 HandleOpLoop(byteCode, opCode);
189                 break;
190             case RegExpOpCode::OP_PUSH_CHAR: {
191                 PushStack(reinterpret_cast<uintptr_t>(GetCurrentPtr()));
192                 Advance(opCode);
193                 break;
194             }
195             case RegExpOpCode::OP_CHECK_CHAR: {
196                 if (PopStack() != reinterpret_cast<uintptr_t>(GetCurrentPtr())) {
197                     Advance(opCode);
198                 } else {
199                     uint32_t offset = byteCode.GetU32(GetCurrentPC() + 1);
200                     Advance(opCode, offset);
201                 }
202                 break;
203             }
204             case RegExpOpCode::OP_PUSH: {
205                 PushStack(0);
206                 Advance(opCode);
207                 break;
208             }
209             case RegExpOpCode::OP_POP: {
210                 PopStack();
211                 Advance(opCode);
212                 break;
213             }
214             case RegExpOpCode::OP_RANGE32: {
215                 if (!HandleOpRange32(byteCode)) {
216                     return false;
217                 }
218                 break;
219             }
220             case RegExpOpCode::OP_RANGE: {
221                 if (!HandleOpRange(byteCode)) {
222                     return false;
223                 }
224                 break;
225             }
226             case RegExpOpCode::OP_BACKREFERENCE:
227             case RegExpOpCode::OP_BACKWARD_BACKREFERENCE: {
228                 if (!HandleOpBackReference(byteCode, opCode)) {
229                     return false;
230                 }
231                 break;
232             }
233             default:
234                 UNREACHABLE();
235         }
236     }
237     // for loop match
238     return true;
239 }
240 
DumpResult(std::ostream & out) const241 void RegExpExecutor::DumpResult(std::ostream &out) const
242 {
243     out << "captures:" << std::endl;
244     for (uint32_t i = 0; i < nCapture_; i++) {
245         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
246         CaptureState *captureState = &captureResultList_[i];
247         int32_t len = captureState->captureEnd - captureState->captureStart;
248         if ((captureState->captureStart != nullptr && captureState->captureEnd != nullptr) && (len >= 0)) {
249             out << i << ":\t" << PandaString(reinterpret_cast<const char *>(captureState->captureStart), len)
250                 << std::endl;
251         } else {
252             out << i << ":\t"
253                 << "undefined" << std::endl;
254         }
255     }
256 }
257 
PushRegExpState(StateType type,uint32_t pc)258 void RegExpExecutor::PushRegExpState(StateType type, uint32_t pc)
259 {
260     ReAllocStack(stateStackLen_ + 1);
261     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
262     auto state = reinterpret_cast<RegExpState *>(stateStack_ + stateStackLen_ * stateSize_);
263     state->type = type;
264     state->currentPc = pc;
265     state->currentStack = currentStack_;
266     state->currentPtr = GetCurrentPtr();
267     size_t listSize = sizeof(CaptureState) * nCapture_;
268     if (memcpy_s(state->captureResultList, listSize, GetCaptureResultList(), listSize) != EOK) {
269         LOG(FATAL, COMMON) << "memcpy_s failed";
270         UNREACHABLE();
271     }
272     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
273     uint8_t *stackStart = reinterpret_cast<uint8_t *>(state->captureResultList) + sizeof(CaptureState) * nCapture_;
274     if (stack_ != nullptr) {
275         size_t stackSize = sizeof(uintptr_t) * nStack_;
276         if (memcpy_s(stackStart, stackSize, stack_, stackSize) != EOK) {
277             LOG(FATAL, COMMON) << "memcpy_s failed";
278             UNREACHABLE();
279         }
280     }
281     stateStackLen_++;
282 }
283 
PopRegExpState(bool copyCaptrue)284 RegExpState *RegExpExecutor::PopRegExpState(bool copyCaptrue)
285 {
286     if (stateStackLen_ != 0) {
287         auto state = PeekRegExpState();
288         size_t listSize = sizeof(CaptureState) * nCapture_;
289         if (copyCaptrue) {
290             if (memcpy_s(GetCaptureResultList(), listSize, state->captureResultList, listSize) != EOK) {
291                 LOG(FATAL, COMMON) << "memcpy_s failed";
292                 UNREACHABLE();
293             }
294         }
295         SetCurrentPtr(state->currentPtr);
296         SetCurrentPC(state->currentPc);
297         currentStack_ = state->currentStack;
298         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
299         uint8_t *stackStart = reinterpret_cast<uint8_t *>(state->captureResultList) + listSize;
300         if (stack_ != nullptr) {
301             size_t stackSize = sizeof(uintptr_t) * nStack_;
302             if (memcpy_s(stack_, stackSize, stackStart, stackSize) != EOK) {
303                 LOG(FATAL, COMMON) << "memcpy_s failed";
304                 UNREACHABLE();
305             }
306         }
307         stateStackLen_--;
308         return state;
309     }
310     return nullptr;
311 }
312 
ReAllocStack(uint32_t stackLen)313 void RegExpExecutor::ReAllocStack(uint32_t stackLen)
314 {
315     auto allocator = Runtime::GetCurrent()->GetInternalAllocator();
316     if (stackLen > stateStackSize_) {
317         uint32_t newStackSize = std::max(stateStackSize_ * 2, MIN_STACK_SIZE);  // 2: double the size
318         uint32_t stackByteSize = newStackSize * stateSize_;
319         // NOLINTNEXTLINE(modernize-avoid-c-arrays)
320         auto newStack = allocator->New<uint8_t[]>(stackByteSize);
321         if (memset_s(newStack, stackByteSize, 0, stackByteSize) != EOK) {
322             LOG(FATAL, COMMON) << "memset_s failed";
323             UNREACHABLE();
324         }
325         if (stateStack_ != nullptr) {
326             size_t stackSize = stateStackSize_ * stateSize_;
327             if (memcpy_s(newStack, stackSize, stateStack_, stackSize) != EOK) {
328                 return;
329             }
330         }
331         allocator->DeleteArray(stateStack_);
332         stateStack_ = newStack;
333         stateStackSize_ = newStackSize;
334     }
335 }
336 
GetChar(const uint8_t ** pp,const uint8_t * end) const337 uint32_t RegExpExecutor::GetChar(const uint8_t **pp, const uint8_t *end) const
338 {
339     uint32_t c;
340     const uint8_t *cptr = *pp;
341     if (!isWideChar_) {
342         c = *cptr;
343         *pp += 1;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
344     } else {
345         uint16_t c1 = *(reinterpret_cast<const uint16_t *>(cptr));
346         c = c1;
347         cptr += WIDE_CHAR_SIZE;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
348         if (U16_IS_LEAD(c) && IsUtf16() && cptr < end) {
349             c1 = *(reinterpret_cast<const uint16_t *>(cptr));
350             if (U16_IS_TRAIL(c1)) {
351                 c = static_cast<uint32_t>(U16_GET_SUPPLEMENTARY(c, c1));  // NOLINT(hicpp-signed-bitwise)
352                 cptr += WIDE_CHAR_SIZE;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
353             }
354         }
355         *pp = cptr;
356     }
357     return c;
358 }
359 
PeekChar(const uint8_t * p,const uint8_t * end) const360 uint32_t RegExpExecutor::PeekChar(const uint8_t *p, const uint8_t *end) const
361 {
362     uint32_t c;
363     const uint8_t *cptr = p;
364     if (!isWideChar_) {
365         c = *cptr;
366     } else {
367         uint16_t c1 = *reinterpret_cast<const uint16_t *>(cptr);
368         c = c1;
369         cptr += WIDE_CHAR_SIZE;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
370         if (U16_IS_LEAD(c) && IsUtf16() && cptr < end) {
371             c1 = *reinterpret_cast<const uint16_t *>(cptr);
372             if (U16_IS_TRAIL(c1)) {
373                 c = static_cast<uint32_t>(U16_GET_SUPPLEMENTARY(c, c1));  // NOLINT(hicpp-signed-bitwise)
374             }
375         }
376     }
377     return c;
378 }
379 
AdvancePtr(const uint8_t ** pp,const uint8_t * end) const380 void RegExpExecutor::AdvancePtr(const uint8_t **pp, const uint8_t *end) const
381 {
382     const uint8_t *cptr = *pp;
383     if (!isWideChar_) {
384         *pp += 1;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
385     } else {
386         uint16_t c1 = *reinterpret_cast<const uint16_t *>(cptr);
387         cptr += WIDE_CHAR_SIZE;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
388         if (U16_IS_LEAD(c1) && IsUtf16() && cptr < end) {
389             c1 = *reinterpret_cast<const uint16_t *>(cptr);
390             if (U16_IS_TRAIL(c1)) {
391                 cptr += WIDE_CHAR_SIZE;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
392             }
393         }
394         *pp = cptr;
395     }
396 }
397 
PeekPrevChar(const uint8_t * p,const uint8_t * start) const398 uint32_t RegExpExecutor::PeekPrevChar(const uint8_t *p, const uint8_t *start) const
399 {
400     uint32_t c;
401     const uint8_t *cptr = p;
402     if (!isWideChar_) {
403         c = cptr[-1];  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
404     } else {
405         cptr -= WIDE_CHAR_SIZE;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
406         uint16_t c1 = *reinterpret_cast<const uint16_t *>(cptr);
407         c = c1;
408         if (U16_IS_TRAIL(c) && IsUtf16() && cptr > start) {
409             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
410             c1 = reinterpret_cast<const uint16_t *>(cptr)[-1];
411             if (U16_IS_LEAD(c1)) {
412                 c = static_cast<uint32_t>(U16_GET_SUPPLEMENTARY(c1, c));  // NOLINT(hicpp-signed-bitwise)
413             }
414         }
415     }
416     return c;
417 }
418 
GetPrevChar(const uint8_t ** pp,const uint8_t * start) const419 uint32_t RegExpExecutor::GetPrevChar(const uint8_t **pp, const uint8_t *start) const
420 {
421     uint32_t c;
422     const uint8_t *cptr = *pp;
423     if (!isWideChar_) {
424         c = cptr[-1];  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
425         cptr -= 1;     // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
426         *pp = cptr;
427     } else {
428         cptr -= WIDE_CHAR_SIZE;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
429         uint16_t c1 = *reinterpret_cast<const uint16_t *>(cptr);
430         c = c1;
431         if (U16_IS_TRAIL(c) && IsUtf16() && cptr > start) {
432             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
433             c1 = reinterpret_cast<const uint16_t *>(cptr)[-1];
434             if (U16_IS_LEAD(c1)) {
435                 c = static_cast<uint32_t>(U16_GET_SUPPLEMENTARY(c1, c));  // NOLINT(hicpp-signed-bitwise)
436                 cptr -= WIDE_CHAR_SIZE;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
437             }
438         }
439         *pp = cptr;
440     }
441     return c;
442 }
443 
PrevPtr(const uint8_t ** pp,const uint8_t * start) const444 void RegExpExecutor::PrevPtr(const uint8_t **pp, const uint8_t *start) const
445 {
446     const uint8_t *cptr = *pp;
447     if (!isWideChar_) {
448         cptr -= 1;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
449         *pp = cptr;
450     } else {
451         cptr -= WIDE_CHAR_SIZE;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
452         uint16_t c1 = *reinterpret_cast<const uint16_t *>(cptr);
453         if (U16_IS_TRAIL(c1) && IsUtf16() && cptr > start) {
454             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
455             c1 = reinterpret_cast<const uint16_t *>(cptr)[-1];
456             if (U16_IS_LEAD(c1)) {
457                 cptr -= WIDE_CHAR_SIZE;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
458             }
459         }
460         *pp = cptr;
461     }
462 }
463 
HandleOpBackReferenceMatch(const uint8_t * captureStart,const uint8_t * captureEnd,uint8_t opCode)464 bool RegExpExecutor::HandleOpBackReferenceMatch(const uint8_t *captureStart, const uint8_t *captureEnd, uint8_t opCode)
465 {
466     const uint8_t *refCptr = captureStart;
467     bool isMatched = true;
468     while (refCptr < captureEnd) {
469         if (IsEOF()) {
470             isMatched = false;
471             break;
472         }
473         // NOLINTNEXTLINE(readability-identifier-naming)
474         uint32_t c1 = GetChar(&refCptr, captureEnd);
475         // NOLINTNEXTLINE(readability-identifier-naming)
476         uint32_t c2 = GetChar(&currentPtr_, inputEnd_);
477         if (IsIgnoreCase()) {
478             c1 = static_cast<uint32_t>(RegExpParser::Canonicalize(c1, IsUtf16()));
479             c2 = static_cast<uint32_t>(RegExpParser::Canonicalize(c2, IsUtf16()));
480         }
481         if (c1 != c2) {
482             isMatched = false;
483             break;
484         }
485     }
486     if (!isMatched) {
487         if (MatchFailed()) {
488             return false;
489         }
490     } else {
491         Advance(opCode);
492     }
493     return true;
494 }
495 
HandleOpBackwardBackReferenceMatch(const uint8_t * captureStart,const uint8_t * captureEnd,uint8_t opCode)496 bool RegExpExecutor::HandleOpBackwardBackReferenceMatch(const uint8_t *captureStart, const uint8_t *captureEnd,
497                                                         uint8_t opCode)
498 {
499     const uint8_t *refCptr = captureEnd;
500     bool isMatched = true;
501     while (refCptr > captureStart) {
502         if (GetCurrentPtr() == input_) {
503             isMatched = false;
504             break;
505         }
506         // NOLINTNEXTLINE(readability-identifier-naming)
507         uint32_t c1 = GetPrevChar(&refCptr, captureStart);
508         // NOLINTNEXTLINE(readability-identifier-naming)
509         uint32_t c2 = GetPrevChar(&currentPtr_, input_);
510         if (IsIgnoreCase()) {
511             c1 = static_cast<uint32_t>(RegExpParser::Canonicalize(c1, IsUtf16()));
512             c2 = static_cast<uint32_t>(RegExpParser::Canonicalize(c2, IsUtf16()));
513         }
514         if (c1 != c2) {
515             isMatched = false;
516             break;
517         }
518     }
519     if (!isMatched) {
520         if (MatchFailed()) {
521             return false;
522         }
523     } else {
524         Advance(opCode);
525     }
526     return true;
527 }
528 
HandleOpBackReference(const DynChunk & byteCode,uint8_t opCode)529 bool RegExpExecutor::HandleOpBackReference(const DynChunk &byteCode, uint8_t opCode)
530 {
531     uint32_t captureIndex = byteCode.GetU8(GetCurrentPC() + 1);
532     if (captureIndex >= nCapture_) {
533         return !MatchFailed();
534     }
535     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
536     const uint8_t *captureStart = captureResultList_[captureIndex].captureStart;
537     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
538     const uint8_t *captureEnd = captureResultList_[captureIndex].captureEnd;
539     if (captureStart == nullptr || captureEnd == nullptr) {
540         Advance(opCode);
541         return true;
542     }
543 
544     if (opCode == RegExpOpCode::OP_BACKREFERENCE) {
545         return HandleOpBackReferenceMatch(captureStart, captureEnd, opCode);
546     }
547     return HandleOpBackwardBackReferenceMatch(captureStart, captureEnd, opCode);
548 }
549 
550 }  // namespace ark
551