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 "ecmascript/builtins/builtins_regexp.h"
17#include "ecmascript/builtins/builtins_regexp-inl.h"
18
19#include <cmath>
20
21#include "ecmascript/ecma_string-inl.h"
22#include "ecmascript/interpreter/interpreter.h"
23#include "ecmascript/js_regexp.h"
24#include "ecmascript/js_regexp_iterator.h"
25#include "ecmascript/js_tagged_value-inl.h"
26#include "ecmascript/object_fast_operator-inl.h"
27#include "ecmascript/property_detector-inl.h"
28#include "ecmascript/regexp/regexp_executor.h"
29#include "ecmascript/regexp/regexp_parser_cache.h"
30#include "ecmascript/tagged_array-inl.h"
31
32namespace panda::ecmascript::builtins {
33// 21.2.3.1
34JSTaggedValue BuiltinsRegExp::RegExpConstructor(EcmaRuntimeCallInfo *argv)
35{
36    ASSERT(argv);
37    BUILTINS_API_TRACE(argv->GetThread(), RegExp, Constructor);
38    JSThread *thread = argv->GetThread();
39    [[maybe_unused]] EcmaHandleScope handleScope(thread);
40    JSHandle<JSTaggedValue> newTargetTemp = GetNewTarget(argv);
41    JSHandle<JSTaggedValue> pattern = GetCallArg(argv, 0);
42    JSHandle<JSTaggedValue> flags = GetCallArg(argv, 1);
43    // 1. Let patternIsRegExp be IsRegExp(pattern).
44    bool patternIsRegExp = JSObject::IsRegExp(thread, pattern);
45    // 2. ReturnIfAbrupt(patternIsRegExp).
46    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
47    // 3. If NewTarget is not undefined, let newTarget be NewTarget.
48    JSHandle<JSTaggedValue> newTarget;
49    const GlobalEnvConstants *globalConst = thread->GlobalConstants();
50    if (!newTargetTemp->IsUndefined()) {
51        newTarget = newTargetTemp;
52    } else {
53        auto ecmaVm = thread->GetEcmaVM();
54        JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
55        // disable gc
56        [[maybe_unused]] DisallowGarbageCollection noGc;
57        // 4.a Let newTarget be the active function object.
58        newTarget = env->GetRegExpFunction();
59        JSHandle<JSTaggedValue> constructorString = globalConst->GetHandledConstructorString();
60        // 4.b If patternIsRegExp is true and flags is undefined
61        if (patternIsRegExp && flags->IsUndefined()) {
62            // 4.b.i Let patternConstructor be Get(pattern, "constructor").
63            JSTaggedValue patternConstructor = ObjectFastOperator::FastGetPropertyByValue(
64                thread, pattern.GetTaggedValue(), constructorString.GetTaggedValue());
65            // 4.b.ii ReturnIfAbrupt(patternConstructor).
66            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
67            // 4.b.iii If SameValue(newTarget, patternConstructor) is true, return pattern.
68            if (JSTaggedValue::SameValue(newTarget.GetTaggedValue(), patternConstructor)) {
69                return pattern.GetTaggedValue();
70            }
71        }
72    }
73    // 5. If Type(pattern) is Object and pattern has a [[RegExpMatcher]] internal slot
74    bool isJsReg = false;
75    if (pattern->IsECMAObject()) {
76        JSHandle<JSObject> patternObj = JSHandle<JSObject>::Cast(pattern);
77        isJsReg = patternObj->IsJSRegExp();
78    }
79    JSHandle<JSTaggedValue> patternTemp;
80    JSHandle<JSTaggedValue> flagsTemp;
81    if (isJsReg) {
82        JSHandle<JSRegExp> patternReg(thread, JSRegExp::Cast(pattern->GetTaggedObject()));
83        // 5.a Let P be the value of pattern’s [[OriginalSource]] internal slot.
84        patternTemp = JSHandle<JSTaggedValue>(thread, patternReg->GetOriginalSource());
85        if (flags->IsUndefined()) {
86            // 5.b If flags is undefined, let F be the value of pattern’s [[OriginalFlags]] internal slot.
87            flagsTemp = JSHandle<JSTaggedValue>(thread, patternReg->GetOriginalFlags());
88        } else {
89            // 5.c Else, let F be flags.
90            flagsTemp = JSHandle<JSTaggedValue>(thread, *JSTaggedValue::ToString(thread, flags));
91        }
92        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
93        // 6. Else if patternIsRegExp is true
94    } else if (patternIsRegExp) {
95        JSHandle<JSTaggedValue> sourceString(globalConst->GetHandledSourceString());
96        JSHandle<JSTaggedValue> flagsString(globalConst->GetHandledFlagsString());
97        // disable gc
98        [[maybe_unused]] DisallowGarbageCollection noGc;
99        // 6.a Let P be Get(pattern, "source").
100        patternTemp = JSObject::GetProperty(thread, pattern, sourceString).GetValue();
101        // 6.b ReturnIfAbrupt(P).
102        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
103        // 6.c If flags is undefined
104        if (flags->IsUndefined()) {
105            // 6.c.i Let F be Get(pattern, "flags").
106            flagsTemp = JSObject::GetProperty(thread, pattern, flagsString).GetValue();
107            // 6.c.ii ReturnIfAbrupt(F).
108            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
109        } else {
110            // 6.d Else, let F be flags.
111            flagsTemp = JSHandle<JSTaggedValue>(thread, *JSTaggedValue::ToString(thread, flags));
112            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
113        }
114    } else {
115        // 7.a Let P be pattern.
116        patternTemp = pattern;
117        // 7.b Let F be flags.
118        if (flags->IsUndefined()) {
119            flagsTemp = flags;
120        } else {
121            flagsTemp = JSHandle<JSTaggedValue>(thread, *JSTaggedValue::ToString(thread, flags));
122            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
123        }
124    }
125    // 8. Let O be RegExpAlloc(newTarget).
126    JSHandle<JSTaggedValue> object(thread, RegExpAlloc(thread, newTarget));
127    // 9. ReturnIfAbrupt(O).
128    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
129    // 10. Return RegExpInitialize(O, P, F).
130    JSTaggedValue result = RegExpInitialize(thread, object, patternTemp, flagsTemp);
131    return JSTaggedValue(result);
132}
133
134// prototype
135// 20.2.5.2
136JSTaggedValue BuiltinsRegExp::Exec(EcmaRuntimeCallInfo *argv)
137{
138    ASSERT(argv);
139    BUILTINS_API_TRACE(argv->GetThread(), RegExp, Exec);
140    JSThread *thread = argv->GetThread();
141    [[maybe_unused]] EcmaHandleScope handleScope(thread);
142    // 1. Let R be the this value.
143    JSHandle<JSTaggedValue> thisObj = GetThis(argv);
144    // 4. Let S be ToString(string).
145    JSHandle<JSTaggedValue> inputStr = GetCallArg(argv, 0);
146    JSHandle<EcmaString> stringHandle = JSTaggedValue::ToString(thread, inputStr);
147    // 5. ReturnIfAbrupt(S).
148    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
149    JSHandle<JSTaggedValue> string = JSHandle<JSTaggedValue>::Cast(stringHandle);
150    // 2. If Type(R) is not Object, throw a TypeError exception.
151    if (!thisObj->IsECMAObject()) {
152        // throw a TypeError exception.
153        THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
154    }
155    // 3. If R does not have a [[RegExpMatcher]] internal slot, throw a TypeError exception.
156    if (!thisObj->IsJSRegExp()) {
157        // throw a TypeError exception.
158        THROW_TYPE_ERROR_AND_RETURN(thread, "this does not have [[RegExpMatcher]]", JSTaggedValue::Exception());
159    }
160
161    bool useCache = true;
162    bool isFastPath = IsFastRegExp(thread, thisObj);
163    JSHandle<RegExpExecResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetRegExpCache());
164    if (!isFastPath || cacheTable->GetLargeStrCount() == 0 || cacheTable->GetConflictCount() == 0) {
165        useCache = false;
166    }
167
168    // 6. Return RegExpBuiltinExec(R, S).
169    return RegExpBuiltinExec(thread, thisObj, string, isFastPath, useCache);
170}
171
172// 20.2.5.13
173JSTaggedValue BuiltinsRegExp::Test(EcmaRuntimeCallInfo *argv)
174{
175    ASSERT(argv);
176    BUILTINS_API_TRACE(argv->GetThread(), RegExp, Test);
177    JSThread *thread = argv->GetThread();
178    [[maybe_unused]] EcmaHandleScope handleScope(thread);
179    // 1. Let R be the this value.
180    JSHandle<JSTaggedValue> thisObj = GetThis(argv);
181    JSHandle<JSTaggedValue> inputStr = GetCallArg(argv, 0);
182    // 2. If Type(R) is not Object, throw a TypeError exception.
183    if (!thisObj->IsECMAObject()) {
184        // throw a TypeError exception.
185        THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
186    }
187    // 3. Let string be ToString(S).
188    // 4. ReturnIfAbrupt(string).
189    JSHandle<EcmaString> stringHandle = JSTaggedValue::ToString(thread, inputStr);
190    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
191    JSHandle<JSTaggedValue> string = JSHandle<JSTaggedValue>::Cast(stringHandle);
192    // test fast path
193    if (IsFastRegExp(thread, thisObj)) {
194        return RegExpTestFast(thread, thisObj, string, true);
195    }
196
197    // 5. Let match be RegExpExec(R, string).
198    JSTaggedValue matchResult = RegExpExec(thread, thisObj, string, false);
199    // 6. ReturnIfAbrupt(match).
200    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
201    // 7. If match is not null, return true; else return false.
202    return GetTaggedBoolean(!matchResult.IsNull());
203}
204
205bool BuiltinsRegExp::IsFastRegExp(JSThread *thread, JSHandle<JSTaggedValue> regexp,
206                                  RegExpSymbol symbolTag)
207{
208    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
209    const GlobalEnvConstants *globalConst = thread->GlobalConstants();
210    JSHClass *hclass = JSHandle<JSObject>::Cast(regexp)->GetJSHClass();
211    JSHClass *originHClass = JSHClass::Cast(globalConst->GetJSRegExpClass().GetTaggedObject());
212    // regexp instance hclass
213    if (hclass != originHClass) {
214        return false;
215    }
216    // lastIndex type is Int
217    JSTaggedValue lastIndex = JSHandle<JSObject>::Cast(regexp)->GetPropertyInlinedProps(LAST_INDEX_OFFSET);
218    if (!lastIndex.IsInt() || lastIndex.GetInt() < 0) {
219        return false;
220    }
221    // RegExp.prototype hclass
222    JSTaggedValue proto = hclass->GetPrototype();
223    JSHClass *regexpHclass = proto.GetTaggedObject()->GetClass();
224    JSHandle<JSTaggedValue> originRegexpClassValue = env->GetRegExpPrototypeClass();
225    JSHClass *originRegexpHclass = JSHClass::Cast(originRegexpClassValue.GetTaggedValue().GetTaggedObject());
226    if (regexpHclass != originRegexpHclass) {
227        return false;
228    }
229    JSObject* protoObj = JSObject::Cast(proto);
230    // RegExp.prototype.exec
231    JSTaggedValue execVal = protoObj->GetPropertyInlinedProps(JSRegExp::EXEC_INLINE_PROPERTY_INDEX);
232    if (execVal != env->GetTaggedRegExpExecFunction()) {
233        return false;
234    }
235    JSTaggedValue symbolFunc = JSTaggedValue::Hole();
236    switch (symbolTag) {
237        case RegExpSymbol::UNKNOWN:
238            break;
239#define V(UpperCase, Camel)                                                     \
240        case RegExpSymbol::UpperCase:                                           \
241            symbolFunc = protoObj->GetPropertyInlinedProps(                     \
242                JSRegExp::UpperCase##_INLINE_PROPERTY_INDEX);                   \
243            if (symbolFunc != env->GetTaggedRegExp##Camel##Function()) {        \
244                return false;                                                   \
245            }                                                                   \
246            break;
247            REGEXP_SYMBOL_FUNCTION_LIST(V)
248#undef V
249    }
250    if (!PropertyDetector::IsRegExpFlagsDetectorValid(env)) {
251        return false;
252    }
253    return true;
254}
255
256JSTaggedValue BuiltinsRegExp::RegExpTestFast(JSThread *thread, JSHandle<JSTaggedValue> regexp,
257                                             const JSHandle<JSTaggedValue> inputStr, bool useCache)
258{
259    // 1. Assert: Type(S) is String.
260    ASSERT(inputStr->IsString());
261    uint32_t lastIndex = static_cast<uint32_t>(GetLastIndex(thread, regexp, true));
262    // 2. Search RegExpExecResult cache
263    JSHandle<RegExpExecResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetRegExpCache());
264    JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
265    if (useCache) {
266        JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, inputStr,
267                                                                 RegExpExecResultCache::TEST_TYPE, regexp,
268                                                                 JSTaggedValue(lastIndex), undefined);
269        if (!cacheResult.IsUndefined()) {
270            return cacheResult;
271        }
272    }
273
274    uint32_t length = EcmaStringAccessor(inputStr->GetTaggedObject()).GetLength();
275    if (lastIndex > length) {
276        SetLastIndex(thread, regexp, JSTaggedValue(0), true);
277        return JSTaggedValue::False();
278    }
279    JSHandle<EcmaString> inputString = JSHandle<EcmaString>::Cast(inputStr);
280    bool matchResult = RegExpExecInternal(thread, regexp, inputString, lastIndex);
281    // 2. Check whether the regexp is global or sticky, which determines whether we update last index later on.
282    bool global = GetOriginalFlag(thread, regexp, RegExpParser::FLAG_GLOBAL);
283    bool sticky = GetOriginalFlag(thread, regexp, RegExpParser::FLAG_STICKY);
284    bool ifUpdateLastIndex = global || sticky;
285    if (!matchResult) {
286        if (ifUpdateLastIndex) {
287            SetLastIndex(thread, regexp, JSTaggedValue(0), true);
288        }
289        if (useCache) {
290            RegExpExecResultCache::AddResultInCache(thread, cacheTable, regexp, inputStr,
291                                                    JSHandle<JSTaggedValue>(thread, JSTaggedValue(matchResult)),
292                                                    RegExpExecResultCache::TEST_TYPE,
293                                                    lastIndex, 0, undefined); // 0: match fail so lastIndex is 0
294        }
295        return JSTaggedValue::False();
296    }
297    JSHandle<RegExpGlobalResult> globalTable(thread->GetCurrentEcmaContext()->GetRegExpGlobalResult());
298    JSTaggedValue endIndex = globalTable->GetEndIndex();
299    uint32_t newLastIndex = lastIndex;
300    if (ifUpdateLastIndex) {
301        newLastIndex = static_cast<uint32_t>(endIndex.GetInt());
302        SetLastIndex(thread, regexp, endIndex, true);
303    }
304    if (useCache) {
305        RegExpExecResultCache::AddResultInCache(thread, cacheTable, regexp, inputStr,
306                                                JSHandle<JSTaggedValue>(thread, JSTaggedValue(matchResult)),
307                                                RegExpExecResultCache::TEST_TYPE,
308                                                lastIndex, newLastIndex, undefined);
309    }
310    return GetTaggedBoolean(matchResult);
311}
312
313// 20.2.5.14
314JSTaggedValue BuiltinsRegExp::ToString(EcmaRuntimeCallInfo *argv)
315{
316    ASSERT(argv);
317    BUILTINS_API_TRACE(argv->GetThread(), RegExp, ToString);
318    JSThread *thread = argv->GetThread();
319    [[maybe_unused]] EcmaHandleScope handleScope(thread);
320    // 1. Let R be the this value.
321    JSHandle<JSTaggedValue> thisObj = GetThis(argv);
322    auto ecmaVm = thread->GetEcmaVM();
323    // 2. If Type(R) is not Object, throw a TypeError exception.
324    if (!thisObj->IsECMAObject()) {
325        THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
326    }
327    const GlobalEnvConstants *globalConstants = thread->GlobalConstants();
328    JSMutableHandle<JSTaggedValue> getSource(thread, JSTaggedValue::Undefined());
329    JSMutableHandle<JSTaggedValue> getFlags(thread, JSTaggedValue::Undefined());
330    JSHandle<EcmaString> sourceStrHandle;
331    JSHandle<EcmaString> flagsStrHandle;
332    if (IsFastRegExp(thread, thisObj)) {
333        JSHandle<JSRegExp> regexp(thread, JSRegExp::Cast(thisObj->GetTaggedObject()));
334        // 3. Let pattern be ToString(Get(R, "source")).
335        getSource.Update(regexp->GetOriginalSource());
336        sourceStrHandle = JSTaggedValue::ToString(thread, getSource);
337        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
338        uint8_t flagsBits = static_cast<uint8_t>(regexp->GetOriginalFlags().GetInt());
339        getFlags.Update(FlagsBitsToString(thread, flagsBits));
340        flagsStrHandle = JSTaggedValue::ToString(thread, getFlags);
341        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
342    } else {
343        JSHandle<JSTaggedValue> sourceString(globalConstants->GetHandledSourceString());
344        JSHandle<JSTaggedValue> flagsString(globalConstants->GetHandledFlagsString());
345        // 3. Let pattern be ToString(Get(R, "source")).
346        getSource.Update(JSObject::GetProperty(thread, thisObj, sourceString).GetValue());
347        sourceStrHandle = JSTaggedValue::ToString(thread, getSource);
348        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
349        getFlags.Update(JSObject::GetProperty(thread, thisObj, flagsString).GetValue());
350        flagsStrHandle = JSTaggedValue::ToString(thread, getFlags);
351        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
352    }
353    JSHandle<EcmaString> slashStr = JSHandle<EcmaString>::Cast(globalConstants->GetHandledBackslashString());
354    // 7. Let result be the String value formed by concatenating "/", pattern, and "/", and flags.
355    ObjectFactory *factory = ecmaVm->GetFactory();
356    JSHandle<EcmaString> tempStr = factory->ConcatFromString(slashStr, sourceStrHandle);
357    JSHandle<EcmaString> resultTemp = factory->ConcatFromString(tempStr, slashStr);
358    return factory->ConcatFromString(resultTemp, flagsStrHandle).GetTaggedValue();
359}
360
361// 20.2.5.3
362JSTaggedValue BuiltinsRegExp::GetFlags(EcmaRuntimeCallInfo *argv)
363{
364    ASSERT(argv);
365    BUILTINS_API_TRACE(argv->GetThread(), RegExp, GetFlags);
366    JSThread *thread = argv->GetThread();
367    [[maybe_unused]] EcmaHandleScope handleScope(thread);
368    // 1. Let R be the this value.
369    JSHandle<JSTaggedValue> thisObj = GetThis(argv);
370    // 2. If Type(R) is not Object, throw a TypeError exception.
371    if (!thisObj->IsECMAObject()) {
372        THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
373    }
374    // 3. Let result be the empty String.
375    // 4. ~ 19.
376    if (!IsFastRegExp(thread, thisObj)) {
377        return GetAllFlagsInternal(thread, thisObj);
378    }
379    uint8_t flagsBits = static_cast<uint8_t>(JSRegExp::Cast(thisObj->GetTaggedObject())->GetOriginalFlags().GetInt());
380    return FlagsBitsToString(thread, flagsBits);
381}
382
383JSTaggedValue BuiltinsRegExp::GetAllFlagsInternal(JSThread *thread, JSHandle<JSTaggedValue> &thisObj)
384{
385    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
386    const GlobalEnvConstants *globalConstants = thread->GlobalConstants();
387    uint8_t *flagsStr = new uint8_t[RegExpParser::FLAG_NUM + 1];  // FLAG_NUM flags + '\0'
388    if (flagsStr == nullptr) {
389        LOG_ECMA(FATAL) << "BuiltinsRegExp::GetAllFlagsInternal:flagsStr is nullptr";
390    }
391    size_t flagsLen = 0;
392    JSHandle<EcmaString> emptyString = factory->GetEmptyString();
393    JSHandle<JSTaggedValue> hasIndicesKey(factory->NewFromASCII("hasIndices"));
394    JSHandle<JSTaggedValue> hasIndicesResult = JSObject::GetProperty(thread, thisObj, hasIndicesKey).GetValue();
395    RETURN_VALUE_IF_ABRUPT_COMPLETION_WITH_DATA_DELETE(thread, emptyString.GetTaggedValue(), flagsStr);
396    if (hasIndicesResult->ToBoolean()) {
397        flagsStr[flagsLen] = 'd';
398        flagsLen++;
399    }
400    JSHandle<JSTaggedValue> globalKey(globalConstants->GetHandledGlobalString());
401    JSHandle<JSTaggedValue> globalResult = JSObject::GetProperty(thread, thisObj, globalKey).GetValue();
402    RETURN_VALUE_IF_ABRUPT_COMPLETION_WITH_DATA_DELETE(thread, emptyString.GetTaggedValue(), flagsStr);
403    if (globalResult->ToBoolean()) {
404        flagsStr[flagsLen] = 'g';
405        flagsLen++;
406    }
407    JSHandle<JSTaggedValue> ignoreCaseKey(factory->NewFromASCII("ignoreCase"));
408    JSHandle<JSTaggedValue> ignoreCaseResult = JSObject::GetProperty(thread, thisObj, ignoreCaseKey).GetValue();
409    RETURN_VALUE_IF_ABRUPT_COMPLETION_WITH_DATA_DELETE(thread, emptyString.GetTaggedValue(), flagsStr);
410    if (ignoreCaseResult->ToBoolean()) {
411        flagsStr[flagsLen] = 'i';
412        flagsLen++;
413    }
414    JSHandle<JSTaggedValue> multilineKey(factory->NewFromASCII("multiline"));
415    JSHandle<JSTaggedValue> multilineResult = JSObject::GetProperty(thread, thisObj, multilineKey).GetValue();
416    RETURN_VALUE_IF_ABRUPT_COMPLETION_WITH_DATA_DELETE(thread, emptyString.GetTaggedValue(), flagsStr);
417    if (multilineResult->ToBoolean()) {
418        flagsStr[flagsLen] = 'm';
419        flagsLen++;
420    }
421    JSHandle<JSTaggedValue> dotAllKey(factory->NewFromASCII("dotAll"));
422    JSHandle<JSTaggedValue> dotAllResult = JSObject::GetProperty(thread, thisObj, dotAllKey).GetValue();
423    RETURN_VALUE_IF_ABRUPT_COMPLETION_WITH_DATA_DELETE(thread, emptyString.GetTaggedValue(), flagsStr);
424    if (dotAllResult->ToBoolean()) {
425        flagsStr[flagsLen] = 's';
426        flagsLen++;
427    }
428    JSHandle<JSTaggedValue> unicodeKey(globalConstants->GetHandledUnicodeString());
429    JSHandle<JSTaggedValue> unicodeResult = JSObject::GetProperty(thread, thisObj, unicodeKey).GetValue();
430    RETURN_VALUE_IF_ABRUPT_COMPLETION_WITH_DATA_DELETE(thread, emptyString.GetTaggedValue(), flagsStr);
431    if (unicodeResult->ToBoolean()) {
432        flagsStr[flagsLen] = 'u';
433        flagsLen++;
434    }
435    JSHandle<JSTaggedValue> stickyKey(globalConstants->GetHandledStickyString());
436    JSHandle<JSTaggedValue> stickyResult = JSObject::GetProperty(thread, thisObj, stickyKey).GetValue();
437    RETURN_VALUE_IF_ABRUPT_COMPLETION_WITH_DATA_DELETE(thread, emptyString.GetTaggedValue(), flagsStr);
438    if (stickyResult->ToBoolean()) {
439        flagsStr[flagsLen] = 'y';
440        flagsLen++;
441    }
442    flagsStr[flagsLen] = '\0';
443    JSHandle<EcmaString> flagsString = factory->NewFromUtf8(flagsStr, flagsLen);
444    delete[] flagsStr;
445
446    return flagsString.GetTaggedValue();
447}
448
449// 20.2.5.4
450JSTaggedValue BuiltinsRegExp::GetGlobal(EcmaRuntimeCallInfo *argv)
451{
452    ASSERT(argv);
453    JSThread *thread = argv->GetThread();
454    BUILTINS_API_TRACE(thread, RegExp, GetGlobal);
455    [[maybe_unused]] EcmaHandleScope handleScope(thread);
456    JSHandle<JSTaggedValue> thisObj = GetThis(argv);
457    JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
458    return GetFlagsInternal(thread, thisObj, constructor, RegExpParser::FLAG_GLOBAL);
459}
460
461// 22.2.6.6
462JSTaggedValue BuiltinsRegExp::GetHasIndices(EcmaRuntimeCallInfo *argv)
463{
464    ASSERT(argv);
465    JSThread *thread = argv->GetThread();
466    BUILTINS_API_TRACE(thread, RegExp, GetHasIndices);
467    [[maybe_unused]] EcmaHandleScope handleScope(thread);
468    JSHandle<JSTaggedValue> thisObj = GetThis(argv);
469    JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
470    return GetFlagsInternal(thread, thisObj, constructor, RegExpParser::FLAG_HASINDICES);
471}
472
473// 20.2.5.5
474JSTaggedValue BuiltinsRegExp::GetIgnoreCase(EcmaRuntimeCallInfo *argv)
475{
476    ASSERT(argv);
477    JSThread *thread = argv->GetThread();
478    BUILTINS_API_TRACE(thread, RegExp, GetIgnoreCase);
479    [[maybe_unused]] EcmaHandleScope handleScope(thread);
480    JSHandle<JSTaggedValue> thisObj = GetThis(argv);
481    JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
482    return GetFlagsInternal(thread, thisObj, constructor, RegExpParser::FLAG_IGNORECASE);
483}
484
485// 20.2.5.7
486JSTaggedValue BuiltinsRegExp::GetMultiline(EcmaRuntimeCallInfo *argv)
487{
488    ASSERT(argv);
489    JSThread *thread = argv->GetThread();
490    BUILTINS_API_TRACE(thread, RegExp, GetMultiline);
491    [[maybe_unused]] EcmaHandleScope handleScope(thread);
492    JSHandle<JSTaggedValue> thisObj = GetThis(argv);
493    JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
494    return GetFlagsInternal(thread, thisObj, constructor, RegExpParser::FLAG_MULTILINE);
495}
496
497JSTaggedValue BuiltinsRegExp::GetDotAll(EcmaRuntimeCallInfo *argv)
498{
499    ASSERT(argv);
500    JSThread *thread = argv->GetThread();
501    BUILTINS_API_TRACE(thread, RegExp, GetDotAll);
502    [[maybe_unused]] EcmaHandleScope handleScope(thread);
503    JSHandle<JSTaggedValue> thisObj = GetThis(argv);
504    JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
505    return GetFlagsInternal(thread, thisObj, constructor, RegExpParser::FLAG_DOTALL);
506}
507
508// 20.2.5.10
509JSTaggedValue BuiltinsRegExp::GetSource(EcmaRuntimeCallInfo *argv)
510{
511    ASSERT(argv);
512    JSThread *thread = argv->GetThread();
513    BUILTINS_API_TRACE(thread, RegExp, GetSource);
514    [[maybe_unused]] EcmaHandleScope handleScope(thread);
515    // 1. Let R be the this value.
516    JSHandle<JSTaggedValue> thisObj = GetThis(argv);
517    // 2. If Type(R) is not Object, throw a TypeError exception.
518    // 3. If R does not have an [[OriginalSource]] internal slot, throw a TypeError exception.
519    // 4. If R does not have an [[OriginalFlags]] internal slot, throw a TypeError exception.
520    if (!thisObj->IsECMAObject()) {
521        // throw a TypeError exception.
522        THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
523    }
524    if (!thisObj->IsJSRegExp()) {
525        // a. If SameValue(R, %RegExp.prototype%) is true, return "(?:)".
526        const GlobalEnvConstants *globalConst = thread->GlobalConstants();
527        JSHandle<JSTaggedValue> constructorKey = globalConst->GetHandledConstructorString();
528        JSHandle<JSTaggedValue> objConstructor = JSTaggedValue::GetProperty(thread, thisObj, constructorKey).GetValue();
529        RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue(false));
530        JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
531        if (objConstructor->IsJSFunction() && constructor->IsJSFunction()) {
532            JSHandle<GlobalEnv> objRealm = JSObject::GetFunctionRealm(thread, objConstructor);
533            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
534            JSHandle<GlobalEnv> ctorRealm = JSObject::GetFunctionRealm(thread, constructor);
535            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
536            if (objRealm->GetRegExpPrototype() == thisObj && *objRealm == *ctorRealm) {
537                JSHandle<EcmaString> result = thread->GetEcmaVM()->GetFactory()->NewFromASCII("(?:)");
538                return result.GetTaggedValue();
539            }
540        }
541        // b. throw a TypeError exception.
542        THROW_TYPE_ERROR_AND_RETURN(thread, "this does not have [[OriginalSource]]", JSTaggedValue::Exception());
543    }
544    // 5. Let src be the value of R’s [[OriginalSource]] internal slot.
545    JSHandle<JSRegExp> regexpObj(thread, JSRegExp::Cast(thisObj->GetTaggedObject()));
546    JSHandle<JSTaggedValue> source(thread, regexpObj->GetOriginalSource());
547    // 6. Let flags be the value of R’s [[OriginalFlags]] internal slot.
548    uint8_t flagsBits = static_cast<uint8_t>(regexpObj->GetOriginalFlags().GetInt());
549    JSHandle<JSTaggedValue> flags(thread, FlagsBitsToString(thread, flagsBits));
550    // 7. Return EscapeRegExpPattern(src, flags).
551    return JSTaggedValue(EscapeRegExpPattern(thread, source, flags));
552}
553
554// 20.2.5.12
555JSTaggedValue BuiltinsRegExp::GetSticky(EcmaRuntimeCallInfo *argv)
556{
557    ASSERT(argv);
558    JSThread *thread = argv->GetThread();
559    BUILTINS_API_TRACE(thread, RegExp, GetSticky);
560    [[maybe_unused]] EcmaHandleScope handleScope(thread);
561    JSHandle<JSTaggedValue> thisObj = GetThis(argv);
562    JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
563    return GetFlagsInternal(thread, thisObj, constructor, RegExpParser::FLAG_STICKY);
564}
565
566// 20.2.5.15
567JSTaggedValue BuiltinsRegExp::GetUnicode(EcmaRuntimeCallInfo *argv)
568{
569    ASSERT(argv);
570    JSThread *thread = argv->GetThread();
571    BUILTINS_API_TRACE(thread, RegExp, GetUnicode);
572    [[maybe_unused]] EcmaHandleScope handleScope(thread);
573    JSHandle<JSTaggedValue> thisObj = GetThis(argv);
574    JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
575    return GetFlagsInternal(thread, thisObj, constructor, RegExpParser::FLAG_UTF16);
576}
577
578// 21.2.4.2
579JSTaggedValue BuiltinsRegExp::GetSpecies(EcmaRuntimeCallInfo *argv)
580{
581    ASSERT(argv);
582    BUILTINS_API_TRACE(argv->GetThread(), RegExp, GetSpecies);
583    return GetThis(argv).GetTaggedValue();
584}
585
586// 21.2.5.6
587JSTaggedValue BuiltinsRegExp::Match(EcmaRuntimeCallInfo *argv)
588{
589    ASSERT(argv);
590    BUILTINS_API_TRACE(argv->GetThread(), RegExp, Match);
591    JSThread *thread = argv->GetThread();
592    [[maybe_unused]] EcmaHandleScope handleScope(thread);
593    // 1. Let rx be the this value.
594    JSHandle<JSTaggedValue> thisObj = GetThis(argv);
595    // 3. Let S be ToString(string)
596    JSHandle<JSTaggedValue> inputString = GetCallArg(argv, 0);
597    JSHandle<EcmaString> stringHandle = JSTaggedValue::ToString(thread, inputString);
598    // 4. ReturnIfAbrupt(string).
599    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
600    JSHandle<JSTaggedValue> string = JSHandle<JSTaggedValue>::Cast(stringHandle);
601    if (!thisObj->IsECMAObject()) {
602        // 2. If Type(rx) is not Object, throw a TypeError exception.
603        THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
604    }
605    bool isFastPath = IsFastRegExp(thread, thisObj);
606    return RegExpMatch(thread, thisObj, string, isFastPath);
607}
608
609JSTaggedValue BuiltinsRegExp::RegExpMatch(JSThread *thread, const JSHandle<JSTaggedValue> regexp,
610                                          const JSHandle<JSTaggedValue> string, bool isFastPath)
611{
612    bool useCache = true;
613    JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
614    JSHandle<RegExpExecResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetRegExpCache());
615    if (!isFastPath || cacheTable->GetLargeStrCount() == 0 || cacheTable->GetConflictCount() == 0) {
616        useCache = false;
617    }
618    bool isGlobal = GetFlag(thread, regexp, RegExpParser::FLAG_GLOBAL, isFastPath);
619    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
620    // 7. If global is false, then
621    if (!isGlobal) {
622        // a. Return RegExpExec(rx, S).
623        if (isFastPath) {
624            return RegExpBuiltinExec(thread, regexp, string, isFastPath, useCache);
625        } else {
626            return RegExpExec(thread, regexp, string, useCache);
627        }
628    }
629
630    if (useCache) {
631        uint32_t lastIndex = static_cast<uint32_t>(GetLastIndex(thread, regexp, isFastPath));
632        JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, string,
633                                                                 RegExpExecResultCache::MATCH_TYPE, regexp,
634                                                                 JSTaggedValue(lastIndex), undefined);
635        if (!cacheResult.IsUndefined()) {
636            return cacheResult;
637        }
638    }
639    bool fullUnicode = GetFlag(thread, regexp, RegExpParser::FLAG_UTF16, isFastPath);
640    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
641    // b. Let setStatus be Set(rx, "lastIndex", 0, true).
642    SetLastIndex(thread, regexp, JSTaggedValue(0), isFastPath);
643    // c. ReturnIfAbrupt(setStatus).
644    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
645    // d. Let A be ArrayCreate(0).
646    JSHandle<JSArray> array(JSArray::ArrayCreate(thread, JSTaggedNumber(0), ArrayMode::LITERAL));
647    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
648    TaggedArray *srcElements = TaggedArray::Cast(array->GetElements().GetTaggedObject());
649    JSMutableHandle<TaggedArray> elements(thread, srcElements);
650    // e. Let n be 0.
651    uint32_t resultNum = 0;
652    uint32_t arrLen = 1;
653    JSMutableHandle<JSTaggedValue> result(thread, JSTaggedValue::Undefined());
654    JSMutableHandle<EcmaString> matchString(thread, JSTaggedValue::Undefined());
655    // f. Repeat,
656    while (true) {
657        if (isFastPath) {
658            uint32_t lastIndex = static_cast<uint32_t>(GetLastIndex(thread, regexp, isFastPath));
659            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
660            result.Update(RegExpBuiltinExecWithoutResult(thread, regexp, string, isFastPath, lastIndex, false));
661            JSHandle<RegExpGlobalResult> globalTable(thread->GetCurrentEcmaContext()->GetRegExpGlobalResult());
662            uint32_t endIndex = static_cast<uint32_t>(globalTable->GetEndOfCaptureIndex(0).GetInt());
663            if (result->IsNull()) {
664                // 1. If n=0, return null.
665                lastIndex = static_cast<uint32_t>(GetLastIndex(thread, regexp, isFastPath));
666                RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
667                if (resultNum == 0) {
668                    RegExpExecResultCache::AddResultInCache(thread, cacheTable, regexp, string,
669                                                            JSHandle<JSTaggedValue>(thread, JSTaggedValue::Null()),
670                                                            RegExpExecResultCache::MATCH_TYPE,
671                                                            0, 0, undefined);
672                    return JSTaggedValue::Null();
673                }
674                RegExpExecResultCache::AddResultInCache(thread, cacheTable, regexp, string,
675                                                        JSHandle<JSTaggedValue>::Cast(array),
676                                                        RegExpExecResultCache::MATCH_TYPE,
677                                                        0, 0, undefined);
678                // 2. Else, return A.
679                return array.GetTaggedValue();
680            }
681            uint32_t startIndex = static_cast<uint32_t>(globalTable->GetStartOfCaptureIndex(0).GetInt());
682            uint32_t len = endIndex - startIndex;
683            matchString.Update(JSTaggedValue(EcmaStringAccessor::FastSubString(
684                thread->GetEcmaVM(), JSHandle<EcmaString>::Cast(string), startIndex, len)));
685        } else {
686            // i. Let result be RegExpExec(rx, S).
687            result.Update(RegExpExec(thread, regexp, string, useCache));
688            // ii. ReturnIfAbrupt(result).
689            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
690            // iii. If result is null, then
691            if (result->IsNull()) {
692                // 1. If n=0, return null.
693                if (resultNum == 0) {
694                    return JSTaggedValue::Null();
695                }
696                // 2. Else, return A.
697                return array.GetTaggedValue();
698            }
699            // iv. Else result is not null,
700            // 1. Let matchStr be ToString(Get(result, "0")).
701            JSHandle<JSTaggedValue> zeroString = thread->GlobalConstants()->GetHandledZeroString();
702            JSTaggedValue matchVal = ObjectFastOperator::FastGetPropertyByValue(
703                thread, result.GetTaggedValue(), zeroString.GetTaggedValue());
704            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
705            JSHandle<JSTaggedValue> matchStr(thread, matchVal);
706            matchString.Update(JSTaggedValue::ToString(thread, matchStr));
707            // 2. ReturnIfAbrupt(matchStr).
708            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
709        }
710        JSHandle<JSTaggedValue> matchValue = JSHandle<JSTaggedValue>::Cast(matchString);
711        // 3. Let status be CreateDataProperty(A, ToString(n), matchStr).
712        if (arrLen > elements->GetLength()) {
713                elements.Update(JSObject::GrowElementsCapacity(thread,
714                    JSHandle<JSObject>::Cast(array), elements->GetLength(), true));
715        }
716        elements->Set(thread, arrLen - 1, matchValue);
717        array->SetArrayLength(thread, arrLen);
718        arrLen++;
719        // 5. If matchStr is the empty String, then
720        if (EcmaStringAccessor(matchString).GetLength() == 0) {
721            int64_t lastIndex = GetLastIndex(thread, regexp, isFastPath);
722            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
723            // c. Let nextIndex be AdvanceStringIndex(S, thisIndex, fullUnicode).
724            // d. Let setStatus be Set(rx, "lastIndex", nextIndex, true).
725            JSTaggedValue nextIndex = JSTaggedValue(AdvanceStringIndex(string, lastIndex, fullUnicode));
726            SetLastIndex(thread, regexp, nextIndex, isFastPath);
727            // e. ReturnIfAbrupt(setStatus).
728            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
729        }
730        // 6. Increase n.
731        resultNum++;
732    }
733}
734
735JSTaggedValue BuiltinsRegExp::MatchAll(EcmaRuntimeCallInfo *argv)
736{
737    ASSERT(argv);
738    JSThread *thread = argv->GetThread();
739    BUILTINS_API_TRACE(thread, RegExp, MatchAll);
740    [[maybe_unused]] EcmaHandleScope handleScope(thread);
741
742    // 1. Let R be the this value.
743    // 2. If Type(R) is not Object, throw a TypeError exception.
744    JSHandle<JSTaggedValue> thisObj = GetThis(argv);
745    if (!thisObj->IsECMAObject()) {
746        THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
747    }
748
749    // 3. Let S be ? ToString(string).
750    JSHandle<JSTaggedValue> inputString = GetCallArg(argv, 0);
751    JSHandle<EcmaString> stringHandle = JSTaggedValue::ToString(thread, inputString);
752    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
753    bool isFastPath = IsFastRegExp(thread, thisObj);
754    return RegExpMatchAll(thread, thisObj, stringHandle, isFastPath);
755}
756
757JSTaggedValue BuiltinsRegExp::RegExpMatchAll(JSThread *thread, const JSHandle<JSTaggedValue> regexp,
758                                             const JSHandle<EcmaString> string, bool isFastPath)
759{
760    JSMutableHandle<JSTaggedValue> matcher(thread, JSTaggedValue::Undefined());
761    bool global = false;
762    bool fullUnicode = false;
763    if (isFastPath) {
764        JSHandle<JSRegExp> jsRegExp = JSHandle<JSRegExp>::Cast(regexp);
765        JSHandle<JSTaggedValue> pattern(thread, jsRegExp->GetOriginalSource());
766        JSHandle<JSTaggedValue> flags(thread, jsRegExp->GetOriginalFlags());
767        matcher.Update(BuiltinsRegExp::RegExpCreate(thread, pattern, flags));
768        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
769        SetLastIndex(thread, matcher,
770            JSHandle<JSObject>::Cast(jsRegExp)->GetPropertyInlinedProps(LAST_INDEX_OFFSET), isFastPath);
771        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
772        global = GetOriginalFlag(thread, matcher, RegExpParser::FLAG_GLOBAL);
773        fullUnicode = GetOriginalFlag(thread, matcher, RegExpParser::FLAG_UTF16);
774    } else {
775        auto ecmaVm = thread->GetEcmaVM();
776        // 4. Let C be ? SpeciesConstructor(R, %RegExp%).
777        JSHandle<JSTaggedValue> defaultConstructor = ecmaVm->GetGlobalEnv()->GetRegExpFunction();
778        JSHandle<JSObject> objHandle(regexp);
779        JSHandle<JSTaggedValue> constructor = JSObject::SpeciesConstructor(thread, objHandle, defaultConstructor);
780        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
781
782        const GlobalEnvConstants *globalConstants = thread->GlobalConstants();
783        // 5. Let flags be ? ToString(? Get(R, "flags")).
784        JSHandle<JSTaggedValue> flagsString(globalConstants->GetHandledFlagsString());
785        JSHandle<JSTaggedValue> getFlags(JSObject::GetProperty(thread, regexp, flagsString).GetValue());
786        JSHandle<EcmaString> flagsStrHandle = JSTaggedValue::ToString(thread, getFlags);
787        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
788
789        // 6. Let matcher be ? Construct(C, « R, flags »).
790        JSHandle<JSTaggedValue> undefined = globalConstants->GetHandledUndefined();
791        EcmaRuntimeCallInfo *runtimeInfo =
792            EcmaInterpreter::NewRuntimeCallInfo(thread, constructor, undefined, undefined, 2); // 2: two args
793        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
794        runtimeInfo->SetCallArg(regexp.GetTaggedValue(), flagsStrHandle.GetTaggedValue());
795        JSTaggedValue taggedMatcher = JSFunction::Construct(runtimeInfo);
796        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
797        matcher.Update(taggedMatcher);
798
799        // 7. Let lastIndex be ? ToLength(? Get(R, "lastIndex")).
800        JSHandle<JSTaggedValue> lastIndexString(globalConstants->GetHandledLastIndexString());
801        JSHandle<JSTaggedValue> getLastIndex(JSObject::GetProperty(thread, regexp, lastIndexString).GetValue());
802        JSTaggedNumber thisLastIndex = JSTaggedValue::ToLength(thread, getLastIndex);
803        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
804
805        // 8. Perform ? Set(matcher, "lastIndex", lastIndex, true).
806        ObjectFastOperator::FastSetPropertyByValue(thread, matcher.GetTaggedValue(), lastIndexString.GetTaggedValue(),
807                                                   thisLastIndex);
808        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
809
810        // 9. If flags contains "g", let global be true.
811        // 10. Else, let global be false.
812        JSHandle<EcmaString> gString(globalConstants->GetHandledGString());
813        if (EcmaStringAccessor::IndexOf(ecmaVm, flagsStrHandle, gString) != -1) {
814            global = true;
815        }
816
817        // 11. If flags contains "u", let fullUnicode be true.
818        // 12. Else, let fullUnicode be false.
819        JSHandle<EcmaString> uString(globalConstants->GetHandledUString());
820        if (EcmaStringAccessor::IndexOf(ecmaVm, flagsStrHandle, uString) != -1) {
821            fullUnicode = true;
822        }
823    }
824    // 13. Return ! CreateRegExpStringIterator(matcher, S, global, fullUnicode).
825    return JSRegExpIterator::CreateRegExpStringIterator(thread, matcher,
826                                                        string, global, fullUnicode).GetTaggedValue();
827}
828
829JSTaggedValue BuiltinsRegExp::RegExpReplaceFast(JSThread *thread, JSHandle<JSTaggedValue> regexp,
830                                                JSHandle<EcmaString> inputString, uint32_t inputLength)
831{
832    ASSERT(regexp->IsJSRegExp());
833    BUILTINS_API_TRACE(thread, RegExp, RegExpReplaceFast);
834    JSHandle<JSRegExp> regexpHandle(regexp);
835    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
836    // get bytecode
837    JSTaggedValue bufferData = JSRegExp::Cast(regexp->GetTaggedObject())->GetByteCodeBuffer();
838    void *dynBuf = JSNativePointer::Cast(bufferData.GetTaggedObject())->GetExternalPointer();
839    // get flags
840    auto bytecodeBuffer = reinterpret_cast<uint8_t *>(dynBuf);
841    uint32_t flags = *reinterpret_cast<uint32_t *>(bytecodeBuffer + RegExpParser::FLAGS_OFFSET);
842    JSHandle<RegExpExecResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetRegExpCache());
843    JSHandle<JSTaggedValue> tagInputString = JSHandle<JSTaggedValue>::Cast(inputString);
844    bool useCache = false;
845    uint32_t lastIndex = 0;
846    GetLastIndex(thread, regexp, lastIndex);
847    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
848
849    auto globalConst = thread->GlobalConstants();
850    JSHandle<JSTaggedValue> pattern(thread, regexpHandle->GetOriginalSource());
851    JSHandle<JSTaggedValue> flagsBits(thread, regexpHandle->GetOriginalFlags());
852    useCache = ShouldUseCache(thread, inputString);
853    uint32_t lastIndexInput = lastIndex;
854    JSHandle<JSTaggedValue> emptyString(thread, globalConst->GetEmptyString());
855    if (useCache) {
856        JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, tagInputString,
857                                                                 RegExpExecResultCache::REPLACE_TYPE, regexp,
858                                                                 JSTaggedValue(lastIndexInput),
859                                                                 emptyString);
860        if (!cacheResult.IsUndefined()) {
861            return cacheResult;
862        }
863    }
864
865    std::string resultString;
866    MatchAndReplace(thread, regexp, inputString, flags,
867                    lastIndex, inputLength, resultString);
868    auto resultValue = factory->NewFromStdString(resultString);
869    if (useCache) {
870        RegExpExecResultCache::AddResultInCache(thread, cacheTable, regexp, tagInputString,
871                                                JSHandle<JSTaggedValue>(resultValue),
872                                                RegExpExecResultCache::REPLACE_TYPE, lastIndexInput, lastIndex,
873                                                emptyString);
874    }
875    return resultValue.GetTaggedValue();
876}
877
878JSTaggedValue BuiltinsRegExp::GetLastIndex(JSThread *thread, JSHandle<JSTaggedValue> regexp,
879                                           uint32_t &lastIndex)
880{
881    JSTaggedValue bufferData = JSRegExp::Cast(regexp->GetTaggedObject())->GetByteCodeBuffer();
882    void *dynBuf = JSNativePointer::Cast(bufferData.GetTaggedObject())->GetExternalPointer();
883    auto bytecodeBuffer = reinterpret_cast<uint8_t *>(dynBuf);
884    uint32_t flags = *reinterpret_cast<uint32_t *>(bytecodeBuffer + RegExpParser::FLAGS_OFFSET);
885    JSHandle<JSTaggedValue> lastIndexHandle(thread->GlobalConstants()->GetHandledLastIndexString());
886    if ((flags & (RegExpParser::FLAG_STICKY | RegExpParser::FLAG_GLOBAL)) == 0) {
887        lastIndex = 0;
888    } else {
889        JSTaggedValue thisIndex =
890            ObjectFastOperator::FastGetPropertyByValue(thread, regexp.GetTaggedValue(),
891                                                       lastIndexHandle.GetTaggedValue());
892            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
893        if (thisIndex.IsInt()) {
894            lastIndex = static_cast<uint32_t>(thisIndex.GetInt());
895        } else {
896            JSHandle<JSTaggedValue> thisIndexHandle(thread, thisIndex);
897            auto lengthValue = JSTaggedValue::ToLength(thread, thisIndexHandle);
898            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
899            lastIndex = lengthValue.GetNumber();
900        }
901    }
902    return JSTaggedValue::Undefined();
903}
904
905bool BuiltinsRegExp::ShouldUseCache(JSThread *thread, JSHandle<EcmaString> inputString)
906{
907    JSHandle<RegExpExecResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetRegExpCache());
908    uint32_t length = EcmaStringAccessor(inputString).GetLength();
909    uint32_t largeStrCount = cacheTable->GetLargeStrCount();
910    if (largeStrCount != 0) {
911        if (length > MIN_REPLACE_STRING_LENGTH) {
912            cacheTable->SetLargeStrCount(thread, --largeStrCount);
913        }
914    } else {
915        cacheTable->SetStrLenThreshold(thread, MIN_REPLACE_STRING_LENGTH);
916    }
917    return length > cacheTable->GetStrLenThreshold();
918}
919
920JSTaggedValue BuiltinsRegExp::MatchAndReplace(JSThread *thread, JSHandle<JSTaggedValue> regexp,
921                                              JSHandle<EcmaString> inputString, uint32_t &flags,
922                                              uint32_t lastIndex, uint32_t inputLength,
923                                              std::string &resultString)
924{
925    uint32_t nextPosition = 0;
926    JSHandle<RegExpGlobalResult> globalTable(thread->GetCurrentEcmaContext()->GetRegExpGlobalResult());
927    JSHandle<JSTaggedValue> lastIndexHandle(thread->GlobalConstants()->GetHandledLastIndexString());
928    JSHandle<JSTaggedValue> tagInputString = JSHandle<JSTaggedValue>::Cast(inputString);
929
930    // 12. Let done be false.
931    // 13. Repeat, while done is false
932    for (;;) {
933        if (lastIndex > inputLength) {
934            break;
935        }
936        bool matchResult = RegExpExecInternal(thread, regexp, inputString, lastIndex);
937        if (!matchResult) {
938            if (flags & (RegExpParser::FLAG_STICKY | RegExpParser::FLAG_GLOBAL)) {
939                lastIndex = 0;
940                ObjectFastOperator::FastSetPropertyByValue(thread, regexp.GetTaggedValue(),
941                                                           lastIndexHandle.GetTaggedValue(), JSTaggedValue(0));
942                RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
943            }
944            break;
945        }
946        uint32_t startIndex = static_cast<uint32_t>(globalTable->GetStartOfCaptureIndex(0).GetInt());
947        uint32_t endIndex = static_cast<uint32_t>(globalTable->GetEndIndex().GetInt());
948        lastIndex = endIndex;
949        if (nextPosition < startIndex) {
950            auto substr = EcmaStringAccessor::FastSubString(
951                thread->GetEcmaVM(), inputString, nextPosition, startIndex - nextPosition);
952            resultString += EcmaStringAccessor(substr).ToStdString(StringConvertedUsage::LOGICOPERATION);
953        }
954        nextPosition = endIndex;
955        if (!(flags & RegExpParser::FLAG_GLOBAL)) {
956            // a. Let setStatus be Set(R, "lastIndex", e, true).
957            ObjectFastOperator::FastSetPropertyByValue(thread, regexp.GetTaggedValue(),
958                                                       lastIndexHandle.GetTaggedValue(),
959                                                       JSTaggedValue(lastIndex));
960            // b. ReturnIfAbrupt(setStatus).
961            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
962            break;
963        }
964        if (endIndex == startIndex) {
965            bool unicode = EcmaStringAccessor(inputString).IsUtf16() && (flags & RegExpParser::FLAG_UTF16);
966            endIndex = static_cast<uint32_t>(AdvanceStringIndex(tagInputString, endIndex, unicode));
967        }
968        lastIndex = endIndex;
969    }
970    auto substr = EcmaStringAccessor::FastSubString(
971        thread->GetEcmaVM(), inputString, nextPosition, inputLength - nextPosition);
972    resultString += EcmaStringAccessor(substr).ToStdString(StringConvertedUsage::LOGICOPERATION);
973    return JSTaggedValue::Undefined();
974}
975
976// 21.2.5.8
977// NOLINTNEXTLINE(readability-function-size)
978JSTaggedValue BuiltinsRegExp::Replace(EcmaRuntimeCallInfo *argv)
979{
980    ASSERT(argv);
981    BUILTINS_API_TRACE(argv->GetThread(), RegExp, Replace);
982    JSThread *thread = argv->GetThread();
983    [[maybe_unused]] EcmaHandleScope handleScope(thread);
984    // 1. Let rx be the this value.
985    JSHandle<JSTaggedValue> thisObj = GetThis(argv);
986    if (!thisObj->IsECMAObject()) {
987        // 2. If Type(rx) is not Object, throw a TypeError exception.
988        THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
989    }
990    // 3. Let S be ToString(string).
991    JSHandle<JSTaggedValue> string = GetCallArg(argv, 0);
992    JSHandle<JSTaggedValue> inputReplaceValue = GetCallArg(argv, 1);
993    return ReplaceInternal(thread, thisObj, string, inputReplaceValue);
994}
995
996JSTaggedValue BuiltinsRegExp::ReplaceInternal(JSThread *thread,
997                                              JSHandle<JSTaggedValue> thisObj,
998                                              JSHandle<JSTaggedValue> string,
999                                              JSHandle<JSTaggedValue> inputReplaceValue)
1000{
1001    JSHandle<EcmaString> srcString = JSTaggedValue::ToString(thread, string);
1002    const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1003
1004    // 4. ReturnIfAbrupt(S).
1005    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1006    JSHandle<JSTaggedValue> inputStr = JSHandle<JSTaggedValue>::Cast(srcString);
1007    // 5. Let lengthS be the number of code unit elements in S.
1008    uint32_t length = EcmaStringAccessor(srcString).GetLength();
1009    // 6. Let functionalReplace be IsCallable(replaceValue).
1010    bool functionalReplace = inputReplaceValue->IsCallable();
1011    JSHandle<EcmaString> replaceValueHandle;
1012    // Add cache for regexp replace
1013    bool useCache = true;
1014    if (!functionalReplace) {
1015        replaceValueHandle = JSTaggedValue::ToString(thread, inputReplaceValue);
1016        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1017    } else {
1018        useCache = false;
1019    }
1020    // 8. Let global be ToBoolean(Get(rx, "global")).
1021    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1022    bool isGlobal = false;
1023    bool fullUnicode = false;
1024    bool isFastPath = IsFastRegExp(thread, thisObj);
1025    if (isFastPath) {
1026        isGlobal = GetOriginalFlag(thread, thisObj, RegExpParser::FLAG_GLOBAL);
1027        fullUnicode = GetOriginalFlag(thread, thisObj, RegExpParser::FLAG_UTF16);
1028        if (isGlobal) {
1029            SetLastIndex(thread, thisObj, JSTaggedValue(0), isFastPath);
1030        }
1031    } else {
1032        // 9. ReturnIfAbrupt(global).
1033        useCache = false;
1034        isGlobal = GetFlag(thread, thisObj, RegExpParser::FLAG_GLOBAL, isFastPath);
1035        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1036        // 10. If global is true, then
1037        if (isGlobal) {
1038            // a. Let fullUnicode be ToBoolean(Get(rx, "unicode")).
1039            fullUnicode = GetFlag(thread, thisObj, RegExpParser::FLAG_UTF16, isFastPath);
1040            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1041            // b. Let setStatus be Set(rx, "lastIndex", 0, true).
1042            SetLastIndex(thread, thisObj, JSTaggedValue(0), isFastPath);
1043            // c. ReturnIfAbrupt(setStatus).
1044            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1045        }
1046    }
1047
1048    // Add cache for the intermediate result of replace
1049    bool useIntermediateCache = false;
1050    JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1051    JSHandle<RegExpExecResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetRegExpCache());
1052    if (isFastPath) {
1053        if (isGlobal && !functionalReplace && EcmaStringAccessor(replaceValueHandle).GetLength() == 0) {
1054            return RegExpReplaceFast(thread, thisObj, srcString, length);
1055        }
1056        JSHandle<JSRegExp> regexpHandle(thisObj);
1057        useIntermediateCache = true;
1058        if (!functionalReplace) {
1059            uint32_t strLength = EcmaStringAccessor(replaceValueHandle).GetLength();
1060            uint32_t largeStrCount = cacheTable->GetLargeStrCount();
1061            if (largeStrCount != 0) {
1062                if (strLength > MIN_REPLACE_STRING_LENGTH) {
1063                    cacheTable->SetLargeStrCount(thread, --largeStrCount);
1064                }
1065            } else {
1066                cacheTable->SetStrLenThreshold(thread, MIN_REPLACE_STRING_LENGTH);
1067            }
1068            if (strLength > cacheTable->GetStrLenThreshold()) {
1069                uint32_t lastIndexInput = static_cast<uint32_t>(GetLastIndex(thread, thisObj, isFastPath));
1070                JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, string,
1071                    RegExpExecResultCache::REPLACE_TYPE, thisObj, JSTaggedValue(lastIndexInput),
1072                    inputReplaceValue);
1073                if (!cacheResult.IsUndefined()) {
1074                    return cacheResult;
1075                }
1076            }
1077        }
1078    }
1079
1080    // 11. Let results be a new empty List.
1081    JSMutableHandle<JSObject> resultsList(thread, JSArray::ArrayCreate(thread, JSTaggedNumber(0)));
1082    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1083    int resultsIndex = 0;
1084    JSMutableHandle<JSTaggedValue> nextIndexHandle(thread, JSTaggedValue(0));
1085    JSMutableHandle<JSTaggedValue> execResult(thread, JSTaggedValue(0));
1086    // Add cache for the intermediate result of replace
1087    JSTaggedValue cachedResultsList(JSTaggedValue::VALUE_UNDEFINED);
1088    if (useIntermediateCache) {
1089        uint32_t lastIndexInput = static_cast<uint32_t>(GetLastIndex(thread, thisObj, isFastPath));
1090        cachedResultsList = cacheTable->FindCachedResult(thread, string,
1091            RegExpExecResultCache::INTERMEDIATE_REPLACE_TYPE, thisObj,
1092            JSTaggedValue(lastIndexInput), undefined, true);
1093    }
1094    if (!cachedResultsList.IsUndefined()) {
1095        resultsList.Update(cachedResultsList);
1096        resultsIndex = static_cast<int>(JSArray::Cast(resultsList.GetTaggedValue())->GetArrayLength());
1097    } else {
1098        // 12. Let done be false.
1099        // 13. Repeat, while done is false
1100        for (;;) {
1101            // a. Let result be RegExpExec(rx, S).
1102            execResult.Update(RegExpExec(thread, thisObj, inputStr, useCache, true));
1103            // b. ReturnIfAbrupt(result).
1104            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1105            // c. If result is null, set done to true.
1106            if (execResult->IsNull()) {
1107                break;
1108            }
1109            // d. Else result is not null, i. Append result to the end of results.
1110            TaggedArray *srcElements = TaggedArray::Cast(resultsList->GetElements().GetTaggedObject());
1111            JSMutableHandle<TaggedArray> elements(thread, srcElements);
1112            if (resultsIndex >= static_cast<int>(elements->GetLength())) {
1113                elements.Update(JSObject::GrowElementsCapacity(thread, resultsList, elements->GetLength(), true));
1114            }
1115            elements->Set(thread, resultsIndex, execResult);
1116            JSArray::Cast(*resultsList)->SetArrayLength(thread, resultsIndex + 1);
1117            resultsIndex++;
1118            // ii. If global is false, set done to true.
1119            if (!isGlobal) {
1120                break;
1121            }
1122            // iii. Else, 1. Let matchStr be ToString(Get(result, "0")).
1123            JSHandle<JSTaggedValue> matchedStr = globalConst->GetHandledZeroString();
1124            JSTaggedValue getMatchVal = ObjectFastOperator::FastGetPropertyByValue(
1125                thread, execResult.GetTaggedValue(), matchedStr.GetTaggedValue());
1126            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1127            JSHandle<JSTaggedValue> getMatch(thread, getMatchVal);
1128            JSHandle<EcmaString> matchString = JSTaggedValue::ToString(thread, getMatch);
1129            // 2. ReturnIfAbrupt(matchStr).
1130            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1131            // 3. If matchStr is the empty String, then
1132            if (EcmaStringAccessor(matchString).GetLength() == 0) {
1133                // a. Let thisIndex be ToLength(Get(rx, "lastIndex")).
1134                uint32_t thisIndex = static_cast<uint32_t>(GetLastIndex(thread, thisObj, isFastPath));
1135                RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1136                // c. Let nextIndex be AdvanceStringIndex(S, thisIndex, fullUnicode).
1137                uint32_t nextIndex = static_cast<uint32_t>(AdvanceStringIndex(inputStr, thisIndex, fullUnicode));
1138                nextIndexHandle.Update(JSTaggedValue(nextIndex));
1139                // d. Let setStatus be Set(rx, "lastIndex", nextIndex, true).
1140                SetLastIndex(thread, thisObj, nextIndexHandle.GetTaggedValue(), isFastPath);
1141                // e. ReturnIfAbrupt(setStatus).
1142                RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1143            }
1144        }
1145        if (useIntermediateCache) {
1146            RegExpExecResultCache::AddResultInCache(thread, cacheTable, thisObj, string,
1147                                                    JSHandle<JSTaggedValue>(resultsList),
1148                                                    RegExpExecResultCache::INTERMEDIATE_REPLACE_TYPE, 0, 0,
1149                                                    undefined, true);
1150        }
1151    }
1152    // 14. Let accumulatedResult be the empty String value.
1153    bool isUtf8 = true;
1154    uint32_t resultStrLength = 0;
1155    uint32_t resultArrayLength = (static_cast<uint32_t>(resultsIndex) + 1) * 2;
1156    JSHandle<TaggedArray> resultArray = factory->NewTaggedArray(resultArrayLength);
1157    std::vector<uint64_t> resultLengthArray(resultArrayLength);
1158    // 15. Let nextSourcePosition be 0.
1159    uint32_t nextSourcePosition = 0;
1160    JSMutableHandle<JSTaggedValue> getMatchString(thread, JSTaggedValue::Undefined());
1161    JSMutableHandle<JSTaggedValue> resultValues(thread, JSTaggedValue(0));
1162    JSMutableHandle<JSTaggedValue> ncapturesHandle(thread, JSTaggedValue(0));
1163    JSMutableHandle<JSTaggedValue> capN(thread, JSTaggedValue(0));
1164    // 16. Repeat, for each result in results,
1165    for (int i = 0; i < resultsIndex; i++) {
1166        resultValues.Update(ElementAccessor::Get(resultsList, i));
1167        // a. Let nCaptures be ToLength(Get(result, "length")).
1168        uint32_t ncaptures;
1169        if (isFastPath) {
1170            ncaptures = static_cast<uint32_t>(JSArray::Cast(resultValues.GetTaggedValue())->GetArrayLength());
1171        } else {
1172            JSHandle<JSTaggedValue> lengthHandle = globalConst->GetHandledLengthString();
1173            ncapturesHandle.Update(ObjectFastOperator::FastGetPropertyByValue(
1174                thread, resultValues.GetTaggedValue(), lengthHandle.GetTaggedValue()));
1175            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1176            ncaptures = JSTaggedValue::ToUint32(thread, ncapturesHandle);
1177        }
1178        // b. ReturnIfAbrupt(nCaptures).
1179        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1180        // c. Let nCaptures be max(nCaptures − 1, 0).
1181        ncaptures = (ncaptures == 0) ? 0 : ncaptures - 1;
1182        // d. Let matched be ToString(Get(result, "0")).
1183        JSTaggedValue value = ObjectFastOperator::GetPropertyByIndex(thread, resultValues.GetTaggedValue(), 0);
1184        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1185        getMatchString.Update(value);
1186        JSHandle<EcmaString> matchString = JSTaggedValue::ToString(thread, getMatchString);
1187        // e. ReturnIfAbrupt(matched).
1188        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1189        // f. Let matchLength be the number of code units in matched.
1190        uint32_t matchLength = EcmaStringAccessor(matchString).GetLength();
1191        // g. Let position be ToInteger(Get(result, "index")).
1192        JSTaggedValue positionTag = GetExecResultIndex(thread, resultValues, isFastPath);
1193        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1194        JSHandle<JSTaggedValue> positionHandle(thread, positionTag);
1195        uint32_t position = 0;
1196        if (positionHandle->IsInt()) {
1197            position = static_cast<uint32_t>(positionHandle->GetInt());
1198        } else {
1199            position = JSTaggedValue::ToUint32(thread, positionHandle);
1200            // h. ReturnIfAbrupt(position).
1201            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1202        }
1203        // i. Let position be max(min(position, lengthS), 0).
1204        position = std::max<uint32_t>(std::min<uint32_t>(position, length), 0);
1205        // j. Let n be 1.
1206        uint32_t index = 1;
1207        // k. Let captures be an empty List.
1208        JSHandle<TaggedArray> capturesList = factory->NewTaggedArray(ncaptures);
1209        // l. Repeat while n ≤ nCaptures
1210        while (index <= ncaptures) {
1211            // i. Let capN be Get(result, ToString(n)).
1212            capN.Update(ObjectFastOperator::FastGetPropertyByIndex(thread, resultValues.GetTaggedValue(), index));
1213            // ii. ReturnIfAbrupt(capN).
1214            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1215            // iii. If capN is not undefined, then
1216            if (!capN->IsUndefined()) {
1217                // 1. Let capN be ToString(capN).
1218                JSHandle<EcmaString> capNStr = JSTaggedValue::ToString(thread, capN);
1219                // 2. ReturnIfAbrupt(capN).
1220                RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1221                JSHandle<JSTaggedValue> capnStr = JSHandle<JSTaggedValue>::Cast(capNStr);
1222                capturesList->Set(thread, index - 1, capnStr);
1223            } else {
1224                // iv. Append capN as the last element of captures.
1225                capturesList->Set(thread, index - 1, capN);
1226            }
1227            // v. Let n be n+1
1228            ++index;
1229        }
1230
1231        // j. Let namedCaptures be ? Get(result, "groups").
1232        JSTaggedValue named = GetExecResultGroups(thread, resultValues, isFastPath);
1233        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1234        JSHandle<JSTaggedValue> namedCaptures(thread, named);
1235        // m. If functionalReplace is true, then
1236        JSMutableHandle<EcmaString> replacementString(thread, factory->GetEmptyString());
1237        int emptyArrLength = 0;
1238        if (namedCaptures->IsUndefined()) {
1239            emptyArrLength = 3; // 3: «matched, pos, and string»
1240        } else {
1241            emptyArrLength = 4; // 4: «matched, pos, string, and groups»
1242        }
1243        JSHandle<TaggedArray> replacerArgs =
1244            factory->NewTaggedArray(emptyArrLength + capturesList->GetLength());
1245        if (functionalReplace) {
1246            // i. Let replacerArgs be «matched».
1247            replacerArgs->Set(thread, 0, getMatchString.GetTaggedValue());
1248            // ii. Append in list order the elements of captures to the end of the List replacerArgs.
1249            // iii. Append position and S as the last two elements of replacerArgs.
1250            index = 0;
1251            while (index < capturesList->GetLength()) {
1252                replacerArgs->Set(thread, index + 1, capturesList->Get(index));
1253                ++index;
1254            }
1255            replacerArgs->Set(thread, index + 1, JSTaggedValue(position));
1256            replacerArgs->Set(thread, index + 2, inputStr.GetTaggedValue());  // 2: position of string
1257            if (!namedCaptures->IsUndefined()) {
1258                replacerArgs->Set(thread, index + 3, namedCaptures.GetTaggedValue()); // 3: position of groups
1259            }
1260            // iv. Let replValue be Call(replaceValue, undefined, replacerArgs).
1261            const uint32_t argsLength = replacerArgs->GetLength();
1262            EcmaRuntimeCallInfo *info =
1263                EcmaInterpreter::NewRuntimeCallInfo(thread, inputReplaceValue, undefined, undefined, argsLength);
1264            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1265            info->SetCallArg(argsLength, replacerArgs);
1266            JSTaggedValue replaceResult = JSFunction::Call(info);
1267            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1268            JSHandle<JSTaggedValue> replValue(thread, replaceResult);
1269            // v. Let replacement be ToString(replValue).
1270            replacementString.Update(JSTaggedValue::ToString(thread, replValue));
1271            // o. ReturnIfAbrupt(replacement).
1272            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1273        } else {
1274            // n. Else,
1275            if (!namedCaptures->IsUndefined()) {
1276                JSHandle<JSObject> namedCapturesObj = JSTaggedValue::ToObject(thread, namedCaptures);
1277                RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1278                namedCaptures = JSHandle<JSTaggedValue>::Cast(namedCapturesObj);
1279            }
1280            replacementString.Update(BuiltinsString::GetSubstitution(thread, matchString, srcString,
1281                position, capturesList, namedCaptures, replaceValueHandle));
1282        }
1283        // p. If position ≥ nextSourcePosition, then
1284        if (position >= nextSourcePosition) {
1285            // ii. Let accumulatedResult be the String formed by concatenating the code units of the current value
1286            // of accumulatedResult with the substring of S consisting of the code units from nextSourcePosition
1287            // (inclusive) up to position (exclusive) and with the code units of replacement.
1288            // store undefined in resultArray
1289            resultArray->Set(thread, REPLACE_RESULT_VAL * i, JSTaggedValue::Undefined());
1290            uint64_t bits = 0;
1291            bits |= ReplaceLengthField::Encode(position - nextSourcePosition);
1292            bits |= ReplacePositionField::Encode(nextSourcePosition);
1293            // store position and length bits in resultLengthArray
1294            resultLengthArray[REPLACE_RESULT_VAL * i] = bits;
1295            resultStrLength += (position - nextSourcePosition);
1296            auto subString = EcmaStringAccessor::FastSubString(
1297                thread->GetEcmaVM(), srcString, nextSourcePosition, position - nextSourcePosition);
1298            isUtf8 &= EcmaStringAccessor(subString).IsUtf8();
1299            // store replacement string in resultArray
1300            resultArray->Set(thread, REPLACE_RESULT_VAL * i + 1, replacementString.GetTaggedValue());
1301            uint32_t replacementLength = EcmaStringAccessor(replacementString).GetLength();
1302            // store length of replacement string in resultLengthArray
1303            resultLengthArray[REPLACE_RESULT_VAL * i + 1] = static_cast<uint64_t>(replacementLength);
1304            resultStrLength += replacementLength;
1305            isUtf8 &= EcmaStringAccessor(replacementString).IsUtf8();
1306            // iii. Let nextSourcePosition be position + matchLength.
1307            nextSourcePosition = position + matchLength;
1308        }
1309    }
1310
1311    // 17. If nextSourcePosition ≥ lengthS, return accumulatedResult.
1312    if (nextSourcePosition < length) {
1313        // store undefined in resultArray
1314        resultArray->Set(thread, REPLACE_RESULT_VAL * resultsIndex, JSTaggedValue::Undefined());
1315        uint64_t bits = 0;
1316        bits |= ReplaceLengthField::Encode(length - nextSourcePosition);
1317        bits |= ReplacePositionField::Encode(nextSourcePosition);
1318        auto subStringEnd = EcmaStringAccessor::FastSubString(
1319            thread->GetEcmaVM(), srcString, nextSourcePosition, length - nextSourcePosition);
1320        isUtf8 &= EcmaStringAccessor(subStringEnd).IsUtf8();
1321        // store position and length bits in resultLengthArray
1322        resultLengthArray[REPLACE_RESULT_VAL * resultsIndex] = bits;
1323        resultStrLength += (length - nextSourcePosition);
1324    }
1325
1326    JSHandle<EcmaString> result =
1327        CreateStringFromResultArray(thread, resultArray, resultLengthArray, srcString, resultStrLength, isUtf8);
1328    // 18. Return the String formed by concatenating the code units of accumulatedResult with the substring of S
1329    // consisting of the code units from nextSourcePosition (inclusive) up through the final code unit of S(inclusive).
1330    if (useCache) {
1331        RegExpExecResultCache::AddResultInCache(thread, cacheTable, thisObj, string,
1332                                                JSHandle<JSTaggedValue>(result),
1333                                                RegExpExecResultCache::REPLACE_TYPE, 0, nextIndexHandle->GetInt(),
1334                                                inputReplaceValue);
1335    }
1336    return result.GetTaggedValue();
1337}
1338
1339// 21.2.5.9
1340JSTaggedValue BuiltinsRegExp::Search(EcmaRuntimeCallInfo *argv)
1341{
1342    ASSERT(argv);
1343    BUILTINS_API_TRACE(argv->GetThread(), RegExp, Search);
1344    JSThread *thread = argv->GetThread();
1345    [[maybe_unused]] EcmaHandleScope handleScope(thread);
1346    // 1. Let rx be the this value.
1347    JSHandle<JSTaggedValue> thisObj = GetThis(argv);
1348    // 3. Let S be ToString(string).
1349    JSHandle<JSTaggedValue> inputStr = GetCallArg(argv, 0);
1350    JSHandle<EcmaString> stringHandle = JSTaggedValue::ToString(thread, inputStr);
1351
1352    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1353    JSHandle<JSTaggedValue> string = JSHandle<JSTaggedValue>::Cast(stringHandle);
1354    if (!thisObj->IsECMAObject()) {
1355        // 2. If Type(rx) is not Object, throw a TypeError exception.
1356        THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
1357    }
1358    return RegExpSearch(thread, thisObj, string);
1359}
1360
1361JSTaggedValue BuiltinsRegExp::RegExpSearch(JSThread *thread,
1362                                           const JSHandle<JSTaggedValue> regexp,
1363                                           const JSHandle<JSTaggedValue> string)
1364{
1365    bool isFastPath = IsFastRegExp(thread, regexp);
1366    if (isFastPath) {
1367        return RegExpSearchFast(thread, regexp, string);
1368    }
1369    // 4. Let previousLastIndex be ? Get(rx, "lastIndex").
1370    JSHandle<JSTaggedValue> lastIndexString(thread->GlobalConstants()->GetHandledLastIndexString());
1371    JSHandle<JSTaggedValue> previousLastIndex = JSObject::GetProperty(thread, regexp, lastIndexString).GetValue();
1372    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1373    // 5. If SameValue(previousLastIndex, 0) is false, then
1374    // Perform ? Set(rx, "lastIndex", 0, true).
1375    if (!JSTaggedValue::SameValue(previousLastIndex.GetTaggedValue(), JSTaggedValue(0))) {
1376        JSHandle<JSTaggedValue> value(thread, JSTaggedValue(0));
1377        JSObject::SetProperty(thread, regexp, lastIndexString, value, true);
1378        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1379    }
1380    // 6. Let result be ? RegExpExec(rx, S).
1381    JSHandle<JSTaggedValue> result(thread, RegExpExec(thread, regexp, string, false));
1382    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1383    // 7. Let currentLastIndex be ? Get(rx, "lastIndex").
1384    JSHandle<JSTaggedValue> currentLastIndex = JSObject::GetProperty(thread, regexp, lastIndexString).GetValue();
1385    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1386    // 8. If SameValue(currentLastIndex, previousLastIndex) is false, then
1387    // Perform ? Set(rx, "lastIndex", previousLastIndex, true).
1388    if (!JSTaggedValue::SameValue(previousLastIndex.GetTaggedValue(), currentLastIndex.GetTaggedValue())) {
1389        JSObject::SetProperty(thread, regexp, lastIndexString, previousLastIndex, true);
1390        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1391    }
1392    // 9. If result is null, return -1.
1393    if (result->IsNull()) {
1394        return JSTaggedValue(-1);
1395    }
1396    // 10. Return ? Get(result, "index").
1397    JSHandle<JSTaggedValue> index(thread->GlobalConstants()->GetHandledIndexString());
1398    return JSObject::GetProperty(thread, result, index).GetValue().GetTaggedValue();
1399}
1400
1401JSTaggedValue BuiltinsRegExp::RegExpSearchFast(JSThread *thread,
1402                                               const JSHandle<JSTaggedValue> regexp,
1403                                               const JSHandle<JSTaggedValue> string)
1404{
1405    JSHandle<RegExpExecResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetRegExpCache());
1406    JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1407
1408    JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, string,
1409                                                             RegExpExecResultCache::SEARCH_TYPE, regexp,
1410                                                             JSTaggedValue(0), undefined);
1411    if (!cacheResult.IsUndefined()) {
1412        return cacheResult;
1413    }
1414    JSHandle<EcmaString> stringHandle = JSHandle<EcmaString>::Cast(string);
1415    bool matchResult = RegExpExecInternal(thread, regexp, stringHandle, 0);
1416    if (!matchResult) {
1417        RegExpExecResultCache::AddResultInCache(thread, cacheTable, regexp, string,
1418                                                JSHandle<JSTaggedValue>(thread, JSTaggedValue(-1)),
1419                                                RegExpExecResultCache::SEARCH_TYPE,
1420                                                0, 0, undefined);
1421        return JSTaggedValue(-1);
1422    }
1423    JSHandle<RegExpGlobalResult> globalTable(thread->GetCurrentEcmaContext()->GetRegExpGlobalResult());
1424    JSTaggedValue result = globalTable->GetStartOfCaptureIndex(0);
1425    RegExpExecResultCache::AddResultInCache(thread, cacheTable, regexp, string,
1426                                            JSHandle<JSTaggedValue>(thread, JSTaggedValue(result)),
1427                                            RegExpExecResultCache::SEARCH_TYPE,
1428                                            0, 0, undefined);
1429    return result;
1430}
1431
1432JSTaggedValue BuiltinsRegExp::RegExpSplit(JSThread *thread, const JSHandle<JSTaggedValue> regexp,
1433                                          JSHandle<JSTaggedValue> jsString, JSHandle<JSTaggedValue> limit,
1434                                          bool isFastPath)
1435{
1436    bool useCache = false;
1437    if (isFastPath) {
1438        if (limit->IsUndefined()) {
1439            useCache = true;
1440            return RegExpSplitFast(thread, regexp, jsString, MAX_SPLIT_LIMIT, useCache);
1441        } else if (limit->IsInt()) {
1442            int64_t lim = limit->GetInt();
1443            if (lim >= 0) {
1444                return RegExpSplitFast(thread, regexp, jsString, static_cast<uint32_t>(lim), useCache);
1445            }
1446        }
1447    }
1448    auto ecmaVm = thread->GetEcmaVM();
1449    // 5. Let C be SpeciesConstructor(rx, %RegExp%).
1450    JSHandle<JSTaggedValue> defaultConstructor = ecmaVm->GetGlobalEnv()->GetRegExpFunction();
1451    JSHandle<JSObject> objHandle(regexp);
1452    JSHandle<JSTaggedValue> constructor = JSObject::SpeciesConstructor(thread, objHandle, defaultConstructor);
1453    // 6. ReturnIfAbrupt(C).
1454    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1455    // 7. Let flags be ToString(Get(rx, "flags")).
1456    ObjectFactory *factory = ecmaVm->GetFactory();
1457    const GlobalEnvConstants *globalConstants = thread->GlobalConstants();
1458    JSHandle<JSTaggedValue> flagsString(globalConstants->GetHandledFlagsString());
1459    JSHandle<JSTaggedValue> taggedFlags = JSObject::GetProperty(thread, regexp, flagsString).GetValue();
1460    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1461    JSHandle<EcmaString> flags;
1462
1463    if (taggedFlags->IsUndefined()) {
1464        flags = factory->GetEmptyString();
1465    } else {
1466        flags = JSTaggedValue::ToString(thread, taggedFlags);
1467    }
1468    //  8. ReturnIfAbrupt(flags).
1469    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1470    // 9. If flags contains "u", let unicodeMatching be true.
1471    // 10. Else, let unicodeMatching be false.
1472    JSHandle<EcmaString> uStringHandle(globalConstants->GetHandledUString());
1473    bool unicodeMatching = (EcmaStringAccessor::IndexOf(ecmaVm, flags, uStringHandle) != -1);
1474    // 11. If flags contains "y", let newFlags be flags.
1475    JSHandle<EcmaString> newFlagsHandle;
1476    JSHandle<EcmaString> yStringHandle = JSHandle<EcmaString>::Cast(globalConstants->GetHandledYString());
1477    if (EcmaStringAccessor::IndexOf(ecmaVm, flags, yStringHandle) != -1) {
1478        newFlagsHandle = flags;
1479    } else {
1480        // 12. Else, let newFlags be the string that is the concatenation of flags and "y".
1481        JSHandle<EcmaString> yStr = JSHandle<EcmaString>::Cast(globalConstants->GetHandledYString());
1482        newFlagsHandle = factory->ConcatFromString(flags, yStr);
1483    }
1484
1485    // 13. Let splitter be Construct(C, «rx, newFlags»).
1486    JSHandle<JSObject> globalObject(thread, thread->GetEcmaVM()->GetGlobalEnv()->GetGlobalObject());
1487    JSHandle<JSTaggedValue> undefined = globalConstants->GetHandledUndefined();
1488    EcmaRuntimeCallInfo *runtimeInfo =
1489        EcmaInterpreter::NewRuntimeCallInfo(thread, constructor, undefined, undefined, 2); // 2: two args
1490    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1491    runtimeInfo->SetCallArg(regexp.GetTaggedValue(), newFlagsHandle.GetTaggedValue());
1492    JSTaggedValue taggedSplitter = JSFunction::Construct(runtimeInfo);
1493    // 14. ReturnIfAbrupt(splitter).
1494    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1495
1496    JSHandle<JSTaggedValue> splitter(thread, taggedSplitter);
1497    // 15. Let A be ArrayCreate(0).
1498    JSHandle<JSObject> array(JSArray::ArrayCreate(thread, JSTaggedNumber(0)));
1499    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1500    // 16. Let lengthA be 0.
1501    uint32_t aLength = 0;
1502
1503    // 17. If limit is undefined, let lim be 2^32–1; else let lim be ToUint32(limit).
1504    uint32_t lim;
1505    if (limit->IsUndefined()) {
1506        lim = MAX_SPLIT_LIMIT;
1507    } else {
1508        lim = JSTaggedValue::ToUint32(thread, limit);
1509        // 18. ReturnIfAbrupt(lim).
1510        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1511    }
1512
1513    // 19. Let size be the number of elements in S.
1514    uint32_t size = EcmaStringAccessor(jsString->GetTaggedObject()).GetLength();
1515    // 20. Let p be 0.
1516    uint32_t startIndex = 0;
1517    // 21. If lim = 0, return A.
1518    if (lim == 0) {
1519        return JSTaggedValue(static_cast<JSArray *>(array.GetTaggedValue().GetTaggedObject()));
1520    }
1521    // 22. If size = 0, then
1522    if (size == 0) {
1523        // a. Let z be RegExpExec(splitter, S).
1524        JSHandle<JSTaggedValue> execResult(thread, RegExpExec(thread, splitter, jsString, useCache));
1525        // b. ReturnIfAbrupt(z).
1526        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1527        // c. If z is not null, return A.
1528        if (!execResult->IsNull()) {
1529            return JSTaggedValue(static_cast<JSArray *>(array.GetTaggedValue().GetTaggedObject()));
1530        }
1531        // d. Assert: The following call will never result in an abrupt completion.
1532        // e. Perform CreateDataProperty(A, "0", S).
1533        JSObject::CreateDataProperty(thread, array, 0, jsString);
1534        // f. Return A.
1535        return JSTaggedValue(static_cast<JSArray *>(array.GetTaggedValue().GetTaggedObject()));
1536    }
1537    // 23. Let q be p.
1538    uint32_t endIndex = startIndex;
1539    JSMutableHandle<JSTaggedValue> lastIndexvalue(thread, JSTaggedValue(endIndex));
1540    // 24. Repeat, while q < size
1541    JSHandle<JSTaggedValue> lastIndexString = globalConstants->GetHandledLastIndexString();
1542    while (endIndex < size) {
1543        // a. Let setStatus be Set(splitter, "lastIndex", q, true).
1544        lastIndexvalue.Update(JSTaggedValue(endIndex));
1545        JSObject::SetProperty(thread, splitter, lastIndexString, lastIndexvalue, true);
1546        // b. ReturnIfAbrupt(setStatus).
1547        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1548        JSHandle<JSTaggedValue> execResult(thread, RegExpExec(thread, splitter, jsString, useCache));
1549        // d. ReturnIfAbrupt(z).
1550        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1551        // e. If z is null, let q be AdvanceStringIndex(S, q, unicodeMatching).
1552        if (execResult->IsNull()) {
1553            endIndex = static_cast<uint32_t>(AdvanceStringIndex(jsString, endIndex, unicodeMatching));
1554        } else {
1555            // f. Else z is not null,
1556            // i. Let e be ToLength(Get(splitter, "lastIndex")).
1557            JSHandle<JSTaggedValue> lastIndexHandle =
1558                JSObject::GetProperty(thread, splitter, lastIndexString).GetValue();
1559            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1560            JSTaggedNumber lastIndexNumber = JSTaggedValue::ToLength(thread, lastIndexHandle);
1561            // ii. ReturnIfAbrupt(e).
1562            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1563            uint32_t lastIndex = lastIndexNumber.GetNumber();
1564            // iii. If e = p, let q be AdvanceStringIndex(S, q, unicodeMatching).
1565            if (lastIndex == startIndex) {
1566                endIndex = static_cast<uint32_t>(AdvanceStringIndex(jsString, endIndex, unicodeMatching));
1567            } else {
1568                // iv. Else e != p,
1569                // 1. Let T be a String value equal to the substring of S consisting of the elements at indices p
1570                // (inclusive) through q (exclusive).
1571                auto substr = EcmaStringAccessor::FastSubString(thread->GetEcmaVM(),
1572                    JSHandle<EcmaString>::Cast(jsString), startIndex, endIndex - startIndex);
1573                std::string stdStrT = EcmaStringAccessor(substr).ToStdString(StringConvertedUsage::LOGICOPERATION);
1574                // 2. Assert: The following call will never result in an abrupt completion.
1575                // 3. Perform CreateDataProperty(A, ToString(lengthA), T).
1576                JSHandle<JSTaggedValue> tValue(factory->NewFromStdString(stdStrT));
1577                JSObject::CreateDataProperty(thread, array, aLength, tValue);
1578                // 4. Let lengthA be lengthA +1.
1579                ++aLength;
1580                // 5. If lengthA = lim, return A.
1581                if (aLength == lim) {
1582                    return array.GetTaggedValue();
1583                }
1584                // 6. Let p be e.
1585                startIndex = lastIndex;
1586                // 7. Let numberOfCaptures be ToLength(Get(z, "length")).
1587                JSHandle<JSTaggedValue> lengthString(thread->GlobalConstants()->GetHandledLengthString());
1588                JSHandle<JSTaggedValue> capturesHandle =
1589                    JSObject::GetProperty(thread, execResult, lengthString).GetValue();
1590                RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1591                JSTaggedNumber numberOfCapturesNumber = JSTaggedValue::ToLength(thread, capturesHandle);
1592                // 8. ReturnIfAbrupt(numberOfCaptures).
1593                RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1594                uint32_t numberOfCaptures = numberOfCapturesNumber.GetNumber();
1595                // 9. Let numberOfCaptures be max(numberOfCaptures-1, 0).
1596                numberOfCaptures = (numberOfCaptures == 0) ? 0 : numberOfCaptures - 1;
1597                // 10. Let i be 1.
1598                uint32_t i = 1;
1599                // 11. Repeat, while i ≤ numberOfCaptures.
1600                while (i <= numberOfCaptures) {
1601                    // a. Let nextCapture be Get(z, ToString(i)).
1602                    JSHandle<JSTaggedValue> nextCapture = JSObject::GetProperty(thread, execResult, i).GetValue();
1603                    // b. ReturnIfAbrupt(nextCapture).
1604                    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1605                    // c. Perform CreateDataProperty(A, ToString(lengthA), nextCapture).
1606                    JSObject::CreateDataProperty(thread, array, aLength, nextCapture);
1607                    // d. Let i be i + 1.
1608                    ++i;
1609                    // e. Let lengthA be lengthA +1.
1610                    ++aLength;
1611                    // f. If lengthA = lim, return A.
1612                    if (aLength == lim) {
1613                        return array.GetTaggedValue();
1614                    }
1615                }
1616                // 12. Let q be p.
1617                endIndex = startIndex;
1618            }
1619        }
1620    }
1621    // 25. Let T be a String value equal to the substring of S consisting of the elements at indices p (inclusive)
1622    // through size (exclusive).
1623    auto substr = EcmaStringAccessor::FastSubString(thread->GetEcmaVM(),
1624        JSHandle<EcmaString>::Cast(jsString), startIndex, size - startIndex);
1625    std::string stdStrT = EcmaStringAccessor(substr).ToStdString(StringConvertedUsage::LOGICOPERATION);
1626    // 26. Assert: The following call will never result in an abrupt completion.
1627    // 27. Perform CreateDataProperty(A, ToString(lengthA), t).
1628    JSHandle<JSTaggedValue> tValue(factory->NewFromStdString(stdStrT));
1629    JSObject::CreateDataProperty(thread, array, aLength, tValue);
1630    // 28. Return A.
1631    return array.GetTaggedValue();
1632}
1633// 21.2.5.11
1634// NOLINTNEXTLINE(readability-function-size)
1635JSTaggedValue BuiltinsRegExp::Split(EcmaRuntimeCallInfo *argv)
1636{
1637    ASSERT(argv);
1638    BUILTINS_API_TRACE(argv->GetThread(), RegExp, Split);
1639    JSThread *thread = argv->GetThread();
1640    [[maybe_unused]] EcmaHandleScope handleScope(thread);
1641    // 1. Let rx be the this value.
1642    JSHandle<JSTaggedValue> thisObj = GetThis(argv);
1643    // 3. Let S be ToString(string).
1644    JSHandle<JSTaggedValue> inputString = GetCallArg(argv, 0);
1645    JSHandle<JSTaggedValue> limit = GetCallArg(argv, 1);
1646    JSHandle<EcmaString> stringHandle = JSTaggedValue::ToString(thread, inputString);
1647
1648    // 4. ReturnIfAbrupt(string).
1649    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1650    JSHandle<JSTaggedValue> jsString = JSHandle<JSTaggedValue>::Cast(stringHandle);
1651    if (!thisObj->IsECMAObject()) {
1652        // 2. If Type(rx) is not Object, throw a TypeError exception.
1653        THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue::Exception());
1654    }
1655    bool isFastPath = IsFastRegExp(thread, thisObj);
1656    return RegExpSplit(thread, thisObj, jsString, limit, isFastPath);
1657}
1658
1659JSTaggedValue BuiltinsRegExp::RegExpSplitFast(JSThread *thread, const JSHandle<JSTaggedValue> regexp,
1660                                              JSHandle<JSTaggedValue> jsString, uint32_t limit, bool useCache)
1661{
1662    if (limit == 0) {
1663        return JSArray::ArrayCreate(thread, JSTaggedNumber(0), ArrayMode::LITERAL).GetTaggedValue();
1664    }
1665    JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1666    JSHandle<RegExpExecResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetRegExpCache());
1667    if (useCache) {
1668        JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, jsString,
1669                                                                 RegExpExecResultCache::SPLIT_TYPE, regexp,
1670                                                                 JSTaggedValue(0), undefined);
1671        if (!cacheResult.IsUndefined()) {
1672            return cacheResult;
1673        }
1674    }
1675    uint32_t size = EcmaStringAccessor(jsString->GetTaggedObject()).GetLength();
1676    JSHandle<EcmaString> string = JSHandle<EcmaString>::Cast(jsString);
1677
1678    if (size == 0) {
1679        bool matchResult = RegExpExecInternal(thread, regexp, string, 0); // 0: lastIndex
1680        if (matchResult) {
1681            JSHandle<JSTaggedValue> res = JSArray::ArrayCreate(thread, JSTaggedNumber(0), ArrayMode::LITERAL);
1682            if (useCache) {
1683                RegExpExecResultCache::AddResultInCache(thread, cacheTable, regexp, jsString,
1684                                                        res, RegExpExecResultCache::SPLIT_TYPE, 0, 0, undefined);
1685            }
1686            return res.GetTaggedValue();
1687        }
1688        JSHandle<TaggedArray> element = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(1); // 1: length
1689        element->Set(thread, 0, jsString);
1690        JSHandle<JSTaggedValue> res = JSHandle<JSTaggedValue>::Cast(JSArray::CreateArrayFromList(thread, element));
1691        if (useCache) {
1692            RegExpExecResultCache::AddResultInCache(thread, cacheTable, regexp, jsString,
1693                                                    res, RegExpExecResultCache::SPLIT_TYPE, 0, 0, undefined);
1694        }
1695        return res.GetTaggedValue();
1696    }
1697
1698    bool isUnicode = GetOriginalFlag(thread, regexp, RegExpParser::FLAG_UTF16);
1699    bool isSticky = GetOriginalFlag(thread, regexp, RegExpParser::FLAG_STICKY);
1700
1701    uint32_t nextMatchFrom = 0;
1702    uint32_t lastMatchEnd = 0;
1703    uint32_t arrLen = 1; // at least one result string
1704    JSHandle<JSArray> splitArray(JSArray::ArrayCreate(thread, JSTaggedNumber(1), ArrayMode::LITERAL));
1705    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1706    TaggedArray *srcElements = TaggedArray::Cast(splitArray->GetElements().GetTaggedObject());
1707    JSMutableHandle<TaggedArray> elements(thread, srcElements);
1708    JSMutableHandle<JSTaggedValue> matchValue(thread, JSTaggedValue::Undefined());
1709    while (nextMatchFrom < size) {
1710        bool matchResult = RegExpExecInternal(thread, regexp, string, nextMatchFrom);
1711        if (!matchResult) {
1712            if (!isSticky) {
1713                // done match
1714                break;
1715            }
1716            nextMatchFrom = static_cast<uint32_t>(AdvanceStringIndex(jsString, nextMatchFrom, isUnicode));
1717            continue;
1718        }
1719        // find match result
1720        JSHandle<RegExpGlobalResult> matchResultInfo(thread->GetCurrentEcmaContext()->GetRegExpGlobalResult());
1721        uint32_t matchStartIndex = static_cast<uint32_t>(matchResultInfo->GetStartOfCaptureIndex(0).GetInt());
1722        uint32_t matchEndIndex = static_cast<uint32_t>(matchResultInfo->GetEndOfCaptureIndex(0).GetInt());
1723        if (matchEndIndex == lastMatchEnd && matchEndIndex == nextMatchFrom) {
1724            // advance index and continue if match result is empty.
1725            nextMatchFrom = static_cast<uint32_t>(AdvanceStringIndex(jsString, nextMatchFrom, isUnicode));
1726        } else {
1727            matchValue.Update(JSTaggedValue(EcmaStringAccessor::FastSubString(thread->GetEcmaVM(),
1728                string, lastMatchEnd, matchStartIndex - lastMatchEnd)));
1729            if (arrLen > elements->GetLength()) {
1730                elements.Update(JSObject::GrowElementsCapacity(thread,
1731                    JSHandle<JSObject>::Cast(splitArray), elements->GetLength(), true));
1732            }
1733            elements->Set(thread, arrLen - 1, matchValue);
1734            splitArray->SetArrayLength(thread, arrLen);
1735            if (arrLen == limit) {
1736                if (useCache) {
1737                    RegExpExecResultCache::AddResultInCache(thread, cacheTable, regexp, jsString,
1738                                                            JSHandle<JSTaggedValue>(splitArray),
1739                                                            RegExpExecResultCache::SPLIT_TYPE, 0, 0, undefined);
1740                }
1741                return JSTaggedValue(splitArray.GetTaggedValue().GetTaggedObject());
1742            }
1743            arrLen++;
1744            uint32_t capturesSize = static_cast<uint32_t>(matchResultInfo->GetTotalCaptureCounts().GetInt());
1745            uint32_t captureIndex = 1;
1746            while (captureIndex < capturesSize) {
1747                uint32_t captureStartIndex = static_cast<uint32_t>(
1748                    matchResultInfo->GetStartOfCaptureIndex(captureIndex).GetInt());
1749                uint32_t captureEndIndex = static_cast<uint32_t>(
1750                    matchResultInfo->GetEndOfCaptureIndex(captureIndex).GetInt());
1751                int32_t subStrLen = static_cast<int32_t>(captureEndIndex - captureStartIndex);
1752                if (subStrLen < 0) {
1753                    matchValue.Update(JSTaggedValue::Undefined());
1754                } else {
1755                    matchValue.Update(JSTaggedValue(EcmaStringAccessor::FastSubString(thread->GetEcmaVM(),
1756                        string, captureStartIndex, subStrLen)));
1757                }
1758                if (arrLen > elements->GetLength()) {
1759                    elements.Update(JSObject::GrowElementsCapacity(thread,
1760                        JSHandle<JSObject>::Cast(splitArray), arrLen, true));
1761                }
1762                elements->Set(thread, arrLen - 1, matchValue);
1763                splitArray->SetArrayLength(thread, arrLen);
1764                if (arrLen == limit) {
1765                    if (useCache) {
1766                        RegExpExecResultCache::AddResultInCache(thread, cacheTable, regexp, jsString,
1767                                                                JSHandle<JSTaggedValue>(splitArray),
1768                                                                RegExpExecResultCache::SPLIT_TYPE, 0, 0, undefined);
1769                    }
1770                    return JSTaggedValue(splitArray.GetTaggedValue().GetTaggedObject());
1771                }
1772                arrLen++;
1773                captureIndex++;
1774            }
1775            lastMatchEnd = matchEndIndex;
1776            nextMatchFrom = matchEndIndex;
1777        }
1778    }
1779    matchValue.Update(JSTaggedValue(EcmaStringAccessor::FastSubString(thread->GetEcmaVM(),
1780        JSHandle<EcmaString>::Cast(jsString), lastMatchEnd, size - lastMatchEnd)));
1781    if (arrLen > elements->GetLength()) {
1782        elements.Update(JSObject::GrowElementsCapacity(thread, JSHandle<JSObject>::Cast(splitArray), arrLen, true));
1783    }
1784    elements->Set(thread, arrLen - 1, matchValue);
1785    splitArray->SetArrayLength(thread, arrLen);
1786    if (limit == MAX_SPLIT_LIMIT) {
1787        RegExpExecResultCache::AddResultInCache(thread, cacheTable, regexp, jsString,
1788                                                JSHandle<JSTaggedValue>(splitArray), RegExpExecResultCache::SPLIT_TYPE,
1789                                                0, 0, undefined);
1790    }
1791    return JSTaggedValue(splitArray.GetTaggedValue().GetTaggedObject());
1792}
1793
1794bool BuiltinsRegExp::RegExpExecInternal(JSThread *thread, const JSHandle<JSTaggedValue> regexp,
1795                                        JSHandle<EcmaString> inputString, int32_t lastIndex)
1796{
1797    size_t stringLength = EcmaStringAccessor(inputString).GetLength();
1798    bool isUtf16 = EcmaStringAccessor(inputString).IsUtf16();
1799    FlatStringInfo flatStrInfo = EcmaStringAccessor::FlattenAllString(thread->GetEcmaVM(), inputString);
1800    if (EcmaStringAccessor(inputString).IsTreeString()) { // use flattenedString as srcString
1801        inputString = JSHandle<EcmaString>(thread, flatStrInfo.GetString());
1802    }
1803    const uint8_t *strBuffer;
1804    if (isUtf16) {
1805        strBuffer = reinterpret_cast<const uint8_t *>(flatStrInfo.GetDataUtf16());
1806    } else {
1807        strBuffer = flatStrInfo.GetDataUtf8();
1808    }
1809    bool isSuccess = Matcher(thread, regexp, strBuffer, stringLength, lastIndex, isUtf16);
1810    if (isSuccess) {
1811        JSHandle<RegExpGlobalResult> globalTable(thread->GetCurrentEcmaContext()->GetRegExpGlobalResult());
1812        globalTable->ResetDollar(thread);
1813        globalTable->SetInputString(thread, inputString.GetTaggedValue());
1814    }
1815    return isSuccess;
1816}
1817
1818// NOLINTNEXTLINE(readability-non-const-parameter)
1819bool BuiltinsRegExp::Matcher(JSThread *thread, const JSHandle<JSTaggedValue> regexp,
1820                             const uint8_t *buffer, size_t length, int32_t lastIndex,
1821                             bool isUtf16)
1822{
1823    BUILTINS_API_TRACE(thread, RegExp, Matcher);
1824    // get bytecode
1825    JSTaggedValue bufferData = JSRegExp::Cast(regexp->GetTaggedObject())->GetByteCodeBuffer();
1826    void *dynBuf = JSNativePointer::Cast(bufferData.GetTaggedObject())->GetExternalPointer();
1827    auto bytecodeBuffer = reinterpret_cast<uint8_t *>(dynBuf);
1828    // execute
1829    RegExpCachedChunk chunk(thread);
1830    RegExpExecutor executor(&chunk);
1831    if (lastIndex < 0) {
1832        lastIndex = 0;
1833    }
1834    bool ret = executor.Execute(buffer, lastIndex, static_cast<uint32_t>(length), bytecodeBuffer, isUtf16);
1835    if (ret) {
1836        executor.GetResult(thread);
1837    }
1838    return ret;
1839}
1840
1841int64_t BuiltinsRegExp::AdvanceStringIndex(const JSHandle<JSTaggedValue> &inputStr, int64_t index,
1842                                           bool unicode)
1843{
1844    // 1. Assert: Type(S) is String.
1845    ASSERT(inputStr->IsString());
1846    // 2. Assert: index is an integer such that 0≤index≤2^53 - 1
1847    ASSERT(0 <= index && index <= pow(2, 53) - 1);
1848    // 3. Assert: Type(unicode) is Boolean.
1849    // 4. If unicode is false, return index+1.
1850    if (!unicode) {
1851        return index + 1;
1852    }
1853    // 5. Let length be the number of code units in S.
1854    uint32_t length = EcmaStringAccessor(inputStr->GetTaggedObject()).GetLength();
1855    // 6. If index+1 ≥ length, return index+1.
1856    if (index + 1 >= length) {
1857        return index + 1;
1858    }
1859    // 7. Let first be the code unit value at index index in S.
1860    uint16_t first = EcmaStringAccessor(inputStr->GetTaggedObject()).Get(index);
1861    // 8. If first < 0xD800 or first > 0xDFFF, return index+1.
1862    if (first < 0xD800 || first > 0xDFFF) {  // NOLINT(readability-magic-numbers)
1863        return index + 1;
1864    }
1865    // 9. Let second be the code unit value at index index+1 in S.
1866    uint16_t second = EcmaStringAccessor(inputStr->GetTaggedObject()).Get(index + 1);
1867    // 10. If second < 0xDC00 or second > 0xDFFF, return index+1.
1868    if (second < 0xDC00 || second > 0xDFFF) {  // NOLINT(readability-magic-numbers)
1869        return index + 1;
1870    }
1871    // 11. Return index + 2.
1872    return index + 2;
1873}
1874
1875JSTaggedValue BuiltinsRegExp::GetFlagsInternal(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
1876                                               const JSHandle<JSTaggedValue> &constructor, const uint8_t mask)
1877{
1878    BUILTINS_API_TRACE(thread, RegExp, GetFlagsInternal);
1879    // 1. Let R be the this value.
1880    // 2. If Type(R) is not Object, throw a TypeError exception.
1881    if (!obj->IsECMAObject()) {
1882        // throw a TypeError exception.
1883        THROW_TYPE_ERROR_AND_RETURN(thread, "this is not Object", JSTaggedValue(false));
1884    }
1885    // 3. If R does not have an [[OriginalFlags]] internal slot, throw a TypeError exception.
1886    JSHandle<JSObject> patternObj = JSHandle<JSObject>::Cast(obj);
1887    if (!patternObj->IsJSRegExp()) {
1888        // a. If SameValue(R, %RegExp.prototype%) is true, return undefined.
1889        const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1890        JSHandle<JSTaggedValue> constructorKey = globalConst->GetHandledConstructorString();
1891        JSHandle<JSTaggedValue> objConstructor = JSTaggedValue::GetProperty(thread, obj, constructorKey).GetValue();
1892        RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue(false));
1893        if (objConstructor->IsJSFunction() && constructor->IsJSFunction()) {
1894            JSHandle<GlobalEnv> objRealm = JSObject::GetFunctionRealm(thread, objConstructor);
1895            JSHandle<GlobalEnv> ctorRealm = JSObject::GetFunctionRealm(thread, constructor);
1896            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1897            if (objRealm->GetRegExpPrototype() == obj && *objRealm == *ctorRealm) {
1898                return JSTaggedValue::Undefined();
1899            }
1900        }
1901        // b. throw a TypeError exception.
1902        THROW_TYPE_ERROR_AND_RETURN(thread, "this does not have [[OriginalFlags]]", JSTaggedValue(false));
1903    }
1904    // 4. Let flags be the value of R’s [[OriginalFlags]] internal slot.
1905    JSHandle<JSRegExp> regexpObj(thread, JSRegExp::Cast(obj->GetTaggedObject()));
1906    // 5. If flags contains the code unit "[flag]", return true.
1907    // 6. Return false.
1908    uint8_t flags = static_cast<uint8_t>(regexpObj->GetOriginalFlags().GetInt());
1909    return GetTaggedBoolean(flags & mask);
1910}
1911
1912// 22.2.7.8
1913JSHandle<JSTaggedValue> BuiltinsRegExp::MakeMatchIndicesIndexPairArray(JSThread *thread,
1914    const std::vector<std::pair<JSTaggedValue, JSTaggedValue>>& indices,
1915    const std::vector<JSHandle<JSTaggedValue>>& groupNames, bool hasGroups)
1916{
1917    // 1. Let n be the number of elements in indices.
1918    uint32_t n = indices.size();
1919    // Assert: groupNames has n - 1 elements.
1920    ASSERT(groupNames.size() == n - 1);
1921    // 5. Let A be ! ArrayCreate(n).
1922    JSHandle<JSObject> results(JSArray::ArrayCreate(thread, JSTaggedNumber(n)));
1923    RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
1924    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1925    // 6. If hasGroups is true, then
1926    //    a. Let groups be OrdinaryObjectCreate(null).
1927    // 7. Else,
1928    //    a. Let groups be undefined.
1929    JSMutableHandle<JSTaggedValue> groups(thread, JSTaggedValue::Undefined());
1930    if (hasGroups) {
1931        JSHandle<JSTaggedValue> nullHandle(thread, JSTaggedValue::Null());
1932        JSHandle<JSObject> nullObj = factory->OrdinaryNewJSObjectCreate(nullHandle);
1933        groups.Update(nullObj.GetTaggedValue());
1934    }
1935    // 8. Perform ! CreateDataPropertyOrThrow(A, "groups", groups).
1936    const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1937    JSHandle<JSTaggedValue> groupsKey = globalConst->GetHandledGroupsString();
1938    JSObject::CreateDataProperty(thread, results, groupsKey, groups);
1939    // 9. For each integer i such that 0 ≤ i < n, in ascending order, do
1940    //    a. Let matchIndices be indices[i].
1941    //    b. If matchIndices is not undefined, then
1942    //        i. Let matchIndexPair be GetMatchIndexPair(S, matchIndices).
1943    //    c. Else,
1944    //        i. Let matchIndexPair be undefined.
1945    //    d. Perform ! CreateDataPropertyOrThrow(A, ! ToString(�(i)), matchIndexPair).
1946    //    e. If i > 0 and groupNames[i - 1] is not undefined, then
1947    //        i. Assert: groups is not undefined.
1948    //        ii. Perform ! CreateDataPropertyOrThrow(groups, groupNames[i - 1], matchIndexPair).
1949    JSMutableHandle<JSTaggedValue> matchIndexPair(thread, JSTaggedValue::Undefined());
1950    for (uint32_t i = 0; i < n; i++) {
1951        std::pair<JSTaggedValue, JSTaggedValue> matchIndices = indices[i];
1952        if (!matchIndices.first.IsUndefined()) {
1953            JSHandle<TaggedArray> match = factory->NewTaggedArray(2); // 2 means the length of array
1954            match->Set(thread, 0, matchIndices.first);
1955            match->Set(thread, 1, matchIndices.second);
1956            JSHandle<JSTaggedValue> pair(JSArray::CreateArrayFromList(thread, JSHandle<TaggedArray>::Cast(match)));
1957            matchIndexPair.Update(pair.GetTaggedValue());
1958        } else {
1959            matchIndexPair.Update(JSTaggedValue::Undefined());
1960        }
1961        JSObject::CreateDataProperty(thread, results, i, matchIndexPair);
1962        if (i > 0) {
1963            JSHandle<JSTaggedValue> groupName = groupNames[i - 1];
1964            if (!groupName->IsUndefined()) {
1965                JSHandle<JSObject> groupObject = JSHandle<JSObject>::Cast(groups);
1966                JSObject::CreateDataProperty(thread, groupObject, groupName, matchIndexPair);
1967            }
1968        }
1969    }
1970    // 10. Return A.
1971    return JSHandle<JSTaggedValue>::Cast(results);
1972}
1973
1974// 21.2.5.2.2
1975JSTaggedValue BuiltinsRegExp::RegExpBuiltinExec(JSThread *thread, const JSHandle<JSTaggedValue> regexp,
1976                                                const JSHandle<JSTaggedValue> inputStr,
1977                                                bool isFastPath, bool useCache, bool isIntermediateResult)
1978{
1979    ASSERT(regexp->IsJSRegExp());
1980    ASSERT(inputStr->IsString());
1981    BUILTINS_API_TRACE(thread, RegExp, RegExpBuiltinExec);
1982    uint32_t lastIndex = static_cast<uint32_t>(GetLastIndex(thread, regexp, isFastPath));
1983    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1984    JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1985    JSHandle<RegExpExecResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetRegExpCache());
1986    if (useCache) {
1987        JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, inputStr,
1988                                                                 RegExpExecResultCache::EXEC_TYPE, regexp,
1989                                                                 JSTaggedValue(lastIndex), undefined);
1990        if (!cacheResult.IsUndefined()) {
1991            return cacheResult;
1992        }
1993    }
1994    JSTaggedValue result = RegExpBuiltinExecWithoutResult(thread, regexp, inputStr, isFastPath, lastIndex, useCache);
1995    if (result.IsNull()) {
1996        return result;
1997    }
1998    JSHandle<EcmaString> inputString = JSHandle<EcmaString>::Cast(inputStr);
1999    JSHandle<RegExpGlobalResult> globalTable(thread->GetCurrentEcmaContext()->GetRegExpGlobalResult());
2000    uint32_t capturesSize = static_cast<uint32_t>(globalTable->GetTotalCaptureCounts().GetInt());
2001    JSHandle<JSObject> results(JSArray::ArrayCreate(thread, JSTaggedNumber(capturesSize)));
2002    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2003    const GlobalEnvConstants *globalConst = thread->GlobalConstants();
2004    JSHandle<JSTaggedValue> indexValue(thread, globalTable->GetStartOfCaptureIndex(0));
2005    if (isIntermediateResult) {
2006        // inlined intermediate result
2007        results->SetPropertyInlinedPropsWithRep(thread, EXEC_RESULT_INDEX_OFFSET, indexValue.GetTaggedValue());
2008        results->SetPropertyInlinedPropsWithRep(thread, EXEC_RESULT_INPUT_OFFSET, inputStr.GetTaggedValue());
2009    } else {
2010        // 24. Perform CreateDataProperty(A, "index", matchIndex).
2011        JSHandle<JSTaggedValue> indexKey = globalConst->GetHandledIndexString();
2012        JSObject::CreateDataProperty(thread, results, indexKey, indexValue);
2013        // 25. Perform CreateDataProperty(A, "input", S).
2014        JSHandle<JSTaggedValue> inputKey = globalConst->GetHandledInputString();
2015        JSObject::CreateDataProperty(thread, results, inputKey, inputStr);
2016    }
2017
2018    // 27. Perform CreateDataProperty(A, "0", matched_substr).
2019    uint32_t startIndex = static_cast<uint32_t>(globalTable->GetStartOfCaptureIndex(0).GetInt());
2020    uint32_t len = static_cast<uint32_t>(globalTable->GetEndOfCaptureIndex(0).GetInt()) - startIndex;
2021    JSHandle<JSTaggedValue> zeroValue(thread, JSTaggedValue(EcmaStringAccessor::FastSubString(
2022        thread->GetEcmaVM(), inputString, startIndex, len)));
2023    TaggedArray *srcElements = TaggedArray::Cast(results->GetElements().GetTaggedObject());
2024    JSHandle<TaggedArray> resultElements(thread, srcElements);
2025    resultElements->Set(thread, 0, zeroValue);
2026
2027    // Let indices be a new empty List.
2028    // Let groupNames be a new empty List.
2029    // Append match to indices.
2030    uint32_t endIndex = globalTable->GetEndIndex().GetInt();
2031    std::vector<std::pair<JSTaggedValue, JSTaggedValue>> indices;
2032    std::vector<JSHandle<JSTaggedValue>> groupNames;
2033    indices.emplace_back(std::make_pair(globalTable->GetStartOfCaptureIndex(0), JSTaggedValue(endIndex)));
2034    // If R contains any GroupName, then
2035    //   a. Let groups be OrdinaryObjectCreate(null).
2036    //   b. Let hasGroups be true.
2037    // Else,
2038    //   a. Let groups be undefined.
2039    //   b. Let hasGroups be false.
2040    JSHandle<JSRegExp> regexpObj(regexp);
2041    JSHandle<JSTaggedValue> groupName(thread, regexpObj->GetGroupName());
2042    JSMutableHandle<JSTaggedValue> groups(thread, JSTaggedValue::Undefined());
2043    bool hasGroups = false;
2044    if (!groupName->IsUndefined()) {
2045        ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2046        JSHandle<JSTaggedValue> nullHandle(thread, JSTaggedValue::Null());
2047        JSHandle<JSObject> nullObj = factory->OrdinaryNewJSObjectCreate(nullHandle);
2048        groups.Update(nullObj.GetTaggedValue());
2049        hasGroups = true;
2050    }
2051    if (isIntermediateResult) {
2052        // inlined intermediate result
2053        results->SetPropertyInlinedPropsWithRep(thread, EXEC_RESULT_GROUPS_OFFSET, groups.GetTaggedValue());
2054    } else {
2055        // Perform ! CreateDataPropertyOrThrow(A, "groups", groups).
2056        JSHandle<JSTaggedValue> groupsKey = globalConst->GetHandledGroupsString();
2057        JSObject::CreateDataProperty(thread, results, groupsKey, groups);
2058    }
2059    // Create a new RegExp on global
2060    uint32_t captureIndex = 1;
2061    JSMutableHandle<JSTaggedValue> iValue(thread, JSTaggedValue::Undefined());
2062    // 28. For each integer i such that i > 0 and i <= n
2063    for (; captureIndex < capturesSize; captureIndex++) {
2064        // a. Let capture_i be ith element of r's captures List
2065        int32_t captureStartIndex = globalTable->GetStartOfCaptureIndex(captureIndex).GetInt();
2066        int32_t captureEndIndex = globalTable->GetEndOfCaptureIndex(captureIndex).GetInt();
2067        int32_t subStrLen = captureEndIndex - captureStartIndex;
2068        if (subStrLen < 0) {
2069            iValue.Update(JSTaggedValue::Undefined());
2070            indices.emplace_back(std::make_pair(JSTaggedValue::Undefined(), JSTaggedValue::Undefined()));
2071        } else {
2072            iValue.Update(JSTaggedValue(EcmaStringAccessor::FastSubString(
2073                thread->GetEcmaVM(), inputString, captureStartIndex, subStrLen)));
2074            indices.emplace_back(std::make_pair(captureStartIndex, captureEndIndex));
2075        }
2076        // add to RegExp.$i and i must <= 9
2077        if (captureIndex <= REGEXP_GLOBAL_ARRAY_SIZE) {
2078            globalTable->SetCapture(thread, captureIndex, iValue.GetTaggedValue());
2079        }
2080
2081        resultElements->Set(thread, captureIndex, iValue);
2082        if (!groupName->IsUndefined()) {
2083            JSHandle<JSObject> groupObject = JSHandle<JSObject>::Cast(groups);
2084            TaggedArray *groupArray = TaggedArray::Cast(regexpObj->GetGroupName().GetTaggedObject());
2085            if (groupArray->GetLength() > captureIndex - 1) {
2086                JSHandle<JSTaggedValue> skey(thread, groupArray->Get(captureIndex - 1));
2087                JSObject::CreateDataProperty(thread, groupObject, skey, iValue);
2088                groupNames.emplace_back(skey);
2089            } else {
2090                groupNames.emplace_back(undefined);
2091            }
2092        } else {
2093            groupNames.emplace_back(undefined);
2094        }
2095    }
2096    // If hasIndices is true, then
2097    //   a. Let indicesArray be MakeMatchIndicesIndexPairArray(S, indices, groupNames, hasGroups).
2098    //   b. Perform ! CreateDataPropertyOrThrow(A, "indices", indicesArray).
2099    bool hasIndices = GetOriginalFlag(thread, regexp, RegExpParser::FLAG_HASINDICES);
2100    if (hasIndices) {
2101        auto indicesArray = MakeMatchIndicesIndexPairArray(thread, indices, groupNames, hasGroups);
2102        JSHandle<JSTaggedValue> indicesKey = globalConst->GetHandledIndicesString();
2103        JSObject::CreateDataProperty(thread, results, indicesKey, indicesArray);
2104    }
2105    JSHandle<JSTaggedValue> emptyString = thread->GlobalConstants()->GetHandledEmptyString();
2106    while (captureIndex <= REGEXP_GLOBAL_ARRAY_SIZE) {
2107        globalTable->SetCapture(thread, captureIndex, emptyString.GetTaggedValue());
2108        ++captureIndex;
2109    }
2110    if (useCache) {
2111        uint32_t newLastIndex = lastIndex;
2112        bool global = GetOriginalFlag(thread, regexp, RegExpParser::FLAG_GLOBAL);
2113        bool sticky = GetOriginalFlag(thread, regexp, RegExpParser::FLAG_STICKY);
2114        if (global || sticky) {
2115            newLastIndex = static_cast<uint32_t>(globalTable->GetEndIndex().GetInt());
2116        }
2117        RegExpExecResultCache::AddResultInCache(thread, cacheTable, regexp, inputStr,
2118                                                JSHandle<JSTaggedValue>(results), RegExpExecResultCache::EXEC_TYPE,
2119                                                lastIndex, newLastIndex, undefined);
2120    }
2121    // 29. Return A.
2122    return results.GetTaggedValue();
2123}
2124
2125JSTaggedValue BuiltinsRegExp::RegExpBuiltinExecWithoutResult(JSThread *thread, const JSHandle<JSTaggedValue> regexp,
2126                                                             const JSHandle<JSTaggedValue> inputStr,
2127                                                             bool isFastPath, uint32_t lastIndex, bool useCache)
2128{
2129    // check global and sticky flag to determine whether need to update lastIndex
2130    bool global = GetOriginalFlag(thread, regexp, RegExpParser::FLAG_GLOBAL);
2131    bool sticky = GetOriginalFlag(thread, regexp, RegExpParser::FLAG_STICKY);
2132    bool ifUpdateLastIndex = global || sticky;
2133    if (ifUpdateLastIndex) {
2134        uint32_t length = EcmaStringAccessor(inputStr->GetTaggedObject()).GetLength();
2135        if (lastIndex > length) {
2136            SetLastIndex(thread, regexp, JSTaggedValue(0), isFastPath);
2137            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2138            return JSTaggedValue::Null();
2139        }
2140    } else {
2141        lastIndex = 0;
2142    }
2143    JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
2144    JSHandle<EcmaString> inputString = JSHandle<EcmaString>::Cast(inputStr);
2145    bool matchResult = RegExpExecInternal(thread, regexp, inputString, lastIndex);
2146    if (!matchResult) {
2147        uint32_t endIndex = lastIndex;
2148        if (ifUpdateLastIndex) {
2149            endIndex = 0;
2150            SetLastIndex(thread, regexp, JSTaggedValue(0), isFastPath);
2151            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2152        }
2153        if (useCache) {
2154            JSHandle<RegExpExecResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetRegExpCache());
2155            RegExpExecResultCache::AddResultInCache(thread, cacheTable, regexp, inputStr,
2156                                                    JSHandle<JSTaggedValue>(thread, JSTaggedValue::Null()),
2157                                                    RegExpExecResultCache::EXEC_TYPE,
2158                                                    lastIndex, endIndex, undefined);
2159        }
2160        return JSTaggedValue::Null();
2161    }
2162    JSHandle<RegExpGlobalResult> globalTable(thread->GetCurrentEcmaContext()->GetRegExpGlobalResult());
2163    JSTaggedValue endIndex = globalTable->GetEndIndex();
2164    if (ifUpdateLastIndex) {
2165        SetLastIndex(thread, regexp, endIndex, isFastPath);
2166        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2167    }
2168    return JSTaggedValue::True();
2169}
2170// 21.2.5.2.1
2171JSTaggedValue BuiltinsRegExp::RegExpExec(JSThread *thread, const JSHandle<JSTaggedValue> &regexp,
2172                                         const JSHandle<JSTaggedValue> &inputString, bool useCache,
2173                                         bool isIntermediateResult)
2174{
2175    BUILTINS_API_TRACE(thread, RegExp, RegExpExec);
2176    // 1. Assert: Type(R) is Object.
2177    ASSERT(regexp->IsECMAObject());
2178    // 2. Assert: Type(S) is String.
2179    ASSERT(inputString->IsString());
2180    // 3. Let exec be Get(R, "exec").
2181    JSHandle<EcmaString> inputStr = JSTaggedValue::ToString(thread, inputString);
2182    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2183    const GlobalEnvConstants *globalConst = thread->GlobalConstants();
2184    JSHandle<JSTaggedValue> execHandle = globalConst->GetHandledExecString();
2185    JSTaggedValue execVal = ObjectFastOperator::FastGetPropertyByValue(thread, regexp.GetTaggedValue(),
2186                                                                       execHandle.GetTaggedValue());
2187    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2188
2189    JSHandle<JSTaggedValue> exec(thread, execVal);
2190    // 4. ReturnIfAbrupt(exec).
2191    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2192    // 5. If IsCallable(exec) is true, then
2193    if (exec->IsCallable()) {
2194        JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
2195        EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, exec, regexp, undefined, 1);
2196        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2197        info->SetCallArg(inputStr.GetTaggedValue());
2198        JSTaggedValue result = JSFunction::Call(info);
2199        // b. ReturnIfAbrupt(result).
2200        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2201        if (!result.IsECMAObject() && !result.IsNull()) {
2202            // throw a TypeError exception.
2203            THROW_TYPE_ERROR_AND_RETURN(thread, "exec result is null or is not Object", JSTaggedValue::Exception());
2204        }
2205        return result;
2206    }
2207    // 6. If R does not have a [[RegExpMatcher]] internal slot, throw a TypeError exception.
2208    if (!regexp->IsJSRegExp()) {
2209        // throw a TypeError exception.
2210        THROW_TYPE_ERROR_AND_RETURN(thread, "this does not have a [[RegExpMatcher]]", JSTaggedValue::Exception());
2211    }
2212    // 7. Return RegExpBuiltinExec(R, S).
2213    return RegExpBuiltinExec(thread, regexp, inputString, false, useCache, isIntermediateResult);
2214}
2215
2216// 21.2.3.2.1
2217JSTaggedValue BuiltinsRegExp::RegExpAlloc(JSThread *thread, const JSHandle<JSTaggedValue> &newTarget)
2218{
2219    BUILTINS_API_TRACE(thread, RegExp, RegExpAlloc);
2220    /**
2221     * 1. Let obj be OrdinaryCreateFromConstructor(newTarget, "%RegExpPrototype%",
2222     * «[[RegExpMatcher]],[[OriginalSource]], [[OriginalFlags]]»).
2223     * */
2224    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2225    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
2226    JSHandle<JSTaggedValue> func = env->GetRegExpFunction();
2227    JSHandle<JSTaggedValue> obj(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(func), newTarget));
2228    // 2. ReturnIfAbrupt(obj).
2229    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2230    // 5. Return obj.
2231    return obj.GetTaggedValue();
2232}
2233
2234uint32_t BuiltinsRegExp::UpdateExpressionFlags(JSThread *thread, const CString &checkStr)
2235{
2236    uint32_t flagsBits = 0;
2237    uint32_t flagsBitsTemp = 0;
2238    for (char i : checkStr) {
2239        switch (i) {
2240            case 'g':
2241                flagsBitsTemp = RegExpParser::FLAG_GLOBAL;
2242                break;
2243            case 'i':
2244                flagsBitsTemp = RegExpParser::FLAG_IGNORECASE;
2245                break;
2246            case 'm':
2247                flagsBitsTemp = RegExpParser::FLAG_MULTILINE;
2248                break;
2249            case 's':
2250                flagsBitsTemp = RegExpParser::FLAG_DOTALL;
2251                break;
2252            case 'u':
2253                flagsBitsTemp = RegExpParser::FLAG_UTF16;
2254                break;
2255            case 'y':
2256                flagsBitsTemp = RegExpParser::FLAG_STICKY;
2257                break;
2258            case 'd':
2259                flagsBitsTemp = RegExpParser::FLAG_HASINDICES;
2260                break;
2261            default: {
2262                ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2263                JSHandle<JSObject> syntaxError = factory->GetJSError(base::ErrorType::SYNTAX_ERROR,
2264                    "invalid regular expression flags", StackCheck::NO);
2265                THROW_NEW_ERROR_AND_RETURN_VALUE(thread, syntaxError.GetTaggedValue(), 0);
2266            }
2267        }
2268        if ((flagsBits & flagsBitsTemp) != 0) {
2269            ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2270            JSHandle<JSObject> syntaxError =
2271                factory->GetJSError(base::ErrorType::SYNTAX_ERROR, "invalid regular expression flags", StackCheck::NO);
2272            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, syntaxError.GetTaggedValue(), 0);
2273        }
2274        flagsBits |= flagsBitsTemp;
2275    }
2276    return flagsBits;
2277}
2278
2279JSTaggedValue BuiltinsRegExp::FlagsBitsToString(JSThread *thread, uint8_t flags)
2280{
2281    ASSERT((flags & 0x80) == 0);  // 0x80: first bit of flags must be 0
2282    BUILTINS_API_TRACE(thread, RegExp, FlagsBitsToString);
2283    uint8_t *flagsStr = new uint8_t[RegExpParser::FLAG_NUM + 1];  // FLAG_NUM flags + '\0'
2284    size_t flagsLen = 0;
2285    if (flags & RegExpParser::FLAG_HASINDICES) {
2286        flagsStr[flagsLen] = 'd';
2287        flagsLen++;
2288    }
2289    if (flags & RegExpParser::FLAG_GLOBAL) {
2290        flagsStr[flagsLen] = 'g';
2291        flagsLen++;
2292    }
2293    if (flags & RegExpParser::FLAG_IGNORECASE) {
2294        flagsStr[flagsLen] = 'i';
2295        flagsLen++;
2296    }
2297    if (flags & RegExpParser::FLAG_MULTILINE) {
2298        flagsStr[flagsLen] = 'm';
2299        flagsLen++;
2300    }
2301    if (flags & RegExpParser::FLAG_DOTALL) {
2302        flagsStr[flagsLen] = 's';
2303        flagsLen++;
2304    }
2305    if (flags & RegExpParser::FLAG_UTF16) {
2306        flagsStr[flagsLen] = 'u';
2307        flagsLen++;
2308    }
2309    if (flags & RegExpParser::FLAG_STICKY) {
2310        flagsStr[flagsLen] = 'y';
2311        flagsLen++;
2312    }
2313    flagsStr[flagsLen] = '\0';
2314    JSHandle<EcmaString> flagsString = thread->GetEcmaVM()->GetFactory()->NewFromUtf8(flagsStr, flagsLen);
2315    delete[] flagsStr;
2316
2317    return flagsString.GetTaggedValue();
2318}
2319
2320// 21.2.3.2.2
2321JSTaggedValue BuiltinsRegExp::RegExpInitialize(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
2322                                               const JSHandle<JSTaggedValue> &pattern,
2323                                               const JSHandle<JSTaggedValue> &flags)
2324{
2325    BUILTINS_API_TRACE(thread, RegExp, RegExpInitialize);
2326    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2327    JSHandle<EcmaString> patternStrHandle;
2328    uint8_t flagsBits = 0;
2329    // 1. If pattern is undefined, let P be the empty String.
2330    if (pattern->IsUndefined()) {
2331        patternStrHandle = factory->GetEmptyString();
2332    } else {
2333        // 2. Else, let P be ToString(pattern).
2334        patternStrHandle = JSTaggedValue::ToString(thread, pattern);
2335        // 3. ReturnIfAbrupt(P).
2336        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2337    }
2338    // 4. If flags is undefined, let F be the empty String.
2339    if (flags->IsUndefined()) {
2340        flagsBits = 0;
2341    } else if (flags->IsInt()) {
2342        flagsBits = static_cast<uint8_t>(flags->GetInt());
2343    } else {
2344        // 5. Else, let F be ToString(flags).
2345        JSHandle<EcmaString> flagsStrHandle = JSTaggedValue::ToString(thread, flags);
2346        // 6. ReturnIfAbrupt(F).
2347        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2348        /**
2349         * 7. If F contains any code unit other than "d", "g", "i", "m", "u", or "y" or if it contains the same code
2350         * unit more than once, throw a SyntaxError exception.
2351         **/
2352        CString checkStr = ConvertToString(*flagsStrHandle, StringConvertedUsage::LOGICOPERATION);
2353        flagsBits = static_cast<uint8_t>(UpdateExpressionFlags(thread, checkStr));
2354        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2355    }
2356    // 9. 10.
2357    Chunk chunk(thread->GetNativeAreaAllocator());
2358    RegExpParser parser = RegExpParser(thread, &chunk);
2359    RegExpParserCache *regExpParserCache = thread->GetCurrentEcmaContext()->GetRegExpParserCache();
2360    CVector<CString> groupName;
2361    auto getCache = regExpParserCache->GetCache(*patternStrHandle, flagsBits, groupName);
2362    if (getCache.first.IsHole()) {
2363        // String -> CString
2364        bool cesu8 = !(RegExpParser::FLAG_UTF16 & flagsBits);
2365        CString patternStdStr = ConvertToString(*patternStrHandle, StringConvertedUsage::LOGICOPERATION, cesu8);
2366        parser.Init(const_cast<char *>(reinterpret_cast<const char *>(patternStdStr.c_str())), patternStdStr.size(),
2367                    flagsBits);
2368        parser.Parse();
2369        if (parser.IsError()) {
2370            JSHandle<JSObject> syntaxError =
2371                factory->GetJSError(base::ErrorType::SYNTAX_ERROR, parser.GetErrorMsg().c_str(), StackCheck::NO);
2372            THROW_NEW_ERROR_AND_RETURN_VALUE(thread, syntaxError.GetTaggedValue(), JSTaggedValue::Exception());
2373        }
2374        groupName = parser.GetGroupNames();
2375    }
2376    JSHandle<JSRegExp> regexp(thread, JSRegExp::Cast(obj->GetTaggedObject()));
2377    // 11. Set the value of obj’s [[OriginalSource]] internal slot to P.
2378    regexp->SetOriginalSource(thread, patternStrHandle.GetTaggedValue());
2379    // 12. Set the value of obj’s [[OriginalFlags]] internal slot to F.
2380    regexp->SetOriginalFlags(thread, JSTaggedValue(flagsBits));
2381    if (!groupName.empty()) {
2382        JSHandle<TaggedArray> taggedArray = factory->NewTaggedArray(groupName.size());
2383        for (size_t i = 0; i < groupName.size(); ++i) {
2384            JSHandle<JSTaggedValue> flagsKey(factory->NewFromStdString(groupName[i].c_str()));
2385            taggedArray->Set(thread, i, flagsKey);
2386        }
2387        regexp->SetGroupName(thread, taggedArray);
2388    }
2389    // 13. Set obj’s [[RegExpMatcher]] internal slot.
2390    if (getCache.first.IsHole()) {
2391        auto bufferSize = parser.GetOriginBufferSize();
2392        auto buffer = parser.GetOriginBuffer();
2393        factory->NewJSRegExpByteCodeData(regexp, buffer, bufferSize);
2394        regExpParserCache->SetCache(*patternStrHandle, flagsBits, regexp->GetByteCodeBuffer(), bufferSize, groupName);
2395    } else {
2396        regexp->SetByteCodeBuffer(thread, getCache.first);
2397        regexp->SetLength(static_cast<uint32_t>(getCache.second));
2398    }
2399    // 14. Let setStatus be Set(obj, "lastIndex", 0, true).
2400    SetLastIndex(thread, obj, JSTaggedValue(0), true);
2401    // 16. Return obj.
2402    return obj.GetTaggedValue();
2403}
2404
2405JSTaggedValue BuiltinsRegExp::RegExpCreate(JSThread *thread, const JSHandle<JSTaggedValue> &pattern,
2406                                           const JSHandle<JSTaggedValue> &flags)
2407{
2408    BUILTINS_API_TRACE(thread, RegExp, Create);
2409    auto ecmaVm = thread->GetEcmaVM();
2410    JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
2411    JSHandle<JSTaggedValue> newTarget = env->GetRegExpFunction();
2412    // 1. Let obj be RegExpAlloc(%RegExp%).
2413    JSHandle<JSTaggedValue> object(thread, RegExpAlloc(thread, newTarget));
2414    // 2. ReturnIfAbrupt(obj).
2415    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2416    // 3. Return RegExpInitialize(obj, P, F).
2417    return RegExpInitialize(thread, object, pattern, flags);
2418}
2419
2420// 21.2.3.2.4
2421EcmaString *BuiltinsRegExp::EscapeRegExpPattern(JSThread *thread, const JSHandle<JSTaggedValue> &src,
2422                                                const JSHandle<JSTaggedValue> &flags)
2423{
2424    BUILTINS_API_TRACE(thread, RegExp, EscapeRegExpPattern);
2425    // String -> CString
2426    JSHandle<EcmaString> srcStr(thread, static_cast<EcmaString *>(src->GetTaggedObject()));
2427    JSHandle<EcmaString> flagsStr(thread, static_cast<EcmaString *>(flags->GetTaggedObject()));
2428    CString srcStdStr = ConvertToString(*srcStr, StringConvertedUsage::LOGICOPERATION);
2429    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2430    // "" -> (?:)
2431    if (srcStdStr.empty()) {
2432        srcStdStr = "(?:)";
2433    }
2434    // "/" -> "\/"
2435    srcStdStr = base::StringHelper::ReplaceAll(srcStdStr, "/", "\\/");
2436    // "\\" -> "\"
2437    srcStdStr = base::StringHelper::ReplaceAll(srcStdStr, "\\", "\\");
2438
2439    return *factory->NewFromUtf8(srcStdStr);
2440}
2441
2442// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
2443#define SET_GET_CAPTURE_IMPL(index)                                                                                   \
2444    JSTaggedValue BuiltinsRegExp::GetCapture##index(JSThread *thread, [[maybe_unused]] const JSHandle<JSObject> &obj) \
2445    {                                                                                                                 \
2446        return RegExpGlobalResult::GetCapture<index>(thread);                                                         \
2447    }                                                                                                                 \
2448    bool BuiltinsRegExp::SetCapture##index([[maybe_unused]] JSThread *thread,                                         \
2449                                           [[maybe_unused]] const JSHandle<JSObject> &obj,                            \
2450                                           [[maybe_unused]] const JSHandle<JSTaggedValue> &value,                     \
2451                                           [[maybe_unused]] bool mayThrow)                                            \
2452    {                                                                                                                 \
2453        return true;                                                                                                  \
2454    }
2455
2456    SET_GET_CAPTURE_IMPL(1)
2457    SET_GET_CAPTURE_IMPL(2)
2458    SET_GET_CAPTURE_IMPL(3)
2459    SET_GET_CAPTURE_IMPL(4)
2460    SET_GET_CAPTURE_IMPL(5)
2461    SET_GET_CAPTURE_IMPL(6)
2462    SET_GET_CAPTURE_IMPL(7)
2463    SET_GET_CAPTURE_IMPL(8)
2464    SET_GET_CAPTURE_IMPL(9)
2465#undef SET_GET_CAPTURE_IMPL
2466
2467JSTaggedValue RegExpExecResultCache::CreateCacheTable(JSThread *thread)
2468{
2469    int length = CACHE_TABLE_HEADER_SIZE + INITIAL_CACHE_NUMBER * ENTRY_SIZE;
2470
2471    auto table = static_cast<RegExpExecResultCache *>(
2472        *thread->GetEcmaVM()->GetFactory()->NewTaggedArray(length, JSTaggedValue::Undefined()));
2473    table->SetLargeStrCount(thread, DEFAULT_LARGE_STRING_COUNT);
2474    table->SetConflictCount(thread, DEFAULT_CONFLICT_COUNT);
2475    table->SetStrLenThreshold(thread, 0);
2476    table->SetHitCount(thread, 0);
2477    table->SetCacheCount(thread, 0);
2478    table->SetCacheLength(thread, INITIAL_CACHE_NUMBER);
2479    return JSTaggedValue(table);
2480}
2481
2482JSTaggedValue RegExpExecResultCache::FindCachedResult(JSThread *thread,
2483                                                      const JSHandle<JSTaggedValue> input, CacheType type,
2484                                                      const JSHandle<JSTaggedValue> regexp,
2485                                                      JSTaggedValue lastIndexInput, JSHandle<JSTaggedValue> extend,
2486                                                      bool isIntermediateResult)
2487{
2488    JSHandle<JSRegExp> regexpObj(regexp);
2489    JSTaggedValue pattern = regexpObj->GetOriginalSource();
2490    JSTaggedValue flags = regexpObj->GetOriginalFlags();
2491    JSTaggedValue inputValue = input.GetTaggedValue();
2492    JSTaggedValue extendValue = extend.GetTaggedValue();
2493    if (!pattern.IsString() || !flags.IsInt() || !input->IsString() || !lastIndexInput.IsInt()) {
2494        return JSTaggedValue::Undefined();
2495    }
2496    uint32_t hash = pattern.GetKeyHashCode() + static_cast<uint32_t>(flags.GetInt()) +
2497                    input->GetKeyHashCode() + static_cast<uint32_t>(lastIndexInput.GetInt());
2498    uint32_t entry = hash & static_cast<uint32_t>(GetCacheLength() - 1);
2499    if (!Match(entry, pattern, flags, inputValue, lastIndexInput, extendValue, type)) {
2500        uint32_t entry2 = (entry + 1) & static_cast<uint32_t>(GetCacheLength() - 1);
2501        if (!Match(entry2, pattern, flags, inputValue, lastIndexInput, extendValue, type)) {
2502            return JSTaggedValue::Undefined();
2503        }
2504        entry = entry2;
2505    }
2506    ASSERT((static_cast<size_t>(CACHE_TABLE_HEADER_SIZE) +
2507        static_cast<size_t>(entry) * static_cast<size_t>(ENTRY_SIZE)) <= static_cast<size_t>(UINT32_MAX));
2508    uint32_t index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE;
2509    // update cached value if input value is changed
2510    JSTaggedValue cachedStr = Get(index + INPUT_STRING_INDEX);
2511    if (!cachedStr.IsUndefined() && cachedStr != inputValue) {
2512        Set(thread, index + INPUT_STRING_INDEX, inputValue);
2513    }
2514    JSTaggedValue result;
2515    switch (type) {
2516        case REPLACE_TYPE:
2517            result = Get(index + RESULT_REPLACE_INDEX);
2518            break;
2519        case SPLIT_TYPE:
2520            result = Get(index + RESULT_SPLIT_INDEX);
2521            break;
2522        case MATCH_TYPE:
2523            result = Get(index + RESULT_MATCH_INDEX);
2524            break;
2525        case EXEC_TYPE:
2526            result = Get(index + RESULT_EXEC_INDEX);
2527            break;
2528        case INTERMEDIATE_REPLACE_TYPE:
2529            result = Get(index + RESULT_INTERMEDIATE_REPLACE_INDEX);
2530            break;
2531        case TEST_TYPE:
2532            result = Get(index + RESULT_TEST_INDEX);
2533            break;
2534        case SEARCH_TYPE:
2535            result = Get(index + RESULT_SEARCH_INDEX);
2536            break;
2537        default:
2538            LOG_ECMA(FATAL) << "this branch is unreachable";
2539            UNREACHABLE();
2540            break;
2541    }
2542    JSHandle<RegExpGlobalResult> globalTable(thread->GetCurrentEcmaContext()->GetRegExpGlobalResult());
2543    JSTaggedValue cachedTable = Get(index + CAPTURE_SIZE);
2544    thread->GetCurrentEcmaContext()->SetRegExpGlobalResult(cachedTable);
2545    SetHitCount(thread, GetHitCount() + 1);
2546    if (type != SEARCH_TYPE && type != SPLIT_TYPE) {
2547        BuiltinsRegExp::SetLastIndex(thread, regexp, Get(index + LAST_INDEX_INDEX), true);
2548    }
2549    if (!isIntermediateResult && result.IsJSArray()) {
2550        JSHandle<JSArray> resultHandle(thread, JSArray::Cast(result));
2551        JSHandle<JSArray> copyArray = thread->GetEcmaVM()->GetFactory()->CloneArrayLiteral(resultHandle);
2552        return copyArray.GetTaggedValue();
2553    }
2554    return result;
2555}
2556
2557void RegExpExecResultCache::AddResultInCache(JSThread *thread, JSHandle<RegExpExecResultCache> cache,
2558                                             const JSHandle<JSTaggedValue> regexp,
2559                                             const JSHandle<JSTaggedValue> input,
2560                                             const JSHandle<JSTaggedValue> resultArray, CacheType type,
2561                                             uint32_t lastIndexInput, uint32_t lastIndex,
2562                                             JSHandle<JSTaggedValue> extend, bool isIntermediateResult)
2563{
2564    JSHandle<JSRegExp> regexpObj(regexp);
2565    JSHandle<JSTaggedValue> pattern(thread, regexpObj->GetOriginalSource());
2566    JSHandle<JSTaggedValue> flags(thread, regexpObj->GetOriginalFlags());
2567    if (!pattern->IsString() || !flags->IsInt() || !input->IsString()) {
2568        return;
2569    }
2570
2571    JSHandle<JSTaggedValue> resultArrayCopy;
2572    if (!isIntermediateResult && resultArray->IsJSArray()) {
2573        JSHandle<JSArray> copyArray = thread->GetEcmaVM()->GetFactory()
2574                                            ->CloneArrayLiteral(JSHandle<JSArray>(resultArray));
2575        resultArrayCopy = JSHandle<JSTaggedValue>(copyArray);
2576    } else {
2577        resultArrayCopy = JSHandle<JSTaggedValue>(resultArray);
2578    }
2579    JSHandle<RegExpGlobalResult> globalTable(thread->GetCurrentEcmaContext()->GetRegExpGlobalResult());
2580    JSHandle<TaggedArray> taggedArray = JSHandle<TaggedArray>::Cast(globalTable);
2581    auto factory = thread->GetEcmaVM()->GetFactory();
2582    uint32_t arrayLength = globalTable->GetLength();
2583    JSHandle<TaggedArray> resTableArray = factory->NewAndCopyTaggedArray(taggedArray, arrayLength, arrayLength);
2584    JSTaggedValue patternValue = pattern.GetTaggedValue();
2585    JSTaggedValue flagsValue = flags.GetTaggedValue();
2586    JSTaggedValue inputValue = input.GetTaggedValue();
2587    JSTaggedValue extendValue = extend.GetTaggedValue();
2588    JSTaggedValue lastIndexInputValue(lastIndexInput);
2589    JSTaggedValue lastIndexValue(lastIndex);
2590    JSTaggedValue resTableArrayValue = resTableArray.GetTaggedValue();
2591
2592    uint32_t hash = patternValue.GetKeyHashCode() + static_cast<uint32_t>(flagsValue.GetInt()) +
2593                    inputValue.GetKeyHashCode() + lastIndexInput;
2594    uint32_t entry = hash & static_cast<uint32_t>(cache->GetCacheLength() - 1);
2595    ASSERT((static_cast<size_t>(CACHE_TABLE_HEADER_SIZE) +
2596        static_cast<size_t>(entry) * static_cast<size_t>(ENTRY_SIZE)) <= static_cast<size_t>(UINT32_MAX));
2597    uint32_t index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE;
2598    if (cache->Get(index).IsUndefined()) {
2599        cache->SetCacheCount(thread, cache->GetCacheCount() + 1);
2600        cache->SetEntry(thread, entry, patternValue, flagsValue, inputValue,
2601                        lastIndexInputValue, lastIndexValue, extendValue, resTableArrayValue);
2602        cache->UpdateResultArray(thread, entry, resultArrayCopy.GetTaggedValue(), type);
2603    } else if (cache->Match(entry, patternValue, flagsValue, inputValue, lastIndexInputValue, extendValue, type)) {
2604        cache->UpdateResultArray(thread, entry, resultArrayCopy.GetTaggedValue(), type);
2605    } else {
2606        uint32_t entry2 = (entry + 1) & static_cast<uint32_t>(cache->GetCacheLength() - 1);
2607        ASSERT((static_cast<size_t>(CACHE_TABLE_HEADER_SIZE) +
2608            static_cast<size_t>(entry2) * static_cast<size_t>(ENTRY_SIZE)) <= static_cast<size_t>(UINT32_MAX));
2609        uint32_t index2 = CACHE_TABLE_HEADER_SIZE + entry2 * ENTRY_SIZE;
2610        if (cache->GetCacheLength() < DEFAULT_CACHE_NUMBER) {
2611            GrowRegexpCache(thread, cache);
2612            // update value after gc.
2613            patternValue = pattern.GetTaggedValue();
2614            flagsValue = flags.GetTaggedValue();
2615            inputValue = input.GetTaggedValue();
2616
2617            cache->SetCacheLength(thread, DEFAULT_CACHE_NUMBER);
2618            entry2 = hash & static_cast<uint32_t>(cache->GetCacheLength() - 1);
2619            index2 = CACHE_TABLE_HEADER_SIZE + entry2 * ENTRY_SIZE;
2620        }
2621        extendValue = extend.GetTaggedValue();
2622        resTableArrayValue = resTableArray.GetTaggedValue();
2623        if (cache->Get(index2).IsUndefined()) {
2624            cache->SetCacheCount(thread, cache->GetCacheCount() + 1);
2625            cache->SetEntry(thread, entry2, patternValue, flagsValue, inputValue,
2626                            lastIndexInputValue, lastIndexValue, extendValue, resTableArrayValue);
2627            cache->UpdateResultArray(thread, entry2, resultArrayCopy.GetTaggedValue(), type);
2628        } else if (cache->Match(entry2, patternValue, flagsValue, inputValue, lastIndexInputValue, extendValue, type)) {
2629            cache->UpdateResultArray(thread, entry2, resultArrayCopy.GetTaggedValue(), type);
2630        } else {
2631            cache->SetConflictCount(thread, cache->GetConflictCount() > 1 ? (cache->GetConflictCount() - 1) : 0);
2632            cache->SetCacheCount(thread, cache->GetCacheCount() - 1);
2633            cache->ClearEntry(thread, entry2);
2634            cache->SetEntry(thread, entry2, patternValue, flagsValue, inputValue,
2635                            lastIndexInputValue, lastIndexValue, extendValue, resTableArrayValue);
2636            cache->UpdateResultArray(thread, entry2, resultArrayCopy.GetTaggedValue(), type);
2637        }
2638    }
2639}
2640
2641void RegExpExecResultCache::GrowRegexpCache(JSThread *thread, JSHandle<RegExpExecResultCache> cache)
2642{
2643    int length = CACHE_TABLE_HEADER_SIZE + DEFAULT_CACHE_NUMBER * ENTRY_SIZE;
2644    auto factory = thread->GetEcmaVM()->GetFactory();
2645    auto newCache = factory->ExtendArray(JSHandle<TaggedArray>(cache), length, JSTaggedValue::Undefined());
2646    thread->GetCurrentEcmaContext()->SetRegExpCache(newCache.GetTaggedValue());
2647}
2648
2649void RegExpExecResultCache::SetEntry(JSThread *thread, int entry, JSTaggedValue &pattern, JSTaggedValue &flags,
2650                                     JSTaggedValue &input, JSTaggedValue &lastIndexInputValue,
2651                                     JSTaggedValue &lastIndexValue, JSTaggedValue &extendValue,
2652                                     JSTaggedValue &resTableArray)
2653{
2654    ASSERT((static_cast<size_t>(CACHE_TABLE_HEADER_SIZE) +
2655            static_cast<size_t>(entry) * static_cast<size_t>(ENTRY_SIZE)) <= static_cast<size_t>(INT_MAX));
2656    int index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE;
2657    Set(thread, index + PATTERN_INDEX, pattern);
2658    Set(thread, index + FLAG_INDEX, flags);
2659    Set(thread, index + INPUT_STRING_INDEX, input);
2660    Set(thread, index + LAST_INDEX_INPUT_INDEX, lastIndexInputValue);
2661    Set(thread, index + LAST_INDEX_INDEX, lastIndexValue);
2662    Set(thread, index + EXTEND_INDEX, extendValue);
2663    Set(thread, index + CAPTURE_SIZE, resTableArray);
2664}
2665
2666void RegExpExecResultCache::UpdateResultArray(JSThread *thread, int entry, JSTaggedValue resultArray, CacheType type)
2667{
2668    ASSERT((static_cast<size_t>(CACHE_TABLE_HEADER_SIZE) +
2669            static_cast<size_t>(entry) * static_cast<size_t>(ENTRY_SIZE)) <= static_cast<size_t>(INT_MAX));
2670    int index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE;
2671    switch (type) {
2672        case REPLACE_TYPE:
2673            Set(thread, index + RESULT_REPLACE_INDEX, resultArray);
2674            break;
2675        case SPLIT_TYPE:
2676            Set(thread, index + RESULT_SPLIT_INDEX, resultArray);
2677            break;
2678        case MATCH_TYPE:
2679            Set(thread, index + RESULT_MATCH_INDEX, resultArray);
2680            break;
2681        case EXEC_TYPE:
2682            Set(thread, index + RESULT_EXEC_INDEX, resultArray);
2683            break;
2684        case INTERMEDIATE_REPLACE_TYPE:
2685            Set(thread, index + RESULT_INTERMEDIATE_REPLACE_INDEX, resultArray);
2686            break;
2687        case TEST_TYPE:
2688            Set(thread, index + RESULT_TEST_INDEX, resultArray);
2689            break;
2690        case SEARCH_TYPE:
2691            Set(thread, index + RESULT_SEARCH_INDEX, resultArray);
2692            break;
2693        default:
2694            LOG_ECMA(FATAL) << "this branch is unreachable";
2695            UNREACHABLE();
2696            break;
2697    }
2698}
2699
2700void RegExpExecResultCache::ClearEntry(JSThread *thread, int entry)
2701{
2702    ASSERT((static_cast<size_t>(CACHE_TABLE_HEADER_SIZE) +
2703            static_cast<size_t>(entry) * static_cast<size_t>(ENTRY_SIZE)) <= static_cast<size_t>(INT_MAX));
2704    int index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE;
2705    JSTaggedValue undefined = JSTaggedValue::Undefined();
2706    for (int i = 0; i < ENTRY_SIZE; i++) {
2707        Set(thread, index + i, undefined);
2708    }
2709}
2710
2711bool RegExpExecResultCache::Match(int entry, JSTaggedValue &pattern, JSTaggedValue &flags, JSTaggedValue &input,
2712                                  JSTaggedValue &lastIndexInputValue, JSTaggedValue &extend, CacheType type)
2713{
2714    ASSERT((static_cast<size_t>(CACHE_TABLE_HEADER_SIZE) +
2715            static_cast<size_t>(entry) * static_cast<size_t>(ENTRY_SIZE)) <= static_cast<size_t>(INT_MAX));
2716    int index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE;
2717
2718    JSTaggedValue typeKey = Get(index + RESULT_REPLACE_INDEX + type);
2719    if (typeKey.IsUndefined()) {
2720        return false;
2721    }
2722
2723    JSTaggedValue keyPattern = Get(index + PATTERN_INDEX);
2724    if (keyPattern.IsUndefined()) {
2725        return false;
2726    }
2727
2728    uint8_t flagsBits = static_cast<uint8_t>(flags.GetInt());
2729    JSTaggedValue keyFlags = Get(index + FLAG_INDEX);
2730    uint8_t keyFlagsBits = static_cast<uint8_t>(keyFlags.GetInt());
2731    if (flagsBits != keyFlagsBits) {
2732        return false;
2733    }
2734
2735    uint32_t lastIndexInputInt = static_cast<uint32_t>(lastIndexInputValue.GetInt());
2736    JSTaggedValue keyLastIndexInput = Get(index + LAST_INDEX_INPUT_INDEX);
2737    uint32_t keyLastIndexInputInt = static_cast<uint32_t>(keyLastIndexInput.GetInt());
2738    if (lastIndexInputInt != keyLastIndexInputInt) {
2739        return false;
2740    }
2741
2742    JSTaggedValue keyInput = Get(index + INPUT_STRING_INDEX);
2743    JSTaggedValue keyExtend = Get(index + EXTEND_INDEX);
2744    EcmaString *patternStr = EcmaString::Cast(pattern.GetTaggedObject());
2745    EcmaString *inputStr = EcmaString::Cast(input.GetTaggedObject());
2746    EcmaString *keyPatternStr = EcmaString::Cast(keyPattern.GetTaggedObject());
2747    EcmaString *keyInputStr = EcmaString::Cast(keyInput.GetTaggedObject());
2748    bool extendEqual = false;
2749    if (extend.IsString() && keyExtend.IsString()) {
2750        EcmaString *extendStr = EcmaString::Cast(extend.GetTaggedObject());
2751        EcmaString *keyExtendStr = EcmaString::Cast(keyExtend.GetTaggedObject());
2752        extendEqual = EcmaStringAccessor::StringsAreEqual(extendStr, keyExtendStr);
2753    } else if (extend.IsUndefined() && keyExtend.IsUndefined()) {
2754        extendEqual = true;
2755    } else {
2756        return false;
2757    }
2758    return extendEqual &&
2759           EcmaStringAccessor::StringsAreEqual(patternStr, keyPatternStr) &&
2760           EcmaStringAccessor::StringsAreEqual(inputStr, keyInputStr);
2761}
2762
2763JSTaggedValue RegExpGlobalResult::CreateGlobalResultTable(JSThread *thread)
2764{
2765    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2766    uint32_t initialLength = GLOBAL_TABLE_SIZE + INITIAL_CAPTURE_INDICES;
2767    auto table = static_cast<RegExpGlobalResult *>(
2768        *factory->NewTaggedArray(initialLength, JSTaggedValue::Undefined()));
2769    // initialize dollars with empty string
2770    JSTaggedValue emptyString = factory->GetEmptyString().GetTaggedValue();
2771    for (uint32_t i = 1; i <= DOLLAR_NUMBER; i++) {
2772        table->SetCapture(thread, CAPTURE_START_INDEX + i, emptyString);
2773    }
2774    // initialize match info
2775    table->SetTotalCaptureCounts(thread, JSTaggedValue(0));
2776    table->SetInputString(thread, emptyString);
2777    for (uint32_t i = 0; i < INITIAL_CAPTURE_INDICES / 2; i++) { // 2: capture pair
2778        table->SetStartOfCaptureIndex(thread, i, JSTaggedValue(0));
2779        table->SetEndOfCaptureIndex(thread, i, JSTaggedValue(0));
2780    }
2781    return JSTaggedValue(table);
2782}
2783
2784JSHandle<RegExpGlobalResult> RegExpGlobalResult::GrowCapturesCapacity(JSThread *thread,
2785    JSHandle<RegExpGlobalResult>result, uint32_t length)
2786{
2787    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2788    JSHandle<TaggedArray> newResult = factory->ExtendArray(
2789        JSHandle<TaggedArray>(result), length, JSTaggedValue(0));
2790    thread->GetCurrentEcmaContext()->SetRegExpGlobalResult(newResult.GetTaggedValue());
2791    return JSHandle<RegExpGlobalResult>(newResult);
2792}
2793
2794bool BuiltinsRegExp::GetFlag(JSThread *thread, const JSHandle<JSTaggedValue> regexp, uint32_t flag, bool isFastPath)
2795{
2796    if (isFastPath) {
2797        uint8_t flagsBits = static_cast<uint8_t>(JSHandle<JSRegExp>::Cast(regexp)->GetOriginalFlags().GetInt());
2798        return (flagsBits & flag) != 0;
2799    } else {
2800        JSMutableHandle<JSTaggedValue> flagStr(thread, JSTaggedValue::Undefined());
2801        switch (flag) {
2802            case RegExpParser::FLAG_GLOBAL:
2803                flagStr.Update(thread->GlobalConstants()->GetHandledGlobalString());
2804                break;
2805            case RegExpParser::FLAG_UTF16:
2806                flagStr.Update(thread->GlobalConstants()->GetHandledUnicodeString());
2807                break;
2808            case RegExpParser::FLAG_STICKY:
2809                flagStr.Update(thread->GlobalConstants()->GetHandledStickyString());
2810                break;
2811            case RegExpParser::FLAG_MULTILINE:
2812                flagStr.Update(thread->GlobalConstants()->GetHandledMultilineString());
2813                break;
2814            case RegExpParser::FLAG_IGNORECASE:
2815                flagStr.Update(thread->GlobalConstants()->GetHandledIgnoreCaseString());
2816                break;
2817            case RegExpParser::FLAG_HASINDICES:
2818                flagStr.Update(thread->GlobalConstants()->GetHandledHasIndicesString());
2819                break;
2820            case RegExpParser::FLAG_DOTALL:
2821                UNREACHABLE();
2822            default:
2823                break;
2824        }
2825        JSTaggedValue globalValue =
2826            ObjectFastOperator::FastGetPropertyByValue(thread, regexp.GetTaggedValue(), flagStr.GetTaggedValue());
2827        RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
2828        return globalValue.ToBoolean();
2829    }
2830}
2831
2832bool BuiltinsRegExp::GetOriginalFlag(JSThread *thread, const JSHandle<JSTaggedValue> regexp, uint32_t flag)
2833{
2834    return GetFlag(thread, regexp, flag, true);
2835}
2836
2837void BuiltinsRegExp::SetLastIndex(JSThread *thread, const JSHandle<JSTaggedValue> regexp,
2838                                  JSTaggedValue lastIndex, bool isFastPath)
2839{
2840    if (isFastPath) {
2841        JSHandle<JSObject>::Cast(regexp)->SetPropertyInlinedPropsWithRep(thread, LAST_INDEX_OFFSET, lastIndex);
2842        return;
2843    }
2844    ObjectFastOperator::FastSetPropertyByValue(thread, regexp.GetTaggedValue(),
2845        thread->GlobalConstants()->GetHandledLastIndexString().GetTaggedValue(), lastIndex);
2846}
2847
2848int64_t BuiltinsRegExp::GetLastIndex(JSThread *thread, const JSHandle<JSTaggedValue> regexp, bool isFastPath)
2849{
2850    if (isFastPath) {
2851        return JSHandle<JSObject>::Cast(regexp)->GetPropertyInlinedProps(LAST_INDEX_OFFSET).GetInt();
2852    }
2853    JSHandle<JSTaggedValue> lastIndexHandle(thread, ObjectFastOperator::FastGetPropertyByValue(
2854        thread, regexp.GetTaggedValue(), thread->GlobalConstants()->GetHandledLastIndexString().GetTaggedValue()));
2855    RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
2856    JSTaggedNumber thisIndex = JSTaggedValue::ToLength(thread, lastIndexHandle);
2857    RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
2858    return thisIndex.GetNumber();
2859}
2860
2861JSTaggedValue BuiltinsRegExp::GetExecResultIndex(JSThread *thread, const JSHandle<JSTaggedValue> &execResults,
2862                                                 bool isFastPath)
2863{
2864    if (isFastPath) {
2865        return JSHandle<JSObject>::Cast(execResults)->GetPropertyInlinedProps(EXEC_RESULT_INDEX_OFFSET);
2866    }
2867    JSHandle<JSTaggedValue> resultIndex = thread->GlobalConstants()->GetHandledIndexString();
2868    JSTaggedValue index = ObjectFastOperator::FastGetPropertyByValue(
2869        thread, execResults.GetTaggedValue(), resultIndex.GetTaggedValue());
2870    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2871    return index;
2872}
2873
2874JSTaggedValue BuiltinsRegExp::GetExecResultGroups(JSThread *thread, const JSHandle<JSTaggedValue> &execResults,
2875                                                  bool isFastPath)
2876{
2877    if (isFastPath) {
2878        return JSHandle<JSObject>::Cast(execResults)->GetPropertyInlinedProps(EXEC_RESULT_GROUPS_OFFSET);
2879    }
2880    JSHandle<JSTaggedValue> groupKey = thread->GlobalConstants()->GetHandledGroupsString();
2881    JSTaggedValue groups = ObjectFastOperator::FastGetPropertyByValue(
2882        thread, execResults.GetTaggedValue(), groupKey.GetTaggedValue());
2883    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2884    return groups;
2885}
2886
2887JSHandle<EcmaString> BuiltinsRegExp::CreateStringFromResultArray(JSThread *thread,
2888    const JSHandle<TaggedArray> resultArray, const std::vector<uint64_t> &resultLengthArray,
2889    JSHandle<EcmaString> srcString, uint32_t resultStrLength, bool isUtf8)
2890{
2891    JSHandle<EcmaString> result = JSHandle<EcmaString>(thread,
2892        EcmaStringAccessor::CreateLineString(thread->GetEcmaVM(), resultStrLength, isUtf8));
2893    FlatStringInfo resultInfo = FlatStringInfo(*result, 0, resultStrLength);
2894    FlatStringInfo flatStrInfo = EcmaStringAccessor::FlattenAllString(thread->GetEcmaVM(), srcString);
2895    if (EcmaStringAccessor(srcString).IsTreeString()) { // use flattenedString as srcString
2896        srcString = JSHandle<EcmaString>(thread, flatStrInfo.GetString());
2897    }
2898    uint32_t nextPos = 0;
2899    uint32_t resultArrayLength = resultArray->GetLength();
2900    for (int i = 0; i < static_cast<int>(resultArrayLength); i++) {
2901        JSTaggedValue substrValue = resultArray->Get(thread, i);
2902        if (substrValue.IsHole()) {
2903            continue;
2904        }
2905        resultInfo.SetStartIndex(nextPos);
2906        if (substrValue.IsUndefined()) {
2907            uint64_t bits = resultLengthArray[i];
2908            uint32_t subLength = ReplaceLengthField::Decode(bits);
2909            uint32_t subPosition = ReplacePositionField::Decode(bits);
2910            if (isUtf8) {
2911                EcmaStringAccessor::WriteToFlatWithPos<uint8_t>(*srcString, resultInfo.GetDataUtf8Writable(),
2912                                                                subLength, subPosition);
2913            } else {
2914                EcmaStringAccessor::WriteToFlatWithPos<uint16_t>(*srcString, resultInfo.GetDataUtf16Writable(),
2915                                                                 subLength, subPosition);
2916            }
2917            nextPos += subLength;
2918        } else {
2919            EcmaString *replacementStr = EcmaString::Cast(substrValue.GetTaggedObject());
2920            uint32_t replaceLength = static_cast<uint32_t>(resultLengthArray[i]);
2921            if (isUtf8) {
2922                EcmaStringAccessor::WriteToFlat(replacementStr, resultInfo.GetDataUtf8Writable(), replaceLength);
2923            } else {
2924                EcmaStringAccessor::WriteToFlat(replacementStr, resultInfo.GetDataUtf16Writable(), replaceLength);
2925            }
2926            nextPos += replaceLength;
2927        }
2928    }
2929    return result;
2930}
2931}  // namespace panda::ecmascript::builtins
2932