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_string.h"
17
18#include <algorithm>
19#include <vector>
20#include <map>
21
22#include "ecmascript/intl/locale_helper.h"
23#include "ecmascript/builtins/builtins_number.h"
24#include "ecmascript/builtins/builtins_regexp.h"
25#include "ecmascript/builtins/builtins_symbol.h"
26#include "ecmascript/interpreter/fast_runtime_stub-inl.h"
27#include "ecmascript/js_primitive_ref.h"
28#include "ecmascript/js_regexp.h"
29#include "ecmascript/js_string_iterator.h"
30#include "ecmascript/property_detector-inl.h"
31#ifdef ARK_SUPPORT_INTL
32#include "ecmascript/js_collator.h"
33#include "ecmascript/js_locale.h"
34#else
35#ifndef ARK_NOT_SUPPORT_INTL_GLOBAL
36#include "ecmascript/intl/global_intl_helper.h"
37#endif
38#endif
39
40#include "unicode/normalizer2.h"
41#include "unicode/normlzr.h"
42#include "unicode/unistr.h"
43
44namespace panda::ecmascript::builtins {
45using ObjectFactory = ecmascript::ObjectFactory;
46using JSArray = ecmascript::JSArray;
47constexpr std::uint16_t CHAR16_LETTER_NULL = u'\0';
48
49// 21.1.1.1 String(value)
50JSTaggedValue BuiltinsString::StringConstructor(EcmaRuntimeCallInfo *argv)
51{
52    ASSERT(argv);
53    BUILTINS_API_TRACE(argv->GetThread(), String, Constructor);
54    JSThread *thread = argv->GetThread();
55    [[maybe_unused]] EcmaHandleScope handleScope(thread);
56    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
57    JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
58    if (argv->GetArgsNumber() > 0) {
59        JSHandle<JSTaggedValue> valTagNew = GetCallArg(argv, 0);
60        if (newTarget->IsUndefined() && valTagNew->IsSymbol()) {
61            return BuiltinsSymbol::SymbolDescriptiveString(thread, valTagNew.GetTaggedValue());
62        }
63        JSHandle<EcmaString> str = JSTaggedValue::ToString(thread, valTagNew);
64        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
65        if (newTarget->IsUndefined()) {
66            return str.GetTaggedValue();
67        }
68        JSHandle<JSTaggedValue> strTag(str);
69        return JSPrimitiveRef::StringCreate(thread, strTag, newTarget).GetTaggedValue();
70    }
71    JSHandle<EcmaString> val = factory->GetEmptyString();
72    JSHandle<JSTaggedValue> valTag(val);
73    if (newTarget->IsUndefined()) {
74        return factory->GetEmptyString().GetTaggedValue();
75    }
76    return JSPrimitiveRef::StringCreate(thread, valTag, newTarget).GetTaggedValue();
77}
78
79// 21.1.2.1
80JSTaggedValue BuiltinsString::FromCharCode(EcmaRuntimeCallInfo *argv)
81{
82    ASSERT(argv);
83    BUILTINS_API_TRACE(argv->GetThread(), String, FromCharCode);
84    JSThread *thread = argv->GetThread();
85    [[maybe_unused]] EcmaHandleScope handleScope(thread);
86    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
87    uint32_t argLength = argv->GetArgsNumber();
88    if (argLength == 0) {
89        return factory->GetEmptyString().GetTaggedValue();
90    }
91    if (argLength == 1) {
92        JSHandle<JSTaggedValue> codePointTag = BuiltinsString::GetCallArg(argv, 0);
93        uint16_t codePointValue = JSTaggedValue::ToUint16(thread, codePointTag);
94        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
95        if (EcmaStringAccessor::CanBeCompressed(&codePointValue, 1)) {
96            JSHandle<SingleCharTable> singleCharTable(thread, thread->GetSingleCharTable());
97            return singleCharTable->GetStringFromSingleCharTable(codePointValue);
98        }
99        JSHandle<EcmaString> strHandle = factory->NewFromUtf16Literal(&codePointValue, 1);
100        return strHandle.GetTaggedValue();
101    }
102    CVector<uint16_t> valueTable;
103    valueTable.reserve(argLength);
104    for (uint32_t i = 0; i < argLength; i++) {
105        JSHandle<JSTaggedValue> nextCp = BuiltinsString::GetCallArg(argv, i);
106        uint16_t nextCv = JSTaggedValue::ToUint16(thread, nextCp);
107        valueTable.emplace_back(nextCv);
108        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
109    }
110    return factory->NewFromUtf16Literal(valueTable.data(), valueTable.size()).GetTaggedValue();
111}
112
113// 21.1.2.2
114JSTaggedValue BuiltinsString::FromCodePoint(EcmaRuntimeCallInfo *argv)
115{
116    ASSERT(argv);
117    BUILTINS_API_TRACE(argv->GetThread(), String, FromCodePoint);
118    JSThread *thread = argv->GetThread();
119    [[maybe_unused]] EcmaHandleScope handleScope(thread);
120    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
121    uint32_t argLength = argv->GetArgsNumber();
122    if (argLength == 0) {
123        return factory->GetEmptyString().GetTaggedValue();
124    }
125    std::u16string u16str;
126    uint32_t u16strSize = argLength;
127    for (uint32_t i = 0; i < argLength; i++) {
128        JSHandle<JSTaggedValue> nextCpTag = BuiltinsString::GetCallArg(argv, i);
129        JSTaggedNumber nextCpVal = JSTaggedValue::ToNumber(thread, nextCpTag);
130        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
131        if (!nextCpVal.IsInteger()) {
132            THROW_RANGE_ERROR_AND_RETURN(thread, "is not integer", JSTaggedValue::Exception());
133        }
134        int32_t cp = nextCpVal.ToInt32();
135        if (cp < 0 || cp > ENCODE_MAX_UTF16) {
136            THROW_RANGE_ERROR_AND_RETURN(thread, "CodePoint < 0 or CodePoint > 0x10FFFF", JSTaggedValue::Exception());
137        }
138        if (cp > UINT16_MAX) {
139            uint16_t cu1 =
140                std::floor((static_cast<uint32_t>(cp) - ENCODE_SECOND_FACTOR) / ENCODE_FIRST_FACTOR) + ENCODE_LEAD_LOW;
141            uint16_t cu2 =
142                ((static_cast<uint32_t>(cp) - ENCODE_SECOND_FACTOR) % ENCODE_FIRST_FACTOR) + ENCODE_TRAIL_LOW;
143            std::u16string nextU16str1 = base::StringHelper::Utf16ToU16String(&cu1, 1);
144            std::u16string nextU16str2 = base::StringHelper::Utf16ToU16String(&cu2, 1);
145            base::StringHelper::InplaceAppend(u16str, nextU16str1);
146            base::StringHelper::InplaceAppend(u16str, nextU16str2);
147            u16strSize++;
148        } else {
149            auto u16tCp = static_cast<uint16_t>(cp);
150            std::u16string nextU16str = base::StringHelper::Utf16ToU16String(&u16tCp, 1);
151            base::StringHelper::InplaceAppend(u16str, nextU16str);
152        }
153    }
154    const char16_t *constChar16tData = u16str.data();
155    auto *char16tData = const_cast<char16_t *>(constChar16tData);
156    auto *uint16tData = reinterpret_cast<uint16_t *>(char16tData);
157    return factory->NewFromUtf16Literal(uint16tData, u16strSize).GetTaggedValue();
158}
159
160// 21.1.2.4
161JSTaggedValue BuiltinsString::Raw(EcmaRuntimeCallInfo *argv)
162{
163    ASSERT(argv);
164    BUILTINS_API_TRACE(argv->GetThread(), String, Raw);
165    JSThread *thread = argv->GetThread();
166    [[maybe_unused]] EcmaHandleScope handleScope(thread);
167    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
168    // Let cooked be ToObject(template).
169    JSHandle<JSObject> cooked = JSTaggedValue::ToObject(thread, BuiltinsString::GetCallArg(argv, 0));
170    // ReturnIfAbrupt(cooked).
171    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
172    // Let raw be ToObject(Get(cooked, "raw")).
173    JSHandle<JSTaggedValue> rawKey(factory->NewFromASCII("raw"));
174    JSHandle<JSTaggedValue> rawTag =
175        JSObject::GetProperty(thread, JSHandle<JSTaggedValue>::Cast(cooked), rawKey).GetValue();
176    JSHandle<JSObject> rawObj = JSTaggedValue::ToObject(thread, rawTag);
177    // ReturnIfAbrupt(rawObj).
178    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
179    JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
180    JSHandle<JSTaggedValue> rawLen =
181        JSObject::GetProperty(thread, JSHandle<JSTaggedValue>::Cast(rawObj), lengthKey).GetValue();
182    // ReturnIfAbrupt(rawLen).
183    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
184    JSTaggedNumber lengthNumber = JSTaggedValue::ToLength(thread, rawLen);
185    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
186    int length = static_cast<int>(lengthNumber.ToUint32());
187    if (length <= 0) {
188        return factory->GetEmptyString().GetTaggedValue();
189    }
190
191    std::u16string u16str;
192    uint32_t argc = argv->GetArgsNumber() - 1;
193    bool canBeCompress = true;
194    for (uint32_t i = 0, argsI = 1; i < static_cast<uint32_t>(length); ++i, ++argsI) {
195        // Let nextSeg be ToString(Get(raw, nextKey)).
196        JSHandle<JSTaggedValue> elementString =
197            JSObject::GetProperty(thread, JSHandle<JSTaggedValue>::Cast(rawObj), i).GetValue();
198        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
199        EcmaString *nextSeg = *JSTaggedValue::ToString(thread, elementString);
200        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
201        u16str += EcmaStringAccessor(nextSeg).ToU16String();
202        if (EcmaStringAccessor(nextSeg).IsUtf16()) {
203            canBeCompress = false;
204        }
205        if (i + 1 == static_cast<uint32_t>(length)) {
206            break;
207        }
208        if (argsI <= argc) {
209            EcmaString *nextSub = *JSTaggedValue::ToString(thread, GetCallArg(argv, argsI));
210            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
211            u16str += EcmaStringAccessor(nextSub).ToU16String();
212            if (EcmaStringAccessor(nextSub).IsUtf16()) {
213                canBeCompress = false;
214            }
215        }
216    }
217    // return the result string
218    auto *uint16tData = reinterpret_cast<uint16_t *>(const_cast<char16_t *>(u16str.data()));
219    return canBeCompress ? factory->NewFromUtf16LiteralCompress(uint16tData, u16str.size()).GetTaggedValue() :
220                           factory->NewFromUtf16LiteralNotCompress(uint16tData, u16str.size()).GetTaggedValue();
221}
222
223// 21.1.3.1
224JSTaggedValue BuiltinsString::CharAt(EcmaRuntimeCallInfo *argv)
225{
226    ASSERT(argv);
227    BUILTINS_API_TRACE(argv->GetThread(), String, CharAt);
228    JSThread *thread = argv->GetThread();
229    [[maybe_unused]] EcmaHandleScope handleScope(thread);
230    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
231    JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
232    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
233    JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
234    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
235    JSHandle<EcmaString> thisFlat(thread, EcmaStringAccessor::Flatten(thread->GetEcmaVM(), thisHandle));
236    int32_t thisLen = static_cast<int32_t>(EcmaStringAccessor(thisFlat).GetLength());
237    JSHandle<JSTaggedValue> posTag = BuiltinsString::GetCallArg(argv, 0);
238    int32_t pos = 0;
239    if (posTag->IsInt()) {
240        pos = posTag->GetInt();
241    } else if (posTag->IsUndefined()) {
242        pos = 0;
243    } else {
244        JSTaggedNumber posVal = JSTaggedValue::ToInteger(thread, posTag);
245        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
246        double valueNumber = posVal.GetNumber();
247        if (!std::isfinite(valueNumber)) {
248            return factory->GetEmptyString().GetTaggedValue();
249        }
250        pos = posVal.ToInt32();
251    }
252    if (pos < 0 || pos >= thisLen) {
253        return factory->GetEmptyString().GetTaggedValue();
254    }
255    uint16_t res = EcmaStringAccessor(thisFlat).Get<false>(pos);
256    if (EcmaStringAccessor::CanBeCompressed(&res, 1)) {
257        JSHandle<SingleCharTable> singleCharTable(thread, thread->GetSingleCharTable());
258        return singleCharTable->GetStringFromSingleCharTable(res);
259    }
260    return factory->NewFromUtf16Literal(&res, 1).GetTaggedValue();
261}
262
263// 21.1.3.2
264JSTaggedValue BuiltinsString::CharCodeAt(EcmaRuntimeCallInfo *argv)
265{
266    ASSERT(argv);
267    BUILTINS_API_TRACE(argv->GetThread(), String, CharCodeAt);
268    JSThread *thread = argv->GetThread();
269    [[maybe_unused]] EcmaHandleScope handleScope(thread);
270    JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
271    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
272    JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
273    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
274    JSHandle<EcmaString> thisFlat(thread, EcmaStringAccessor::Flatten(thread->GetEcmaVM(), thisHandle));
275    int32_t thisLen = static_cast<int32_t>(EcmaStringAccessor(thisFlat).GetLength());
276    JSHandle<JSTaggedValue> posTag = BuiltinsString::GetCallArg(argv, 0);
277    int32_t pos = 0;
278    if (posTag->IsInt()) {
279        pos = posTag->GetInt();
280    } else if (posTag->IsUndefined()) {
281        pos = 0;
282    } else {
283        JSTaggedNumber posVal = JSTaggedValue::ToInteger(thread, posTag);
284        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
285        double valueNumber = posVal.GetNumber();
286        if (!std::isfinite(valueNumber)) {
287            return GetTaggedDouble(base::NAN_VALUE);
288        }
289        pos = posVal.ToInt32();
290    }
291    if (pos < 0 || pos >= thisLen) {
292        return GetTaggedDouble(base::NAN_VALUE);
293    }
294    uint16_t ret = EcmaStringAccessor(thisFlat).Get<false>(pos);
295    return GetTaggedInt(ret);
296}
297
298// 21.1.3.3
299JSTaggedValue BuiltinsString::CodePointAt(EcmaRuntimeCallInfo *argv)
300{
301    ASSERT(argv);
302    BUILTINS_API_TRACE(argv->GetThread(), String, CodePointAt);
303    JSThread *thread = argv->GetThread();
304    [[maybe_unused]] EcmaHandleScope handleScope(thread);
305    JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
306    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
307    JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
308    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
309    JSHandle<EcmaString> thisFlat(thread, EcmaStringAccessor::Flatten(thread->GetEcmaVM(), thisHandle));
310    JSHandle<JSTaggedValue> posTag = BuiltinsString::GetCallArg(argv, 0);
311
312    JSTaggedNumber posVal = JSTaggedValue::ToNumber(thread, posTag);
313    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
314    int32_t pos = base::NumberHelper::DoubleInRangeInt32(posVal.GetNumber());
315    int32_t thisLen = static_cast<int32_t>(EcmaStringAccessor(thisFlat).GetLength());
316    if (pos < 0 || pos >= thisLen) {
317        return JSTaggedValue::Undefined();
318    }
319    uint16_t first = EcmaStringAccessor(thisFlat).Get<false>(pos);
320    if (first < base::utf_helper::DECODE_LEAD_LOW || first > base::utf_helper::DECODE_LEAD_HIGH || pos + 1 == thisLen) {
321        return GetTaggedInt(first);
322    }
323    uint16_t second = EcmaStringAccessor(thisFlat).Get<false>(pos + 1);
324    if (second < base::utf_helper::DECODE_TRAIL_LOW || second > base::utf_helper::DECODE_TRAIL_HIGH) {
325        return GetTaggedInt(first);
326    }
327    uint32_t res = base::utf_helper::UTF16Decode(first, second);
328    return GetTaggedInt(res);
329}
330
331// 21.1.3.4
332JSTaggedValue BuiltinsString::Concat(EcmaRuntimeCallInfo *argv)
333{
334    ASSERT(argv);
335    BUILTINS_API_TRACE(argv->GetThread(), String, Concat);
336    JSThread *thread = argv->GetThread();
337    [[maybe_unused]] EcmaHandleScope handleScope(thread);
338    auto ecmaVm = thread->GetEcmaVM();
339    JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
340    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
341    JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
342    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
343    uint32_t argLength = argv->GetArgsNumber();
344    if (argLength == 0) {
345        return thisHandle.GetTaggedValue();
346    }
347    for (uint32_t i = 0; i < argLength; i++) {
348        JSHandle<JSTaggedValue> nextTag = BuiltinsString::GetCallArg(argv, i);
349        JSHandle<EcmaString> nextHandle = JSTaggedValue::ToString(thread, nextTag);
350        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
351        EcmaString *tempStr = EcmaStringAccessor::Concat(ecmaVm, thisHandle, nextHandle);
352        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
353        thisHandle = JSHandle<EcmaString>(thread, tempStr);
354    }
355    return thisHandle.GetTaggedValue();
356}
357
358// 21.1.3.5 String.prototype.constructor
359// 21.1.3.6
360JSTaggedValue BuiltinsString::EndsWith(EcmaRuntimeCallInfo *argv)
361{
362    ASSERT(argv);
363    BUILTINS_API_TRACE(argv->GetThread(), String, EndsWith);
364    JSThread *thread = argv->GetThread();
365    [[maybe_unused]] EcmaHandleScope handleScope(thread);
366    JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
367    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
368    JSHandle<JSTaggedValue> searchTag = BuiltinsString::GetCallArg(argv, 0);
369    JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
370    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
371    bool isRegexp = JSObject::IsRegExp(thread, searchTag);
372    if (isRegexp) {
373        THROW_TYPE_ERROR_AND_RETURN(thread, "is regexp", JSTaggedValue::Exception());
374    }
375    JSHandle<EcmaString> searchHandle = JSTaggedValue::ToString(thread, searchTag);
376    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
377    uint32_t thisLen = EcmaStringAccessor(thisHandle).GetLength();
378    uint32_t searchLen = EcmaStringAccessor(searchHandle).GetLength();
379    int32_t pos = 0;
380    JSHandle<JSTaggedValue> posTag = BuiltinsString::GetCallArg(argv, 1);
381    if (posTag->IsUndefined()) {
382        pos = static_cast<int32_t>(thisLen);
383    } else {
384        JSTaggedNumber posVal = JSTaggedValue::ToInteger(thread, posTag);
385        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
386        if (posVal.GetNumber() == BuiltinsNumber::POSITIVE_INFINITY) {
387            pos = static_cast<int32_t>(thisLen);
388        } else {
389            pos = base::NumberHelper::DoubleInRangeInt32(posVal.GetNumber());
390        }
391    }
392    pos = std::min(std::max(pos, 0), static_cast<int32_t>(thisLen));
393    int32_t start = pos - static_cast<int32_t>(searchLen);
394    if (start < 0) {
395        return BuiltinsString::GetTaggedBoolean(false);
396    }
397
398    int32_t idx = EcmaStringAccessor::IndexOf(thread->GetEcmaVM(), thisHandle, searchHandle, start);
399    if (idx == start) {
400        return BuiltinsString::GetTaggedBoolean(true);
401    }
402    return BuiltinsString::GetTaggedBoolean(false);
403}
404
405// 21.1.3.7
406JSTaggedValue BuiltinsString::Includes(EcmaRuntimeCallInfo *argv)
407{
408    ASSERT(argv);
409    BUILTINS_API_TRACE(argv->GetThread(), String, Includes);
410    JSThread *thread = argv->GetThread();
411    [[maybe_unused]] EcmaHandleScope handleScope(thread);
412    JSHandle<JSTaggedValue> searchTag = BuiltinsString::GetCallArg(argv, 0);
413    JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
414    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
415    JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
416    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
417    bool isRegexp = JSObject::IsRegExp(thread, searchTag);
418    if (isRegexp) {
419        THROW_TYPE_ERROR_AND_RETURN(thread, "is regexp", JSTaggedValue::Exception());
420    }
421    JSHandle<EcmaString> searchHandle = JSTaggedValue::ToString(thread, searchTag);
422    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
423    uint32_t thisLen = EcmaStringAccessor(thisHandle).GetLength();
424    int32_t pos = 0;
425    JSHandle<JSTaggedValue> posTag = BuiltinsBase::GetCallArg(argv, 1);
426    if (argv->GetArgsNumber() == 1) {
427        pos = 0;
428    } else {
429        JSTaggedNumber posVal = JSTaggedValue::ToNumber(thread, posTag);
430        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
431        pos = base::NumberHelper::DoubleInRangeInt32(posVal.GetNumber());
432    }
433    int32_t start = std::min(std::max(pos, 0), static_cast<int32_t>(thisLen));
434    int32_t idx = EcmaStringAccessor::IndexOf(thread->GetEcmaVM(), thisHandle, searchHandle, start);
435    if (idx < 0 || idx > static_cast<int32_t>(thisLen)) {
436        return BuiltinsString::GetTaggedBoolean(false);
437    }
438    return BuiltinsString::GetTaggedBoolean(true);
439}
440
441// 21.1.3.8
442JSTaggedValue BuiltinsString::IndexOf(EcmaRuntimeCallInfo *argv)
443{
444    ASSERT(argv);
445    BUILTINS_API_TRACE(argv->GetThread(), String, IndexOf);
446    JSThread *thread = argv->GetThread();
447    [[maybe_unused]] EcmaHandleScope handleScope(thread);
448    JSHandle<JSTaggedValue> searchTag = BuiltinsString::GetCallArg(argv, 0);
449    JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
450    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
451    JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
452    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
453    uint32_t thisLen = EcmaStringAccessor(thisHandle).GetLength();
454    JSHandle<EcmaString> searchHandle = JSTaggedValue::ToString(thread, searchTag);
455    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
456    JSHandle<JSTaggedValue> posTag = BuiltinsString::GetCallArg(argv, 1);
457    int32_t pos = 0;
458    if (posTag->IsInt()) {
459        pos = posTag->GetInt();
460    } else if (posTag->IsUndefined()) {
461        pos = 0;
462    } else {
463        JSTaggedNumber posVal = JSTaggedValue::ToInteger(thread, posTag);
464        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
465        pos = posVal.ToInt32();
466    }
467    pos = std::min(std::max(pos, 0), static_cast<int32_t>(thisLen));
468    // If searching for an null string.
469    if (EcmaStringAccessor(searchHandle).GetLength() == 0) {
470        return GetTaggedInt(pos);
471    }
472    int32_t res = EcmaStringAccessor::IndexOf(thread->GetEcmaVM(), thisHandle, searchHandle, pos);
473    if (res >= 0 && res < static_cast<int32_t>(thisLen)) {
474        return GetTaggedInt(res);
475    }
476    return GetTaggedInt(-1);
477}
478
479// 21.1.3.9
480JSTaggedValue BuiltinsString::LastIndexOf(EcmaRuntimeCallInfo *argv)
481{
482    ASSERT(argv);
483    BUILTINS_API_TRACE(argv->GetThread(), String, LastIndexOf);
484    JSThread *thread = argv->GetThread();
485    [[maybe_unused]] EcmaHandleScope handleScope(thread);
486    JSHandle<JSTaggedValue> searchTag = BuiltinsString::GetCallArg(argv, 0);
487    JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
488    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
489    JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
490    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
491    int32_t thisLen = static_cast<int32_t>(EcmaStringAccessor(thisHandle).GetLength());
492    JSHandle<EcmaString> searchHandle = JSTaggedValue::ToString(thread, searchTag);
493    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
494    int32_t pos = 0;
495    if (argv->GetArgsNumber() == 1) {
496        pos = thisLen;
497    } else {
498        JSHandle<JSTaggedValue> posTag = BuiltinsString::GetCallArg(argv, 1);
499        JSTaggedNumber posVal = JSTaggedValue::ToInteger(thread, posTag);
500        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
501        if (std::isnan(JSTaggedValue::ToNumber(thread, posTag).GetNumber())) {
502            pos = thisLen;
503        } else {
504            pos = posVal.ToInt32();
505        }
506        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
507    }
508    pos = std::min(std::max(pos, 0), thisLen);
509    int32_t res = EcmaStringAccessor::LastIndexOf(thread->GetEcmaVM(), thisHandle, searchHandle, pos);
510    if (res >= 0 && res < thisLen) {
511        return GetTaggedInt(res);
512    }
513    res = -1;
514    return GetTaggedInt(static_cast<int32_t>(res));
515}
516
517// 21.1.3.10
518JSTaggedValue BuiltinsString::LocaleCompare(EcmaRuntimeCallInfo *argv)
519{
520    ASSERT(argv);
521    BUILTINS_API_TRACE(argv->GetThread(), String, LocaleCompare);
522    JSThread *thread = argv->GetThread();
523    [[maybe_unused]] EcmaHandleScope handleScope(thread);
524    JSHandle<JSTaggedValue> thatTag = BuiltinsString::GetCallArg(argv, 0);
525    JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
526    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
527    [[maybe_unused]] JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
528    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
529    [[maybe_unused]] JSHandle<EcmaString> thatHandle = JSTaggedValue::ToString(thread, thatTag);
530    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
531
532    JSHandle<JSTaggedValue> locales = GetCallArg(argv, 1);
533    JSHandle<JSTaggedValue> options = GetCallArg(argv, 2); // 2: the second argument
534    return DoLocaleCompare(thread, thisHandle, thatHandle, locales, options);
535}
536
537JSTaggedValue BuiltinsString::DoLocaleCompare(JSThread *thread,
538                                              const JSHandle<EcmaString> &thisHandle,
539                                              const JSHandle<EcmaString> &thatHandle,
540                                              const JSHandle<JSTaggedValue> &locales,
541                                              const JSHandle<JSTaggedValue> &options)
542{
543    [[maybe_unused]] bool cacheable = (locales->IsUndefined() || locales->IsString()) && options->IsUndefined();
544    const CompareStringsOption csOption = JSCollator::CompareStringsOptionFor(thread, locales, options);
545#ifdef ARK_SUPPORT_INTL
546    if (cacheable) {
547        auto collator = JSCollator::GetCachedIcuCollator(thread, locales);
548        if (collator != nullptr) {
549            JSTaggedValue result = JSCollator::CompareStrings(thread, collator, thisHandle, thatHandle, csOption);
550            return result;
551        }
552    }
553    return LocaleCompareGC(thread, thisHandle, thatHandle, locales, options, csOption, cacheable);
554#else
555#ifdef ARK_NOT_SUPPORT_INTL_GLOBAL
556    ARK_SUPPORT_INTL_RETURN_JSVALUE(thread, "LocaleCompare");
557#else
558    intl::GlobalIntlHelper gh(thread, intl::GlobalFormatterType::Collator);
559    auto collator = gh.GetGlobalObject<intl::GlobalCollator>(thread,
560        locales, options, intl::GlobalFormatterType::Collator, cacheable);
561    if (collator == nullptr) {
562        LOG_ECMA(ERROR) << "BuiltinsString::LocaleCompare:collator is nullptr";
563    }
564    ASSERT(collator != nullptr);
565    auto result = collator->Compare(EcmaStringAccessor(thisHandle).ToStdString(),
566        EcmaStringAccessor(thatHandle).ToStdString());
567    return JSTaggedValue(result);
568#endif
569#endif
570}
571
572JSTaggedValue BuiltinsString::LocaleCompareGC(JSThread *thread,
573                                              const JSHandle<EcmaString> &thisHandle,
574                                              const JSHandle<EcmaString> &thatHandle,
575                                              const JSHandle<JSTaggedValue> &locales,
576                                              const JSHandle<JSTaggedValue> &options,
577                                              CompareStringsOption csOption,
578                                              bool cacheable)
579{
580    EcmaVM *ecmaVm = thread->GetEcmaVM();
581    ObjectFactory *factory = ecmaVm->GetFactory();
582    JSHandle<JSTaggedValue> ctor = ecmaVm->GetGlobalEnv()->GetCollatorFunction();
583    JSHandle<JSCollator> collator =
584        JSHandle<JSCollator>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(ctor)));
585    JSHandle<JSCollator> initCollator =
586        JSCollator::InitializeCollator(thread, collator, locales, options, cacheable);
587    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
588    icu::Collator *icuCollator = nullptr;
589    if (cacheable) {
590        icuCollator = JSCollator::GetCachedIcuCollator(thread, locales);
591        ASSERT(icuCollator != nullptr);
592    } else {
593        icuCollator = initCollator->GetIcuCollator();
594    }
595    JSTaggedValue result = JSCollator::CompareStrings(thread, icuCollator, thisHandle, thatHandle, csOption);
596    return result;
597}
598
599
600// 21.1.3.11
601JSTaggedValue BuiltinsString::Match(EcmaRuntimeCallInfo *argv)
602{
603    ASSERT(argv);
604    BUILTINS_API_TRACE(argv->GetThread(), String, Match);
605    JSThread *thread = argv->GetThread();
606    [[maybe_unused]] EcmaHandleScope handleScope(thread);
607    const GlobalEnvConstants *globalConst = thread->GlobalConstants();
608    JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
609    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
610    JSHandle<JSTaggedValue> regexp = BuiltinsString::GetCallArg(argv, 0);
611    if (thisTag->IsString() && regexp->IsECMAObject()) {
612        if (BuiltinsRegExp::IsFastRegExp(thread, regexp, BuiltinsRegExp::RegExpSymbol::MATCH)) {
613            return BuiltinsRegExp::RegExpMatch(thread, regexp, thisTag, true);
614        }
615    }
616
617    JSHandle<JSTaggedValue> matchTag = thread->GetEcmaVM()->GetGlobalEnv()->GetMatchSymbol();
618    JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
619    if (!regexp->IsUndefined() && !regexp->IsNull()) {
620        JSHandle<JSTaggedValue> matcher = JSObject::GetMethod(thread, regexp, matchTag);
621        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
622        if (!matcher->IsUndefined()) {
623            ASSERT(matcher->IsJSFunctionBase());
624            EcmaRuntimeCallInfo *info =
625                EcmaInterpreter::NewRuntimeCallInfo(thread, matcher, regexp, undefined, 1);
626            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
627            info->SetCallArg(thisTag.GetTaggedValue());
628            return JSFunction::Call(info);
629        }
630    }
631    JSHandle<EcmaString> thisVal = JSTaggedValue::ToString(thread, thisTag);
632    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
633    JSHandle<JSTaggedValue> undifinedHandle = globalConst->GetHandledUndefined();
634    JSHandle<JSTaggedValue> rx(thread, BuiltinsRegExp::RegExpCreate(thread, regexp, undifinedHandle));
635    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
636    EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, rx, undefined, 1);
637    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
638    info->SetCallArg(thisVal.GetTaggedValue());
639    return JSFunction::Invoke(info, matchTag);
640}
641
642JSTaggedValue BuiltinsString::MatchAll(EcmaRuntimeCallInfo *argv)
643{
644    ASSERT(argv);
645    BUILTINS_API_TRACE(argv->GetThread(), String, MatchAll);
646    JSThread *thread = argv->GetThread();
647    [[maybe_unused]] EcmaHandleScope handleScope(thread);
648    const GlobalEnvConstants *globalConst = thread->GlobalConstants();
649    // 1. Let O be ? RequireObjectCoercible(this value).
650    JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
651    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
652    JSHandle<JSTaggedValue> regexp = BuiltinsString::GetCallArg(argv, 0);
653    EcmaVM *ecmaVm = thread->GetEcmaVM();
654    JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
655    JSHandle<JSTaggedValue> matchAllTag = env->GetMatchAllSymbol();
656    JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
657
658    // 2. If regexp is neither undefined nor null, then
659    if (!regexp->IsUndefined() && !regexp->IsNull()) {
660        // a. Let isRegExp be ? IsRegExp(searchValue).
661        if (regexp->IsECMAObject() &&
662            BuiltinsRegExp::IsFastRegExp(thread, regexp, BuiltinsRegExp::RegExpSymbol::MATCH)) {
663            bool isGlobal = BuiltinsRegExp::GetOriginalFlag(thread, regexp, RegExpParser::FLAG_GLOBAL);
664            if (!isGlobal) {
665                THROW_TYPE_ERROR_AND_RETURN(thread,
666                                            "matchAll called with a non-global RegExp argument",
667                                            JSTaggedValue::Exception());
668            }
669        } else if (JSObject::IsRegExp(thread, regexp)) {
670            // i. Let flags be ? Get(searchValue, "flags").
671            JSHandle<JSTaggedValue> flagsString(globalConst->GetHandledFlagsString());
672            JSHandle<JSTaggedValue> flags = JSObject::GetProperty(thread, regexp, flagsString).GetValue();
673            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
674            // ii. Perform ? RequireObjectCoercible(flags).
675            JSTaggedValue::RequireObjectCoercible(thread, flags);
676            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
677            // iii. If ? ToString(flags) does not contain "g", throw a TypeError exception.
678            JSHandle<EcmaString> flagString = JSTaggedValue::ToString(thread, flags);
679            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
680            int32_t pos = EcmaStringAccessor::IndexOf(ecmaVm,
681                flagString, ecmaVm->GetFactory()->NewFromASCII("g"));
682            if (pos == -1) {
683                THROW_TYPE_ERROR_AND_RETURN(thread,
684                                            "matchAll called with a non-global RegExp argument",
685                                            JSTaggedValue::Exception());
686            }
687        }
688
689        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
690        if (thisTag->IsString() && regexp->IsECMAObject()) {
691            if (PropertyDetector::IsRegExpSpeciesDetectorValid(env) &&
692                BuiltinsRegExp::IsFastRegExp(thread, regexp, BuiltinsRegExp::RegExpSymbol::MATCHALL)) {
693                JSHandle<EcmaString> string = JSHandle<EcmaString>::Cast(thisTag);
694                return BuiltinsRegExp::RegExpMatchAll(thread, regexp, string, true);
695            }
696        }
697        // c. Let matcher be ? GetMethod(regexp, @@matchAll).
698        // d. If matcher is not undefined, then
699        bool canSkip = (PropertyDetector::IsNumberStringNotRegexpLikeDetectorValid(env) &&
700                       (regexp->IsString() || regexp->IsNumber()));
701        if (!canSkip) {
702            JSHandle<JSTaggedValue> matcher = JSObject::GetMethod(thread, regexp, matchAllTag);
703            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
704            if (!matcher->IsUndefined()) {
705                // i. Return ? Call(matcher, regexp, « O »).
706                EcmaRuntimeCallInfo *info =
707                    EcmaInterpreter::NewRuntimeCallInfo(thread, matcher, regexp, undefined, 1);
708                RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
709                info->SetCallArg(thisTag.GetTaggedValue());
710                return JSFunction::Call(info);
711            }
712        }
713    }
714    // 3. Let S be ? ToString(O).
715    JSHandle<EcmaString> thisVal = JSTaggedValue::ToString(thread, thisTag);
716    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
717    // 4. Let rx be ? RegExpCreate(regexp, "g").
718    JSHandle<JSTaggedValue> rx(thread, BuiltinsRegExp::RegExpCreate(
719        thread, regexp, JSHandle<JSTaggedValue>(ecmaVm->GetFactory()->NewFromASCII("g"))));
720    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
721    EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, rx, undefined, 1);
722    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
723    info->SetCallArg(thisVal.GetTaggedValue());
724    return JSFunction::Invoke(info, matchAllTag);
725}
726
727JSTaggedValue BuiltinsString::IsWellFormed(EcmaRuntimeCallInfo *argv)
728{
729    ASSERT(argv);
730    BUILTINS_API_TRACE(argv->GetThread(), String, IsWellFormed);
731    JSThread *thread = argv->GetThread();
732    [[maybe_unused]] EcmaHandleScope handleScope(thread);
733
734    // 1. Let O be ? RequireObjectCoercible(this value).
735    JSHandle<JSTaggedValue> obj(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
736    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
737
738    // 2. Let S be ? ToString(O).
739    JSHandle<EcmaString> string = JSTaggedValue::ToString(thread, obj);
740    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
741
742    // 3. Return IsStringWellFormedUnicode(S).
743    uint32_t size = EcmaStringAccessor(string).GetLength();
744    uint32_t position = 0;
745    while (position < size) {
746        // i.Let first be the code unit at index position within string.
747        uint16_t first = EcmaStringAccessor(string).Get(position);
748        uint32_t cp = first - CHAR16_LETTER_NULL;
749        uint8_t codeUnitCount = 0;
750        bool isUnpairedSurrogate = false;
751        // ii. If first is neither a leading surrogate nor a trailing surrogate, then
752        //   a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: false }.
753        if (!IsUTF16HighSurrogate(first) && !IsUTF16LowSurrogate(first)) {
754            codeUnitCount = 1; // 1 means: code unit count
755            isUnpairedSurrogate = false;
756        } else if (IsUTF16HighSurrogate(first) && position + 1 == size) {
757            // iii. If first is a trailing surrogate or position + 1 = size, then
758            //   a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }.
759            codeUnitCount = 1;
760            isUnpairedSurrogate = true;
761        } else {
762            // iv. Let second be the code unit at index position + 1 within string.
763            uint16_t second = EcmaStringAccessor(string).Get(position + 1);
764            // v. If second is not a trailing surrogate, then
765            //   a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }.
766            if (!IsUTF16LowSurrogate(second)) {
767                codeUnitCount = 1; // 1 means: code unit count
768                isUnpairedSurrogate = true;
769            } else {
770            // vi. Set cp to UTF16SurrogatePairToCodePoint(first, second).
771            // vii. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 2, [[IsUnpairedSurrogate]]: false }.
772                cp = UTF16SurrogatePairToCodePoint(first, second);
773                codeUnitCount = 2; // 2 means: code unit count
774                isUnpairedSurrogate = false;
775            }
776        }
777        if (isUnpairedSurrogate) {
778            return JSTaggedValue::False();
779        } else {
780            position = position + codeUnitCount;
781        }
782        thread->CheckSafepointIfSuspended();
783    }
784    return JSTaggedValue::True();
785}
786
787JSTaggedValue BuiltinsString::ToWellFormed(EcmaRuntimeCallInfo *argv)
788{
789    ASSERT(argv);
790    BUILTINS_API_TRACE(argv->GetThread(), String, ToWellFormed);
791    JSThread *thread = argv->GetThread();
792    [[maybe_unused]] EcmaHandleScope handleScope(thread);
793    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
794
795    // 1. Let O be ? RequireObjectCoercible(this value).
796    JSHandle<JSTaggedValue> obj(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
797    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
798
799    // 2. Let S be ? ToString(O).
800    JSHandle<EcmaString> string = JSTaggedValue::ToString(thread, obj);
801    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
802
803    // 3. Let strLen be the length of S.
804    // 4. Let k be 0.
805    uint32_t size = EcmaStringAccessor(string).GetLength();
806    uint32_t position = 0;
807
808    // 5. Let result be the empty String.
809    std::u16string r;
810
811    // Repeat, while k < strLen,
812    //     a. Let cp be CodePointAt(S, k).
813    //     b. If cp.[[IsUnpairedSurrogate]] is true, then
814    //         i. Set result to the string-concatenation of result and 0xFFFD (REPLACEMENT CHARACTER).
815    //     c. Else,
816    //         i. Set result to the string-concatenation of result and UTF16EncodeCodePoint(cp.[[CodePoint]]).
817    //     d. Set k to k + cp.[[CodeUnitCount]].
818    while (position < size) {
819        // i.Let first be the code unit at index position within string.
820        uint16_t first = EcmaStringAccessor(string).Get(position);
821        uint32_t cp = first - CHAR16_LETTER_NULL;
822        uint8_t codeUnitCount = 0;
823        bool isUnpairedSurrogate = false;
824        // ii. If first is neither a leading surrogate nor a trailing surrogate, then
825        //   a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: false }.
826        if (!IsUTF16HighSurrogate(first) && !IsUTF16LowSurrogate(first)) {
827            codeUnitCount = 1; // 1 means: code unit count
828            isUnpairedSurrogate = false;
829        } else if (IsUTF16HighSurrogate(first) && position + 1 == size) {
830            // iii. If first is a trailing surrogate or position + 1 = size, then
831            //   a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }.
832            codeUnitCount = 1;
833            isUnpairedSurrogate = true;
834        } else {
835            // iv. Let second be the code unit at index position + 1 within string.
836            uint16_t second = EcmaStringAccessor(string).Get(position + 1);
837            // v. If second is not a trailing surrogate, then
838            //   a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }.
839            if (!IsUTF16LowSurrogate(second)) {
840                codeUnitCount = 1; // 1 means: code unit count
841                isUnpairedSurrogate = true;
842            } else {
843            // vi. Set cp to UTF16SurrogatePairToCodePoint(first, second).
844            // vii. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 2, [[IsUnpairedSurrogate]]: false }.
845                cp = UTF16SurrogatePairToCodePoint(first, second);
846                codeUnitCount = 2; // 2 means: code unit count
847                isUnpairedSurrogate = false;
848            }
849        }
850        if (isUnpairedSurrogate) {
851            r.push_back(0xFFFD);
852        } else {
853            if (cp < 0 || cp > ENCODE_MAX_UTF16) {
854                THROW_RANGE_ERROR_AND_RETURN(thread, "CodePoint < 0 or CodePoint > 0x10FFFF",
855                                             JSTaggedValue::Exception());
856            }
857            if (cp > UINT16_MAX) {
858                uint16_t cu1 = std::floor((static_cast<uint32_t>(cp) - ENCODE_SECOND_FACTOR) /
859                                           ENCODE_FIRST_FACTOR) + ENCODE_LEAD_LOW;
860                uint16_t cu2 = ((static_cast<uint32_t>(cp) - ENCODE_SECOND_FACTOR) %
861                                 ENCODE_FIRST_FACTOR) + ENCODE_TRAIL_LOW;
862                std::u16string nextU16str1 = base::StringHelper::Utf16ToU16String(&cu1, 1);
863                std::u16string nextU16str2 = base::StringHelper::Utf16ToU16String(&cu2, 1);
864                base::StringHelper::InplaceAppend(r, nextU16str1);
865                base::StringHelper::InplaceAppend(r, nextU16str2);
866            } else {
867                auto u16tCp = static_cast<uint16_t>(cp);
868                std::u16string nextU16str = base::StringHelper::Utf16ToU16String(&u16tCp, 1);
869                base::StringHelper::InplaceAppend(r, nextU16str);
870            }
871        }
872        position = position + codeUnitCount;
873        thread->CheckSafepointIfSuspended();
874    }
875    const char16_t *constChar16tData = r.data();
876    auto *char16tData = const_cast<char16_t *>(constChar16tData);
877    auto *uint16tData = reinterpret_cast<uint16_t *>(char16tData);
878    uint32_t u16strSize = r.size();
879    return factory->NewFromUtf16Literal(uint16tData, u16strSize).GetTaggedValue();
880}
881
882// Static Semantics: UTF16SurrogatePairToCodePoint ( lead, trail )
883uint32_t BuiltinsString::UTF16SurrogatePairToCodePoint(uint16_t lead, uint16_t trail)
884{
885    // 1. Assert: lead is a leading surrogate and trail is a trailing surrogate.
886    ASSERT(IsUTF16HighSurrogate(lead) && IsUTF16LowSurrogate(trail));
887    // 2. Let cp be (lead - 0xD800) × 0x400 + (trail - 0xDC00) + 0x10000.
888    uint32_t cp = ((lead - 0xD800) << 10UL) + (trail - 0xDC00) + 0x10000;
889    // 3. Return the code point cp.
890    return cp;
891}
892
893// 21.1.3.12
894JSTaggedValue BuiltinsString::Normalize(EcmaRuntimeCallInfo *argv)
895{
896    ASSERT(argv);
897    BUILTINS_API_TRACE(argv->GetThread(), String, Normalize);
898    JSThread *thread = argv->GetThread();
899    [[maybe_unused]] EcmaHandleScope handleScope(thread);
900    auto vm = thread->GetEcmaVM();
901    JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
902    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
903    JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
904    RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
905    JSHandle<EcmaString> formValue;
906    if (argv->GetArgsNumber() == 0) {
907        formValue = JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledNfcString());
908    } else {
909        JSHandle<JSTaggedValue> formTag = BuiltinsString::GetCallArg(argv, 0);
910        if (formTag->IsUndefined()) {
911            formValue = JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledNfcString());
912        } else {
913            formValue = JSTaggedValue::ToString(thread, formTag);
914            RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
915        }
916    }
917    UNormalizationMode uForm;
918    if (JSHandle<EcmaString> nfc =
919        JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledNfcString());
920        EcmaStringAccessor::StringsAreEqual(vm, formValue, nfc)) {
921        uForm = UNORM_NFC;
922    } else if (JSHandle<EcmaString> nfd =
923        JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledNfdString());
924        EcmaStringAccessor::StringsAreEqual(vm, formValue, nfd)) {
925        uForm = UNORM_NFD;
926    } else if (JSHandle<EcmaString> nfkc =
927        JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledNfkcString());
928        EcmaStringAccessor::StringsAreEqual(vm, formValue, nfkc)) {
929        uForm = UNORM_NFKC;
930    } else if (JSHandle<EcmaString> nfkd =
931        JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledNfkdString());
932        EcmaStringAccessor::StringsAreEqual(vm, formValue, nfkd)) {
933        uForm = UNORM_NFKD;
934    } else {
935        THROW_RANGE_ERROR_AND_RETURN(thread, "compare not equal", JSTaggedValue::Exception());
936    }
937
938    std::u16string u16strThis = EcmaStringAccessor(thisHandle).ToU16String();
939    const char16_t *constChar16tData = u16strThis.data();
940    icu::UnicodeString src(constChar16tData, u16strThis.size());
941    icu::UnicodeString res;
942    UErrorCode errorCode = U_ZERO_ERROR;
943    int32_t option = 0;
944
945    icu::Normalizer::normalize(src, uForm, option, res, errorCode);
946    JSHandle<EcmaString> str = intl::LocaleHelper::UStringToString(thread, res);
947    return JSTaggedValue(*str);
948}
949
950JSTaggedValue BuiltinsString::PadStart(EcmaRuntimeCallInfo *argv)
951{
952    ASSERT(argv);
953    BUILTINS_API_TRACE(argv->GetThread(), String, PadStart);
954    return BuiltinsString::Pad(argv, true);
955}
956
957JSTaggedValue BuiltinsString::PadEnd(EcmaRuntimeCallInfo *argv)
958{
959    ASSERT(argv);
960    BUILTINS_API_TRACE(argv->GetThread(), String, PadEnd);
961    return BuiltinsString::Pad(argv, false);
962}
963
964// 21.1.3.13
965JSTaggedValue BuiltinsString::Repeat(EcmaRuntimeCallInfo *argv)
966{
967    ASSERT(argv);
968    BUILTINS_API_TRACE(argv->GetThread(), String, Repeat);
969    JSThread *thread = argv->GetThread();
970    [[maybe_unused]] EcmaHandleScope handleScope(thread);
971    JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
972    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
973    JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
974    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
975    uint32_t thisLen = EcmaStringAccessor(thisHandle).GetLength();
976    JSHandle<JSTaggedValue> countTag = BuiltinsString::GetCallArg(argv, 0);
977    int32_t count = 0;
978    if (countTag->IsInt()) {
979        count = countTag->GetInt();
980    } else {
981        JSTaggedNumber num = JSTaggedValue::ToInteger(thread, countTag);
982        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
983        double d = num.GetNumber();
984        if (d == base::POSITIVE_INFINITY) {
985            THROW_RANGE_ERROR_AND_RETURN(thread, "is infinity", JSTaggedValue::Exception());
986        }
987        count = base::NumberHelper::DoubleInRangeInt32(d);
988    }
989    if (count < 0) {
990        THROW_RANGE_ERROR_AND_RETURN(thread, "less than 0", JSTaggedValue::Exception());
991    }
992    if (count == 0) {
993        auto emptyStr = thread->GetEcmaVM()->GetFactory()->GetEmptyString();
994        return emptyStr.GetTaggedValue();
995    }
996    if (thisLen == 0) {
997        return thisHandle.GetTaggedValue();
998    }
999    if (static_cast<uint32_t>(count) >= static_cast<uint32_t>(EcmaString::MAX_STRING_LENGTH) / thisLen) {
1000        THROW_RANGE_ERROR_AND_RETURN(thread, "Invalid string length", JSTaggedValue::Exception());
1001    }
1002    bool isUtf8 = EcmaStringAccessor(thisHandle).IsUtf8();
1003    EcmaString *result = EcmaStringAccessor::CreateLineString(thread->GetEcmaVM(), thisLen * count, isUtf8);
1004    for (uint32_t index = 0; index < static_cast<uint32_t>(count); ++index) {
1005        EcmaStringAccessor::ReadData(result, *thisHandle, index * thisLen, (count - index) * thisLen, thisLen);
1006    }
1007    return JSTaggedValue(result);
1008}
1009
1010// 21.1.3.14
1011JSTaggedValue BuiltinsString::Replace(EcmaRuntimeCallInfo *argv)
1012{
1013    ASSERT(argv);
1014    BUILTINS_API_TRACE(argv->GetThread(), String, Replace);
1015    JSThread *thread = argv->GetThread();
1016    [[maybe_unused]] EcmaHandleScope handleScope(thread);
1017    JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, BuiltinsString::GetThis(argv));
1018    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1019
1020    auto ecmaVm = thread->GetEcmaVM();
1021    JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
1022    const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1023    JSHandle<JSTaggedValue> searchTag = BuiltinsString::GetCallArg(argv, 0);
1024    JSHandle<JSTaggedValue> replaceTag = BuiltinsString::GetCallArg(argv, 1);
1025
1026    ObjectFactory *factory = ecmaVm->GetFactory();
1027
1028    if (searchTag->IsJSRegExp() && replaceTag->IsString()) {
1029        JSHandle<RegExpExecResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetRegExpCache());
1030        JSHandle<JSRegExp> re(searchTag);
1031        JSHandle<JSTaggedValue> pattern(thread, re->GetOriginalSource());
1032        JSHandle<JSTaggedValue> flags(thread, re->GetOriginalFlags());
1033        bool isFastPath = BuiltinsRegExp::IsFastRegExp(thread, searchTag);
1034        if (isFastPath) {
1035            uint32_t lastIndex = static_cast<uint32_t>(BuiltinsRegExp::GetLastIndex(thread, searchTag, true));
1036            JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, thisTag,
1037                RegExpExecResultCache::REPLACE_TYPE, searchTag, JSTaggedValue(lastIndex),
1038                replaceTag);
1039            if (!cacheResult.IsUndefined()) {
1040                return cacheResult;
1041            }
1042        }
1043    }
1044
1045    if (searchTag->IsJSRegExp() && PropertyDetector::IsRegExpReplaceDetectorValid(env)) {
1046        JSTaggedValue proto = JSObject::GetPrototype(JSHandle<JSObject>(searchTag));
1047        if (proto == env->GetTaggedRegExpPrototype()) {
1048            return BuiltinsRegExp::ReplaceInternal(thread, searchTag, thisTag, replaceTag);
1049        }
1050    }
1051
1052    // If searchValue is neither undefined nor null, then
1053    if (!searchTag->IsUndefined() && !searchTag->IsNull()) {
1054        JSHandle<JSTaggedValue> replaceKey = env->GetReplaceSymbol();
1055        // Let replacer be GetMethod(searchValue, @@replace).
1056        JSHandle<JSTaggedValue> replaceMethod = JSObject::GetMethod(thread, searchTag, replaceKey);
1057        // ReturnIfAbrupt(replacer).
1058        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1059        // If replacer is not undefined, then
1060        if (!replaceMethod->IsUndefined()) {
1061            // Return Call(replacer, searchValue, «O, replaceValue»).
1062            const uint32_t argsLength = 2;
1063            JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
1064            EcmaRuntimeCallInfo *info =
1065                EcmaInterpreter::NewRuntimeCallInfo(thread, replaceMethod, searchTag, undefined, argsLength);
1066            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1067            info->SetCallArg(thisTag.GetTaggedValue(), replaceTag.GetTaggedValue());
1068            return JSFunction::Call(info);
1069        }
1070    }
1071
1072    // Let string be ToString(O).
1073    JSHandle<EcmaString> thisString = JSTaggedValue::ToString(thread, thisTag);
1074    // ReturnIfAbrupt(string).
1075    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1076    // Let searchString be ToString(searchValue).
1077    JSHandle<EcmaString> searchString = JSTaggedValue::ToString(thread, searchTag);
1078    // ReturnIfAbrupt(searchString).
1079    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1080    // Let functionalReplace be IsCallable(replaceValue).
1081    if (!replaceTag->IsCallable()) {
1082        // If functionalReplace is false, then
1083        // Let replaceValue be ToString(replaceValue).
1084        // ReturnIfAbrupt(replaceValue)
1085        replaceTag = JSHandle<JSTaggedValue>(JSTaggedValue::ToString(thread, replaceTag));
1086        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1087    }
1088    // Search string for the first occurrence of searchString and let pos be the index within string of the first code
1089    // unit of the matched substring and let matched be searchString. If no occurrences of searchString were found,
1090    // return string.
1091    int32_t pos = EcmaStringAccessor::IndexOf(ecmaVm, thisString, searchString);
1092    if (pos == -1) {
1093        return thisString.GetTaggedValue();
1094    }
1095    JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
1096    JSMutableHandle<JSTaggedValue> replHandle(thread, factory->GetEmptyString().GetTaggedValue());
1097    // If functionalReplace is true, then
1098    if (replaceTag->IsCallable()) {
1099        // Let replValue be Call(replaceValue, undefined,«matched, pos, and string»).
1100        const uint32_t argsLength = 3; // 3: «matched, pos, and string»
1101        EcmaRuntimeCallInfo *info =
1102            EcmaInterpreter::NewRuntimeCallInfo(thread, replaceTag, undefined, undefined, argsLength);
1103        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1104        info->SetCallArg(searchString.GetTaggedValue(), JSTaggedValue(pos), thisString.GetTaggedValue());
1105        JSTaggedValue replStrDeocodeValue = JSFunction::Call(info);
1106        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1107        replHandle.Update(replStrDeocodeValue);
1108    } else {
1109        // Let captures be an empty List.
1110        JSHandle<TaggedArray> capturesList = factory->EmptyArray();
1111        ASSERT_PRINT(replaceTag->IsString(), "replace must be string");
1112        JSHandle<EcmaString> replacement(thread, replaceTag->GetTaggedObject());
1113        // Let replStr be GetSubstitution(matched, string, pos, captures, replaceValue)
1114        replHandle.Update(GetSubstitution(thread, searchString, thisString, pos, capturesList, undefined, replacement));
1115    }
1116    JSHandle<EcmaString> realReplaceStr = JSTaggedValue::ToString(thread, replHandle);
1117    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1118    // Let tailPos be pos + the number of code units in matched.
1119    int32_t tailPos = pos + static_cast<int32_t>(EcmaStringAccessor(searchString).GetLength());
1120    // Let newString be the String formed by concatenating the first pos code units of string,
1121    // replStr, and the trailing
1122    // substring of string starting at index tailPos. If pos is 0,
1123    // the first element of the concatenation will be the
1124    // empty String.
1125    // Return newString.
1126    JSHandle<EcmaString> prefixString(thread, EcmaStringAccessor::FastSubString(ecmaVm, thisString, 0, pos));
1127    auto thisLen = EcmaStringAccessor(thisString).GetLength();
1128    JSHandle<EcmaString> suffixString(thread,
1129        EcmaStringAccessor::FastSubString(ecmaVm, thisString, tailPos, thisLen - tailPos));
1130    EcmaString *tempStr = EcmaStringAccessor::Concat(ecmaVm, prefixString, realReplaceStr);
1131    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1132    JSHandle<EcmaString> tempString(thread, tempStr);
1133    EcmaString *resultStr = EcmaStringAccessor::Concat(ecmaVm, tempString, suffixString);
1134    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1135    return JSTaggedValue(resultStr);
1136}
1137
1138JSTaggedValue BuiltinsString::ReplaceAll(EcmaRuntimeCallInfo *argv)
1139{
1140    ASSERT(argv);
1141    JSThread *thread = argv->GetThread();
1142    BUILTINS_API_TRACE(thread, String, ReplaceAll);
1143    [[maybe_unused]] EcmaHandleScope handleScope(thread);
1144    JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, BuiltinsString::GetThis(argv));
1145    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1146
1147    auto ecmaVm = thread->GetEcmaVM();
1148    JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
1149    const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1150    JSHandle<JSTaggedValue> searchTag = BuiltinsString::GetCallArg(argv, 0);
1151    JSHandle<JSTaggedValue> replaceTag = BuiltinsString::GetCallArg(argv, 1);
1152
1153    ObjectFactory *factory = ecmaVm->GetFactory();
1154
1155    if (!searchTag->IsUndefined() && !searchTag->IsNull()) {
1156        // a. Let isRegExp be ? IsRegExp(searchValue).
1157        bool isJSRegExp = JSObject::IsRegExp(thread, searchTag);
1158        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1159        // b. If isRegExp is true, then
1160        if (isJSRegExp) {
1161            // i. Let flags be ? Get(searchValue, "flags").
1162            JSHandle<JSTaggedValue> flagsString(globalConst->GetHandledFlagsString());
1163            JSHandle<JSTaggedValue> flags = JSObject::GetProperty(thread, searchTag, flagsString).GetValue();
1164            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1165            // ii. Perform ? RequireObjectCoercible(flags).
1166            JSTaggedValue::RequireObjectCoercible(thread, flags);
1167            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1168            // iii. If ? ToString(flags) does not contain "g", throw a TypeError exception.
1169            JSHandle<EcmaString> flagString = JSTaggedValue::ToString(thread, flags);
1170            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1171            JSHandle<EcmaString> gString(globalConst->GetHandledGString());
1172            int32_t pos = EcmaStringAccessor::IndexOf(ecmaVm, flagString, gString);
1173            if (pos == -1) {
1174                THROW_TYPE_ERROR_AND_RETURN(thread,
1175                                            "string.prototype.replaceAll called with a non-global RegExp argument",
1176                                            JSTaggedValue::Exception());
1177            }
1178        }
1179        // c. Let replacer be ? GetMethod(searchValue, @@replace).
1180        JSHandle<JSTaggedValue> replaceKey = env->GetReplaceSymbol();
1181        JSHandle<JSTaggedValue> replaceMethod = JSObject::GetMethod(thread, searchTag, replaceKey);
1182        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1183        // d. If replacer is not undefined, then
1184        if (!replaceMethod->IsUndefined()) {
1185            // i. Return ? Call(replacer, searchValue, «O, replaceValue»).
1186            const size_t argsLength = 2;
1187            JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
1188            EcmaRuntimeCallInfo *info =
1189                EcmaInterpreter::NewRuntimeCallInfo(thread, replaceMethod, searchTag, undefined, argsLength);
1190            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1191            info->SetCallArg(thisTag.GetTaggedValue(), replaceTag.GetTaggedValue());
1192            return JSFunction::Call(info);
1193        }
1194    }
1195
1196    // 3. Let string be ? ToString(O).
1197    JSHandle<EcmaString> thisString = JSTaggedValue::ToString(thread, thisTag);
1198    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1199    // 4. Let searchString be ? ToString(searchValue).
1200    JSHandle<EcmaString> searchString = JSTaggedValue::ToString(thread, searchTag);
1201    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1202    // 5. Let functionalReplace be IsCallable(replaceValue).
1203    // 6. If functionalReplace is false, then
1204    if (!replaceTag->IsCallable()) {
1205        // a. Set replaceValue to ? ToString(replaceValue).
1206        replaceTag = JSHandle<JSTaggedValue>(JSTaggedValue::ToString(thread, replaceTag));
1207        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1208    }
1209
1210    // 7. Let searchLength be the length of searchString.
1211    // 8. Let advanceBy be max(1, searchLength).
1212    int32_t searchLength = static_cast<int32_t>(EcmaStringAccessor(searchString).GetLength());
1213    int32_t advanceBy = std::max(1, searchLength);
1214    // 9. Let matchPositions be a new empty List.
1215    JSMutableHandle<EcmaString> accumulatedResult(thread, factory->GetEmptyString());
1216    // 10. Let position be ! StringIndexOf(string, searchString, 0).
1217    int32_t pos = EcmaStringAccessor::IndexOf(ecmaVm, thisString, searchString);
1218    int32_t endOfLastMatch = 0;
1219    JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
1220    JSMutableHandle<JSTaggedValue> replHandle(thread, factory->GetEmptyString().GetTaggedValue());
1221    while (pos != -1) {
1222        // If functionalReplace is true, then
1223        if (replaceTag->IsCallable()) {
1224            // Let replValue be Call(replaceValue, undefined,«matched, pos, and string»).
1225            const uint32_t argsLength = 3; // 3: «matched, pos, and string»
1226            EcmaRuntimeCallInfo *info =
1227                EcmaInterpreter::NewRuntimeCallInfo(thread, replaceTag, undefined, undefined, argsLength);
1228            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1229            info->SetCallArg(searchString.GetTaggedValue(), JSTaggedValue(pos), thisString.GetTaggedValue());
1230            JSTaggedValue replStrDeocodeValue = JSFunction::Call(info);
1231            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1232            replHandle.Update(replStrDeocodeValue);
1233        } else {
1234            // Let captures be an empty List.
1235            JSHandle<TaggedArray> capturesList = factory->NewTaggedArray(0);
1236            ASSERT_PRINT(replaceTag->IsString(), "replace must be string");
1237            JSHandle<EcmaString> replacement(thread, replaceTag->GetTaggedObject());
1238            // Let replStr be GetSubstitution(matched, string, pos, captures, replaceValue)
1239            replHandle.Update(GetSubstitution(thread, searchString, thisString, pos,
1240                                              capturesList, undefined, replacement));
1241        }
1242        JSHandle<EcmaString> realReplaceStr = JSTaggedValue::ToString(thread, replHandle);
1243        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1244        // Let tailPos be pos + the number of code units in matched.
1245        // Let newString be the String formed by concatenating the first pos code units of string,
1246        // replStr, and the trailing substring of string starting at index tailPos.
1247        // If pos is 0, the first element of the concatenation will be the
1248        // empty String.
1249        // Return newString.
1250        JSHandle<EcmaString> prefixString(thread,
1251                                          EcmaStringAccessor::FastSubString(ecmaVm, thisString, endOfLastMatch,
1252                                                                            pos - endOfLastMatch));
1253        accumulatedResult.Update(JSTaggedValue(EcmaStringAccessor::Concat(ecmaVm, accumulatedResult, prefixString)));
1254        accumulatedResult.Update(JSTaggedValue(EcmaStringAccessor::Concat(ecmaVm, accumulatedResult, realReplaceStr)));
1255        endOfLastMatch = pos + searchLength;
1256        pos = EcmaStringAccessor::IndexOf(ecmaVm, thisString, searchString, pos + advanceBy);
1257        thread->CheckSafepointIfSuspended();
1258    }
1259
1260    if (endOfLastMatch < static_cast<int32_t>(EcmaStringAccessor(thisString).GetLength())) {
1261        auto thisLen = EcmaStringAccessor(thisString).GetLength();
1262        JSHandle<EcmaString> suffixString(thread,
1263            EcmaStringAccessor::FastSubString(ecmaVm, thisString, endOfLastMatch, thisLen - endOfLastMatch));
1264        accumulatedResult.Update(JSTaggedValue(EcmaStringAccessor::Concat(ecmaVm, accumulatedResult, suffixString)));
1265    }
1266
1267    return accumulatedResult.GetTaggedValue();
1268}
1269
1270// Handle $& - match case
1271void ProcessDollarAmpersand(std::u16string &stringBuilder, const JSHandle<EcmaString> &matched, bool &canBeCompress)
1272{
1273    stringBuilder += EcmaStringAccessor(matched).ToU16String();
1274    if (EcmaStringAccessor(matched).IsUtf16()) {
1275        canBeCompress = false;
1276    }
1277}
1278
1279// Handle $` - prefix case
1280void ProcessDollarBacktick(EcmaVM *ecmaVm, std::u16string &stringBuilder, const JSHandle<EcmaString> &srcString,
1281                           int position, bool &canBeCompress)
1282{
1283    if (position > 0) {
1284        EcmaString *prefix = EcmaStringAccessor::FastSubString(ecmaVm, srcString, 0, position);
1285        stringBuilder += EcmaStringAccessor(prefix).ToU16String();
1286        if (EcmaStringAccessor(prefix).IsUtf16()) {
1287            canBeCompress = false;
1288        }
1289    }
1290}
1291
1292// Handle $' - suffix case
1293void ProcessDollarSingleQuote(EcmaVM *ecmaVm, std::u16string &stringBuilder, const JSHandle<EcmaString> &srcString,
1294                              int tailPos, bool &canBeCompress)
1295{
1296    int32_t srcLength = static_cast<int32_t>(EcmaStringAccessor(srcString).GetLength());
1297    if (tailPos < srcLength) {
1298        EcmaString *suffix = EcmaStringAccessor::FastSubString(ecmaVm, srcString, tailPos, srcLength - tailPos);
1299        stringBuilder += EcmaStringAccessor(suffix).ToU16String();
1300        if (EcmaStringAccessor(suffix).IsUtf16()) {
1301            canBeCompress = false;
1302        }
1303    }
1304}
1305
1306std::pair<int32_t, bool> ProcessDigitCapture(const JSHandle<EcmaString> &replacementFlat, uint32_t peekIndex,
1307                                             uint32_t replaceLength, const JSHandle<TaggedArray> &captureList,
1308                                             std::u16string &stringBuilder)
1309{
1310    uint32_t capturesLength = captureList->GetLength();
1311    uint16_t peek = EcmaStringAccessor(replacementFlat).Get(peekIndex);
1312    uint32_t scaledIndex = peek - '0';
1313    int32_t advance = 1;
1314    bool canBeCompress = true;
1315
1316    if (peekIndex + 1 < replaceLength) {
1317        uint16_t nextPeek = EcmaStringAccessor(replacementFlat).Get(peekIndex + 1);
1318        if (nextPeek >= '0' && nextPeek <= '9') {
1319            constexpr uint32_t TEN_BASE = 10;
1320            uint32_t newScaledIndex = scaledIndex * TEN_BASE + (nextPeek - '0');
1321            if (newScaledIndex <= capturesLength) {
1322                scaledIndex = newScaledIndex;
1323                advance = 2;  // 2: 2 means from index needs to add two.
1324            }
1325        }
1326    }
1327
1328    if (scaledIndex == 0 || scaledIndex > capturesLength) {
1329        stringBuilder += '$';
1330        return {peekIndex, canBeCompress};  // No change in compressibility, just return the next index.
1331    }
1332
1333    JSTaggedValue capturesVal(captureList->Get(scaledIndex - 1));
1334    if (!capturesVal.IsUndefined()) {
1335        EcmaString *captureString = EcmaString::Cast(capturesVal.GetTaggedObject());
1336        stringBuilder += EcmaStringAccessor(captureString).ToU16String();
1337        if (EcmaStringAccessor(captureString).IsUtf16()) {
1338            canBeCompress = false;
1339        }
1340    }
1341    return {static_cast<int32_t>(peekIndex) + advance, canBeCompress};
1342}
1343
1344// Handle $< case
1345std::pair<int32_t, bool> ProcessNamedCaptures(JSThread *thread, const JSHandle<EcmaString> &replacementFlat,
1346                                              int32_t peekIndex, const JSHandle<JSTaggedValue> &namedCaptures,
1347                                              std::u16string &stringBuilder)
1348{
1349    bool canBeCompress = true;
1350    if (namedCaptures->IsUndefined()) {
1351        stringBuilder += '$';
1352        return {peekIndex, canBeCompress};
1353    }
1354    auto ecmaVm = thread->GetEcmaVM();
1355    ObjectFactory *factory = ecmaVm->GetFactory();
1356    JSHandle<EcmaString> greaterSymString = factory->NewFromASCII(">");
1357    int32_t pos = EcmaStringAccessor::IndexOf(ecmaVm, replacementFlat, greaterSymString, peekIndex);
1358    if (pos == -1) {
1359        stringBuilder += '$';
1360        return {peekIndex, canBeCompress};
1361    }
1362    JSHandle<EcmaString> groupName = JSHandle<EcmaString>(
1363        thread, EcmaStringAccessor::FastSubString(ecmaVm, replacementFlat, peekIndex + 1, pos - peekIndex - 1));
1364    JSHandle<JSTaggedValue> names(groupName);
1365    JSHandle<JSTaggedValue> capture = JSObject::GetProperty(thread, namedCaptures, names).GetValue();
1366    if (capture->IsUndefined()) {
1367        return {pos + 1, canBeCompress};
1368    }
1369    JSHandle<EcmaString> captureName = JSTaggedValue::ToString(thread, capture);
1370    stringBuilder += EcmaStringAccessor(captureName).ToU16String();
1371    if (EcmaStringAccessor(captureName).IsUtf16()) {
1372        canBeCompress = false;
1373    }
1374    return {pos + 1, canBeCompress};
1375}
1376
1377JSTaggedValue BuiltinsString::GetSubstitution(JSThread *thread, const JSHandle<EcmaString> &matched,
1378                                              const JSHandle<EcmaString> &srcString, int position,
1379                                              const JSHandle<TaggedArray> &captureList,
1380                                              const JSHandle<JSTaggedValue> &namedCaptures,
1381                                              const JSHandle<EcmaString> &replacement)
1382{
1383    BUILTINS_API_TRACE(thread, String, GetSubstitution);
1384    auto ecmaVm = thread->GetEcmaVM();
1385    ObjectFactory *factory = ecmaVm->GetFactory();
1386    JSHandle<EcmaString> dollarString = JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledDollarString());
1387    JSHandle<EcmaString> replacementFlat(thread, EcmaStringAccessor::Flatten(ecmaVm, replacement));
1388    int32_t replaceLength = static_cast<int32_t>(EcmaStringAccessor(replacementFlat).GetLength());
1389    int32_t tailPos = position + static_cast<int32_t>(EcmaStringAccessor(matched).GetLength());
1390
1391    int32_t nextDollarIndex = EcmaStringAccessor::IndexOf(ecmaVm, replacementFlat, dollarString);
1392    if (nextDollarIndex < 0) {
1393        return replacementFlat.GetTaggedValue();
1394    }
1395    std::u16string stringBuilder;
1396    bool canBeCompress = true;
1397    if (nextDollarIndex > 0) {
1398        stringBuilder = EcmaStringAccessor(replacementFlat).ToU16String(nextDollarIndex);
1399        if (EcmaStringAccessor(replacementFlat).IsUtf16()) {
1400            canBeCompress = false;
1401        }
1402    }
1403
1404    while (true) {
1405        int peekIndex = nextDollarIndex + 1;
1406        if (peekIndex >= replaceLength) {
1407            stringBuilder += '$';
1408            auto *char16tData = const_cast<char16_t *>(stringBuilder.c_str());
1409            auto *uint16tData = reinterpret_cast<uint16_t *>(char16tData);
1410            return canBeCompress ?
1411                   factory->NewFromUtf16LiteralCompress(uint16tData, stringBuilder.length()).GetTaggedValue() :
1412                   factory->NewFromUtf16LiteralNotCompress(uint16tData, stringBuilder.length()).GetTaggedValue();
1413        }
1414        int continueFromIndex = -1;
1415        uint16_t peek = EcmaStringAccessor(replacementFlat).Get(peekIndex);
1416        switch (peek) {
1417            case '$':  // $$
1418                stringBuilder += '$';
1419                continueFromIndex = peekIndex + 1;
1420                break;
1421            case '&':  // $& - match
1422                ProcessDollarAmpersand(stringBuilder, matched, canBeCompress);
1423                continueFromIndex = peekIndex + 1;
1424                break;
1425            case '`':  // $` - prefix
1426                ProcessDollarBacktick(ecmaVm, stringBuilder, srcString, position, canBeCompress);
1427                continueFromIndex = peekIndex + 1;
1428                break;
1429            case '\'': {  // $' - suffix
1430                ProcessDollarSingleQuote(ecmaVm, stringBuilder, srcString, tailPos, canBeCompress);
1431                continueFromIndex = peekIndex + 1;
1432                break;
1433            }
1434            case '0':
1435            case '1':
1436            case '2':
1437            case '3':
1438            case '4':
1439            case '5':
1440            case '6':
1441            case '7':
1442            case '8':
1443            case '9': {
1444                auto result =
1445                    ProcessDigitCapture(replacementFlat, peekIndex, replaceLength, captureList, stringBuilder);
1446                continueFromIndex = result.first;
1447                canBeCompress = result.second && canBeCompress;  // 保留canBeCompress的值,只在需要时更新为false
1448                break;
1449            }
1450            case '<': {
1451                auto result = ProcessNamedCaptures(thread, replacementFlat, peekIndex, namedCaptures, stringBuilder);
1452                continueFromIndex = result.first;
1453                canBeCompress = result.second && canBeCompress;  // 保留canBeCompress的值,只在需要时更新为false
1454                break;
1455            }
1456            default:
1457                stringBuilder += '$';
1458                continueFromIndex = peekIndex;
1459                break;
1460        }
1461        // Go the the next $ in the replacement.
1462        nextDollarIndex = EcmaStringAccessor::IndexOf(ecmaVm, replacementFlat, dollarString, continueFromIndex);
1463        if (nextDollarIndex < 0) {
1464            if (continueFromIndex < replaceLength) {
1465                EcmaString *nextAppend = EcmaStringAccessor::FastSubString(ecmaVm, replacementFlat, continueFromIndex,
1466                                                                           replaceLength - continueFromIndex);
1467                stringBuilder += EcmaStringAccessor(nextAppend).ToU16String();
1468                if (EcmaStringAccessor(nextAppend).IsUtf16()) {
1469                    canBeCompress = false;
1470                }
1471            }
1472            auto *char16tData = const_cast<char16_t *>(stringBuilder.c_str());
1473            auto *uint16tData = reinterpret_cast<uint16_t *>(char16tData);
1474            return canBeCompress ?
1475                   factory->NewFromUtf16LiteralCompress(uint16tData, stringBuilder.length()).GetTaggedValue() :
1476                   factory->NewFromUtf16LiteralNotCompress(uint16tData, stringBuilder.length()).GetTaggedValue();
1477        }
1478        // Append substring between the previous and the next $ character.
1479        if (nextDollarIndex > continueFromIndex) {
1480            EcmaString *nextAppend = EcmaStringAccessor::FastSubString(
1481                ecmaVm, replacementFlat, continueFromIndex, nextDollarIndex - continueFromIndex);
1482            stringBuilder += EcmaStringAccessor(nextAppend).ToU16String();
1483            if (EcmaStringAccessor(nextAppend).IsUtf16()) {
1484                canBeCompress = false;
1485            }
1486        }
1487        thread->CheckSafepointIfSuspended();
1488    }
1489    LOG_ECMA(FATAL) << "this branch is unreachable";
1490    UNREACHABLE();
1491}
1492
1493// 21.1.3.15
1494JSTaggedValue BuiltinsString::Search(EcmaRuntimeCallInfo *argv)
1495{
1496    ASSERT(argv);
1497    BUILTINS_API_TRACE(argv->GetThread(), String, Search);
1498    JSThread *thread = argv->GetThread();
1499    [[maybe_unused]] EcmaHandleScope handleScope(thread);
1500    const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1501    JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1502    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1503    JSHandle<JSTaggedValue> regexp = BuiltinsString::GetCallArg(argv, 0);
1504    if (thisTag->IsString() && regexp->IsECMAObject()) {
1505        if (BuiltinsRegExp::IsFastRegExp(thread, regexp, BuiltinsRegExp::RegExpSymbol::SEARCH)) {
1506            return BuiltinsRegExp::RegExpSearchFast(thread, regexp, thisTag);
1507        }
1508    }
1509    JSHandle<JSTaggedValue> searchTag = thread->GetEcmaVM()->GetGlobalEnv()->GetSearchSymbol();
1510    JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
1511    if (!regexp->IsUndefined() && !regexp->IsNull()) {
1512        JSHandle<JSTaggedValue> searcher = JSObject::GetMethod(thread, regexp, searchTag);
1513        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1514        if (!searcher->IsUndefined()) {
1515            ASSERT(searcher->IsJSFunctionBase());
1516            EcmaRuntimeCallInfo *info =
1517                EcmaInterpreter::NewRuntimeCallInfo(thread, searcher, regexp, undefined, 1);
1518            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1519            info->SetCallArg(thisTag.GetTaggedValue());
1520            return JSFunction::Call(info);
1521        }
1522    }
1523    JSHandle<EcmaString> thisVal = JSTaggedValue::ToString(thread, thisTag);
1524    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1525    JSHandle<JSTaggedValue> rx(thread, BuiltinsRegExp::RegExpCreate(thread, regexp, undefined));
1526    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1527    EcmaRuntimeCallInfo *info =
1528        EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, rx, undefined, 1);
1529    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1530    info->SetCallArg(thisVal.GetTaggedValue());
1531    return JSFunction::Invoke(info, searchTag);
1532}
1533
1534// 21.1.3.16
1535JSTaggedValue BuiltinsString::Slice(EcmaRuntimeCallInfo *argv)
1536{
1537    ASSERT(argv);
1538    BUILTINS_API_TRACE(argv->GetThread(), String, Slice);
1539    JSThread *thread = argv->GetThread();
1540    [[maybe_unused]] EcmaHandleScope handleScope(thread);
1541
1542    JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1543    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1544    JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
1545    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1546    int32_t thisLen = static_cast<int32_t>(EcmaStringAccessor(thisHandle).GetLength());
1547    JSHandle<JSTaggedValue> startTag = BuiltinsString::GetCallArg(argv, 0);
1548    JSTaggedNumber startVal = JSTaggedValue::ToInteger(thread, startTag);
1549    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1550    int32_t start = ConvertDoubleToInt(startVal.GetNumber());
1551    int32_t end = 0;
1552    JSHandle<JSTaggedValue> endTag = BuiltinsString::GetCallArg(argv, 1);
1553    if (endTag->IsUndefined()) {
1554        end = thisLen;
1555    } else {
1556        JSTaggedNumber endVal = JSTaggedValue::ToInteger(thread, endTag);
1557        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1558        end = ConvertDoubleToInt(endVal.GetNumber());
1559    }
1560    int32_t from = 0;
1561    int32_t to = 0;
1562    if (start < 0) {
1563        from = std::max(start + thisLen, 0);
1564    } else {
1565        from = std::min(start, thisLen);
1566    }
1567    if (end < 0) {
1568        to = std::max(end + thisLen, 0);
1569    } else {
1570        to = std::min(end, thisLen);
1571    }
1572    int32_t len = std::max(to - from, 0);
1573    return JSTaggedValue(EcmaStringAccessor::FastSubString(thread->GetEcmaVM(), thisHandle, from, len));
1574}
1575
1576// 21.1.3.17
1577JSTaggedValue BuiltinsString::Split(EcmaRuntimeCallInfo *argv)
1578{
1579    ASSERT(argv);
1580    BUILTINS_API_TRACE(argv->GetThread(), String, Split);
1581    JSThread *thread = argv->GetThread();
1582    [[maybe_unused]] EcmaHandleScope handleScope(thread);
1583    auto ecmaVm = thread->GetEcmaVM();
1584    JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
1585
1586    // Let O be RequireObjectCoercible(this value).
1587    JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv));
1588    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1589    JSHandle<JSTaggedValue> seperatorTag = BuiltinsString::GetCallArg(argv, 0);
1590    JSHandle<JSTaggedValue> limitTag = BuiltinsString::GetCallArg(argv, 1);
1591
1592    if (thisTag->IsString() && seperatorTag->IsECMAObject()) {
1593        // this condition need change, all regexp should use RegExpSplit
1594        if (BuiltinsRegExp::IsFastRegExp(thread, seperatorTag)) {
1595            return BuiltinsRegExp::RegExpSplit(thread, seperatorTag, thisTag, limitTag, true);
1596        }
1597    }
1598    if (thisTag->IsString() && seperatorTag->IsString()) {
1599        JSHandle<EcmaString> thisString(thisTag);
1600        JSHandle<EcmaString> seperatorString(seperatorTag);
1601        auto thisLength = EcmaStringAccessor(thisString).GetLength();
1602        auto seperatorLength = EcmaStringAccessor(seperatorString).GetLength();
1603        if (limitTag->IsUndefined() && thisLength != 0 && seperatorLength != 0) {
1604            return CreateArrayThisStringAndSeperatorStringAreNotEmpty(
1605                thread, ecmaVm, thisString, seperatorString, thisLength, seperatorLength);
1606        }
1607        uint32_t lim = UINT32_MAX - 1;
1608        if (!limitTag->IsUndefined()) {
1609            JSTaggedNumber limitIntValue = JSTaggedValue::ToInteger(thread, limitTag);
1610            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1611            lim = limitIntValue.ToUint32();
1612        }
1613        // ReturnIfAbrupt(lim).
1614        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1615        if (lim == 0) {
1616            JSHandle<JSObject> resultArray(JSArray::ArrayCreate(thread, JSTaggedNumber(0)));
1617            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1618            return resultArray.GetTaggedValue();
1619        }
1620        return CreateArrayBySplitString(thread, ecmaVm, thisString, seperatorString, thisLength, seperatorLength, lim);
1621    }
1622
1623    // If separator is neither undefined nor null, then
1624    if (!seperatorTag->IsUndefined() && !seperatorTag->IsNull()) {
1625        JSHandle<JSTaggedValue> splitKey = env->GetSplitSymbol();
1626        // Let splitter be GetMethod(separator, @@split).
1627        JSHandle<JSTaggedValue> splitter = JSObject::GetMethod(thread, seperatorTag, splitKey);
1628        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1629        if (!splitter->IsUndefined()) {
1630            // Return Call(splitter, separator, «‍O, limit»).
1631            const uint32_t argsLength = 2;
1632            JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1633            EcmaRuntimeCallInfo *info =
1634                EcmaInterpreter::NewRuntimeCallInfo(thread, splitter, seperatorTag, undefined, argsLength);
1635            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1636            info->SetCallArg(thisTag.GetTaggedValue(), limitTag.GetTaggedValue());
1637            return JSFunction::Call(info);
1638        }
1639    }
1640    // Let S be ToString(O).
1641    JSHandle<EcmaString> thisString = JSTaggedValue::ToString(thread, thisTag);
1642    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1643
1644    // If limit is undefined, let lim = 2^53–1; else let lim = ToLength(limit).
1645    uint32_t lim = UINT32_MAX - 1;
1646    if (!limitTag->IsUndefined()) {
1647        JSTaggedNumber limitIntValue = JSTaggedValue::ToInteger(thread, limitTag);
1648        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1649        lim = limitIntValue.ToUint32();
1650    }
1651    // ReturnIfAbrupt(lim).
1652    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1653    // Let s be the number of elements in S.
1654    auto thisLength = EcmaStringAccessor(thisString).GetLength();
1655    JSHandle<EcmaString> seperatorString = JSTaggedValue::ToString(thread, seperatorTag);
1656    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1657    // If lim = 0, return A.
1658    if (lim == 0) {
1659        JSHandle<JSObject> resultArray(JSArray::ArrayCreate(thread, JSTaggedNumber(0)));
1660        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1661        return resultArray.GetTaggedValue();
1662    }
1663    auto seperatorLength = EcmaStringAccessor(seperatorString).GetLength();
1664    // If S is undefined or (this.length = 0 and S.length != 0), return array of size is 1 containing this string
1665    if (seperatorTag->IsUndefined()) {
1666        JSHandle<JSObject> resultArray(JSArray::ArrayCreate(thread, JSTaggedNumber(1)));
1667        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1668        // Perform CreateDataProperty(A, "0", S), CreateDataProperty's fast path
1669        JSObject::CreateDataProperty(thread, resultArray, 0, JSHandle<JSTaggedValue>(thisString));
1670        ASSERT_PRINT(!thread->HasPendingException(), "CreateDataProperty(A, \"0\", S) can't throw exception");
1671        return resultArray.GetTaggedValue();
1672    }
1673    return CreateArrayBySplitString(thread, ecmaVm, thisString, seperatorString, thisLength, seperatorLength, lim);
1674}
1675
1676JSTaggedValue BuiltinsString::CreateArrayFromString(JSThread *thread, EcmaVM *ecmaVm,
1677    const JSHandle<EcmaString> &thisString, uint32_t thisLength, uint32_t lim)
1678{
1679    bool isUtf8 = EcmaStringAccessor(thisString).IsUtf8();
1680    bool canBeCompressed = false;
1681    if (EcmaStringAccessor(thisString).IsLineOrConstantString()) {
1682        canBeCompressed = EcmaStringAccessor::CanBeCompressed(*thisString);
1683    }
1684    bool isOneByte = isUtf8 & canBeCompressed;
1685    JSHandle<EcmaString> seperatorString = thread->GetEcmaVM()->GetFactory()->GetEmptyString();
1686    if (lim == UINT32_MAX - 1) {
1687        JSHandle<StringSplitResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetStringSplitResultCache());
1688        JSTaggedValue cacheResult = StringSplitResultCache::FindCachedResult(thread, cacheTable, thisString,
1689            seperatorString, isOneByte);
1690        if (cacheResult != JSTaggedValue::Undefined()) {
1691            JSHandle<JSTaggedValue> resultArray(JSArray::CreateArrayFromList(thread,
1692                JSHandle<TaggedArray>(thread, cacheResult)));
1693            return resultArray.GetTaggedValue();
1694        }
1695    }
1696    uint32_t actualLength = std::min(thisLength, lim);
1697    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1698    JSHandle<TaggedArray> array = factory->NewTaggedArray(actualLength);
1699    for (uint32_t i = 0; i < actualLength; ++i) {
1700        EcmaString *elementString = EcmaStringAccessor::GetSubString(ecmaVm, thisString, i, 1);
1701        // Perform CreateDataProperty(A, "0", S), CreateDataProperty's fast path
1702        if (isOneByte) {
1703            array->Set<false>(thread, i, JSTaggedValue(elementString));
1704        } else {
1705            array->Set(thread, i, JSTaggedValue(elementString));
1706        }
1707        ASSERT_PRINT(!thread->HasPendingException(), "CreateDataProperty can't throw exception");
1708    }
1709    JSHandle<JSArray> resultArray = JSArray::CreateArrayFromList(thread, array);
1710    if (lim == UINT32_MAX - 1) {
1711        JSHandle<StringSplitResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetStringSplitResultCache());
1712        StringSplitResultCache::SetCachedResult(thread, cacheTable, thisString, seperatorString, array);
1713    }
1714    return resultArray.GetTaggedValue();
1715}
1716
1717JSTaggedValue BuiltinsString::CreateArrayBySplitString(JSThread *thread, EcmaVM *ecmaVm,
1718    const JSHandle<EcmaString> &thisString, const JSHandle<EcmaString> &seperatorString,
1719    uint32_t thisLength, uint32_t seperatorLength, uint32_t lim)
1720{
1721    if (thisLength != 0) {
1722        if (seperatorLength != 0) {
1723            return CreateArrayThisStringAndSeperatorStringAreNotEmpty(
1724                thread, ecmaVm, thisString, seperatorString, thisLength, seperatorLength, lim);
1725        }
1726        return CreateArrayFromString(thread, ecmaVm, thisString, thisLength, lim);
1727    } else {
1728        if (seperatorLength != 0) {
1729            JSHandle<JSObject> resultArray(JSArray::ArrayCreate(thread, JSTaggedNumber(1)));
1730            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1731            // Perform CreateDataProperty(A, "0", S), CreateDataProperty's fast path
1732            JSObject::CreateDataProperty(thread, resultArray, 0, JSHandle<JSTaggedValue>(thisString));
1733            ASSERT_PRINT(!thread->HasPendingException(), "CreateDataProperty(A, \"0\", S) can't throw exception");
1734            return resultArray.GetTaggedValue();
1735        }
1736        JSHandle<JSObject> resultArray(JSArray::ArrayCreate(thread, JSTaggedNumber(0)));
1737        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1738        return resultArray.GetTaggedValue();
1739    }
1740}
1741
1742JSTaggedValue BuiltinsString::CreateArrayThisStringAndSeperatorStringAreNotEmpty(JSThread *thread,
1743    EcmaVM *ecmaVm, const JSHandle<EcmaString> &thisString, const JSHandle<EcmaString> &seperatorString,
1744    uint32_t thisLength, uint32_t seperatorLength, uint32_t lim)
1745{
1746    if (lim == UINT32_MAX - 1) {
1747        JSHandle<StringSplitResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetStringSplitResultCache());
1748        JSTaggedValue cacheResult = StringSplitResultCache::FindCachedResult(thread, cacheTable, thisString,
1749            seperatorString);
1750        if (cacheResult != JSTaggedValue::Undefined()) {
1751            JSHandle<JSTaggedValue> resultArray(JSArray::CreateArrayFromList(thread,
1752                JSHandle<TaggedArray>(thread, cacheResult)));
1753            return resultArray.GetTaggedValue();
1754        }
1755    }
1756    uint32_t arrayLength = 0;
1757    std::vector<int32_t> posArray;
1758    int32_t index = 0;
1759    int32_t pos = EcmaStringAccessor::IndexOf(ecmaVm, thisString, seperatorString);
1760    while (pos != -1) {
1761        posArray.emplace_back(pos);
1762        ++arrayLength;
1763        if (arrayLength == lim) {
1764            break;
1765        }
1766        index = pos + static_cast<int32_t>(seperatorLength);
1767        pos = EcmaStringAccessor::IndexOf(ecmaVm, thisString, seperatorString, index);
1768        thread->CheckSafepointIfSuspended();
1769    }
1770    uint32_t posArrLength = posArray.size();
1771    arrayLength = lim > posArrLength ? posArrLength + 1 : posArrLength;
1772    return JSArray::ArrayCreateWithInit(thread, arrayLength,
1773        [thread, ecmaVm, &thisString, &seperatorString, &posArray, thisLength, seperatorLength, lim, posArrLength]
1774        (const JSHandle<TaggedArray> &newElements, [[maybe_unused]] uint32_t length) {
1775        int32_t index = 0;
1776        int32_t pos = 0;
1777        JSMutableHandle<JSTaggedValue> elementTag(thread, JSTaggedValue::Undefined());
1778        for (uint32_t i = 0; i < posArrLength; i++) {
1779            pos = posArray[i];
1780            EcmaString *elementString = EcmaStringAccessor::GetSubString(ecmaVm, thisString, index, pos - index);
1781            elementTag.Update(JSTaggedValue(elementString));
1782            newElements->Set(thread, i, elementTag);
1783            index = pos + static_cast<int32_t>(seperatorLength);
1784        }
1785        if (lim > posArrLength) {
1786            EcmaString *elementString =
1787                EcmaStringAccessor::GetSubString(ecmaVm, thisString, index, thisLength - index);
1788            elementTag.Update(JSTaggedValue(elementString));
1789            newElements->Set(thread, posArrLength, elementTag);
1790        }
1791        if (lim == UINT32_MAX - 1) {
1792            JSHandle<StringSplitResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetStringSplitResultCache());
1793            StringSplitResultCache::SetCachedResult(thread, cacheTable, thisString, seperatorString, newElements);
1794        }
1795    });
1796}
1797
1798// 21.1.3.18
1799JSTaggedValue BuiltinsString::StartsWith(EcmaRuntimeCallInfo *argv)
1800{
1801    ASSERT(argv);
1802    BUILTINS_API_TRACE(argv->GetThread(), String, StartsWith);
1803    JSThread *thread = argv->GetThread();
1804    [[maybe_unused]] EcmaHandleScope handleScope(thread);
1805    JSHandle<JSTaggedValue> searchTag = BuiltinsString::GetCallArg(argv, 0);
1806
1807    JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1808    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1809    JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
1810    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1811    bool isRegexp = JSObject::IsRegExp(thread, searchTag);
1812    if (isRegexp) {
1813        THROW_TYPE_ERROR_AND_RETURN(thread, "is regexp", JSTaggedValue::Exception());
1814    }
1815
1816    JSHandle<EcmaString> searchHandle = JSTaggedValue::ToString(thread, searchTag);
1817    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1818    uint32_t thisLen = EcmaStringAccessor(thisHandle).GetLength();
1819    uint32_t searchLen = EcmaStringAccessor(searchHandle).GetLength();
1820    int32_t pos = 0;
1821    JSHandle<JSTaggedValue> posTag = BuiltinsString::GetCallArg(argv, 1);
1822    if (posTag->IsUndefined()) {
1823        pos = 0;
1824    } else if (posTag->IsInt()) {
1825        pos = posTag->GetInt();
1826    } else {
1827        JSTaggedNumber posVal = JSTaggedValue::ToInteger(thread, posTag);
1828        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1829        if (posVal.GetNumber() == BuiltinsNumber::POSITIVE_INFINITY) {
1830            pos = thisLen;
1831        } else {
1832            pos = posVal.ToInt32();
1833        }
1834    }
1835    pos = std::min(std::max(pos, 0), static_cast<int32_t>(thisLen));
1836    if (static_cast<uint32_t>(pos) + searchLen > thisLen) {
1837        return BuiltinsString::GetTaggedBoolean(false);
1838    }
1839
1840    bool result = EcmaStringAccessor::IsSubStringAt(thread->GetEcmaVM(), thisHandle, searchHandle, pos);
1841
1842    return BuiltinsString::GetTaggedBoolean(result);
1843}
1844
1845// 21.1.3.19
1846JSTaggedValue BuiltinsString::Substring(EcmaRuntimeCallInfo *argv)
1847{
1848    ASSERT(argv);
1849    BUILTINS_API_TRACE(argv->GetThread(), String, Substring);
1850    JSThread *thread = argv->GetThread();
1851    [[maybe_unused]] EcmaHandleScope handleScope(thread);
1852
1853    JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1854    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1855    JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
1856    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1857    int32_t thisLen = static_cast<int32_t>(EcmaStringAccessor(thisHandle).GetLength());
1858    JSHandle<JSTaggedValue> startTag = BuiltinsString::GetCallArg(argv, 0);
1859    JSTaggedNumber startVal = JSTaggedValue::ToInteger(thread, startTag);
1860    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1861    int32_t start = ConvertDoubleToInt(startVal.GetNumber());
1862    int32_t end = 0;
1863    JSHandle<JSTaggedValue> endTag = BuiltinsString::GetCallArg(argv, 1);
1864    if (endTag->IsUndefined()) {
1865        end = thisLen;
1866    } else {
1867        JSTaggedNumber endVal = JSTaggedValue::ToInteger(thread, endTag);
1868        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1869        end = ConvertDoubleToInt(endVal.GetNumber());
1870    }
1871    start = std::min(std::max(start, 0), thisLen);
1872    end = std::min(std::max(end, 0), thisLen);
1873    int32_t from = std::min(start, end);
1874    int32_t to = std::max(start, end);
1875    int32_t len = to - from;
1876    return JSTaggedValue(EcmaStringAccessor::GetSubString(thread->GetEcmaVM(), thisHandle, from, len));
1877}
1878
1879// 21.1.3.20
1880JSTaggedValue BuiltinsString::ToLocaleLowerCase(EcmaRuntimeCallInfo *argv)
1881{
1882    ASSERT(argv);
1883    BUILTINS_API_TRACE(argv->GetThread(), String, ToLocaleLowerCase);
1884    JSThread *thread = argv->GetThread();
1885    EcmaVM *ecmaVm = thread->GetEcmaVM();
1886    [[maybe_unused]] EcmaHandleScope handleScope(thread);
1887
1888    // Let O be RequireObjectCoercible(this value).
1889    JSHandle<JSTaggedValue> obj(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1890    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1891    // Let S be ? ToString(O).
1892    JSHandle<EcmaString> string = JSTaggedValue::ToString(thread, obj);
1893    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1894
1895    // Let requestedLocales be ? CanonicalizeLocaleList(locales).
1896    JSHandle<JSTaggedValue> locales = GetCallArg(argv, 0);
1897    // Fast path
1898    if (locales->IsUndefined() && EcmaStringAccessor(string).IsUtf8()) {
1899        EcmaString *result = EcmaStringAccessor::TryToLower(ecmaVm, string);
1900        return JSTaggedValue(result);
1901    }
1902    JSHandle<TaggedArray> requestedLocales = intl::LocaleHelper::CanonicalizeLocaleList(thread, locales);
1903    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1904
1905    // If requestedLocales is not an empty List, then Let requestedLocale be requestedLocales[0].
1906    // Else, Let requestedLocale be DefaultLocale().
1907    JSHandle<EcmaString> requestedLocale = intl::LocaleHelper::DefaultLocale(thread);
1908    if (requestedLocales->GetLength() != 0) {
1909        requestedLocale = JSHandle<EcmaString>(thread, requestedLocales->Get(0));
1910    }
1911
1912    // Let noExtensionsLocale be the String value that is requestedLocale with all Unicode locale extension sequences
1913    // removed.
1914    intl::LocaleHelper::ParsedLocale noExtensionsLocale = intl::LocaleHelper::HandleLocale(requestedLocale);
1915    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1916
1917    // Let availableLocales be a List with language tags that includes the languages for which the Unicode Character
1918    // Database contains language sensitive case mappings. Implementations may add additional language tags
1919    // if they support case mapping for additional locales.
1920    std::vector<std::string> availableLocales = intl::LocaleHelper::GetAvailableLocales(thread, nullptr, nullptr);
1921    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1922
1923    // Let locale be BestAvailableLocale(availableLocales, noExtensionsLocale).
1924    std::string locale = intl::LocaleHelper::BestAvailableLocale(availableLocales, noExtensionsLocale.base);
1925    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1926
1927    // If locale is undefined, let locale be "und".
1928    if (locale.empty()) {
1929        locale = "und";
1930    }
1931
1932    // Let uString be a List containing in order the code points of S as defined in ES2020, 6.1.4,
1933    // starting at the first element of S.
1934    // Transform those elements in uString to the to the Unicode Default Case Conversion algorithm
1935    icu::Locale icuLocale = icu::Locale::createFromName(locale.c_str());
1936    EcmaString *result = EcmaStringAccessor::ToLocaleLower(ecmaVm, string, icuLocale);
1937    return JSTaggedValue(result);
1938}
1939
1940// 21.1.3.21
1941JSTaggedValue BuiltinsString::ToLocaleUpperCase(EcmaRuntimeCallInfo *argv)
1942{
1943    ASSERT(argv);
1944    BUILTINS_API_TRACE(argv->GetThread(), String, ToLocaleLowerCase);
1945    JSThread *thread = argv->GetThread();
1946    EcmaVM *ecmaVm = thread->GetEcmaVM();
1947    [[maybe_unused]] EcmaHandleScope handleScope(thread);
1948
1949    // Let O be RequireObjectCoercible(this value).
1950    JSHandle<JSTaggedValue> obj(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1951    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1952    // Let S be ? ToString(O).
1953    JSHandle<EcmaString> string = JSTaggedValue::ToString(thread, obj);
1954    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1955
1956    // Let requestedLocales be ? CanonicalizeLocaleList(locales).
1957    JSHandle<JSTaggedValue> locales = GetCallArg(argv, 0);
1958    // Fast path
1959    if (locales->IsUndefined() && EcmaStringAccessor(string).IsUtf8()) {
1960        EcmaString *result = EcmaStringAccessor::TryToUpper(ecmaVm, string);
1961        return JSTaggedValue(result);
1962    }
1963    JSHandle<TaggedArray> requestedLocales = intl::LocaleHelper::CanonicalizeLocaleList(thread, locales);
1964    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1965
1966    // If requestedLocales is not an empty List, then Let requestedLocale be requestedLocales[0].
1967    // Else, Let requestedLocale be DefaultLocale().
1968    JSHandle<EcmaString> requestedLocale = intl::LocaleHelper::DefaultLocale(thread);
1969    if (requestedLocales->GetLength() != 0) {
1970        requestedLocale = JSHandle<EcmaString>(thread, requestedLocales->Get(0));
1971    }
1972
1973    // Let noExtensionsLocale be the String value that is requestedLocale with all Unicode locale extension sequences
1974    // removed.
1975    intl::LocaleHelper::ParsedLocale noExtensionsLocale = intl::LocaleHelper::HandleLocale(requestedLocale);
1976    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1977
1978    // Let availableLocales be a List with language tags that includes the languages for which the Unicode Character
1979    // Database contains language sensitive case mappings. Implementations may add additional language tags
1980    // if they support case mapping for additional locales.
1981    std::vector<std::string> availableLocales = intl::LocaleHelper::GetAvailableLocales(thread, nullptr, nullptr);
1982    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1983
1984    // Let locale be BestAvailableLocale(availableLocales, noExtensionsLocale).
1985    std::string locale = intl::LocaleHelper::BestAvailableLocale(availableLocales, noExtensionsLocale.base);
1986    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1987
1988    // If locale is undefined, let locale be "und".
1989    if (locale.empty()) {
1990        locale = "und";
1991    }
1992
1993    // Let uString be a List containing in order the code points of S as defined in ES2020, 6.1.4,
1994    // starting at the first element of S.
1995    // Transform those elements in uString to the to the Unicode Default Case Conversion algorithm
1996    icu::Locale icuLocale = icu::Locale::createFromName(locale.c_str());
1997    EcmaString *result = EcmaStringAccessor::ToLocaleUpper(ecmaVm, string, icuLocale);
1998    return JSTaggedValue(result);
1999}
2000
2001// 21.1.3.22
2002JSTaggedValue BuiltinsString::ToLowerCase(EcmaRuntimeCallInfo *argv)
2003{
2004    ASSERT(argv);
2005    BUILTINS_API_TRACE(argv->GetThread(), String, ToLowerCase);
2006    JSThread *thread = argv->GetThread();
2007    [[maybe_unused]] EcmaHandleScope handleScope(thread);
2008    JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
2009    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2010    JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2011    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2012    EcmaString *result = EcmaStringAccessor::ToLower(thread->GetEcmaVM(), thisHandle);
2013    return JSTaggedValue(result);
2014}
2015
2016// 21.1.3.23
2017JSTaggedValue BuiltinsString::ToString(EcmaRuntimeCallInfo *argv)
2018{
2019    ASSERT(argv);
2020    return ThisStringValue(argv->GetThread(), GetThis(argv).GetTaggedValue());
2021}
2022
2023// 21.1.3.24
2024JSTaggedValue BuiltinsString::ToUpperCase(EcmaRuntimeCallInfo *argv)
2025{
2026    ASSERT(argv);
2027    BUILTINS_API_TRACE(argv->GetThread(), String, ToUpperCase);
2028    JSThread *thread = argv->GetThread();
2029    [[maybe_unused]] EcmaHandleScope handleScope(thread);
2030
2031    JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
2032    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2033    JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2034    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2035    EcmaString *result = EcmaStringAccessor::ToUpper(thread->GetEcmaVM(), thisHandle);
2036    return JSTaggedValue(result);
2037}
2038
2039// 21.1.3.25
2040JSTaggedValue BuiltinsString::Trim(EcmaRuntimeCallInfo *argv)
2041{
2042    ASSERT(argv);
2043    BUILTINS_API_TRACE(argv->GetThread(), String, Trim);
2044    JSThread *thread = argv->GetThread();
2045    [[maybe_unused]] EcmaHandleScope handleScope(thread);
2046    JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
2047    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2048    JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2049    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2050    EcmaString *res = EcmaStringAccessor::Trim(thread, thisHandle, EcmaString::TrimMode::TRIM);
2051    return JSTaggedValue(res);
2052}
2053
2054JSTaggedValue BuiltinsString::TrimStart(EcmaRuntimeCallInfo *argv)
2055{
2056    ASSERT(argv);
2057    BUILTINS_API_TRACE(argv->GetThread(), String, TrimStart);
2058    JSThread *thread = argv->GetThread();
2059    [[maybe_unused]] EcmaHandleScope handleScope(thread);
2060    JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv));
2061    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2062    JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2063    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2064    EcmaString *res = EcmaStringAccessor::Trim(thread, thisHandle, EcmaString::TrimMode::TRIM_START);
2065    return JSTaggedValue(res);
2066}
2067
2068JSTaggedValue BuiltinsString::TrimEnd(EcmaRuntimeCallInfo *argv)
2069{
2070    ASSERT(argv);
2071    BUILTINS_API_TRACE(argv->GetThread(), String, TrimEnd);
2072    JSThread *thread = argv->GetThread();
2073    [[maybe_unused]] EcmaHandleScope handleScope(thread);
2074    JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv));
2075    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2076    JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2077    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2078    EcmaString *res = EcmaStringAccessor::Trim(thread, thisHandle, EcmaString::TrimMode::TRIM_END);
2079    return JSTaggedValue(res);
2080}
2081
2082JSTaggedValue BuiltinsString::TrimLeft(EcmaRuntimeCallInfo *argv)
2083{
2084    ASSERT(argv);
2085    BUILTINS_API_TRACE(argv->GetThread(), String, TrimLeft);
2086    JSThread *thread = argv->GetThread();
2087    [[maybe_unused]] EcmaHandleScope handleScope(thread);
2088    JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv));
2089    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2090    JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2091    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2092    EcmaString *res = EcmaStringAccessor::Trim(thread, thisHandle, EcmaString::TrimMode::TRIM_START);
2093    return JSTaggedValue(res);
2094}
2095
2096JSTaggedValue BuiltinsString::TrimRight(EcmaRuntimeCallInfo *argv)
2097{
2098    ASSERT(argv);
2099    BUILTINS_API_TRACE(argv->GetThread(), String, TrimRight);
2100    JSThread *thread = argv->GetThread();
2101    [[maybe_unused]] EcmaHandleScope handleScope(thread);
2102    JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv));
2103    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2104    JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2105    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2106    EcmaString *res = EcmaStringAccessor::Trim(thread, thisHandle, EcmaString::TrimMode::TRIM_END);
2107    return JSTaggedValue(res);
2108}
2109
2110// 21.1.3.26
2111JSTaggedValue BuiltinsString::ValueOf(EcmaRuntimeCallInfo *argv)
2112{
2113    ASSERT(argv);
2114    return ThisStringValue(argv->GetThread(), GetThis(argv).GetTaggedValue());
2115}
2116
2117// 21.1.3.27
2118JSTaggedValue BuiltinsString::GetStringIterator(EcmaRuntimeCallInfo *argv)
2119{
2120    ASSERT(argv);
2121    BUILTINS_API_TRACE(argv->GetThread(), String, GetStringIterator);
2122    JSThread *thread = argv->GetThread();
2123    [[maybe_unused]] EcmaHandleScope handleScope(thread);
2124    // 1. Let O be RequireObjectCoercible(this value).
2125    JSHandle<JSTaggedValue> current(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
2126    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2127    // Let S be ToString(O).
2128
2129    JSHandle<EcmaString> string = JSTaggedValue::ToString(thread, current);
2130    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(argv->GetThread());
2131    // Return CreateStringIterator(S).
2132    return JSStringIterator::CreateStringIterator(thread, string).GetTaggedValue();
2133}
2134
2135//  B.2.3.1
2136JSTaggedValue BuiltinsString::SubStr(EcmaRuntimeCallInfo *argv)
2137{
2138    ASSERT(argv);
2139    BUILTINS_API_TRACE(argv->GetThread(), String, SubStr);
2140    JSThread *thread = argv->GetThread();
2141
2142    [[maybe_unused]] EcmaHandleScope handleScope(thread);
2143
2144    // 1. Let O be RequireObjectCoercible(this value).
2145    // 2. Let S be ToString(O).
2146
2147    JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
2148    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2149    JSHandle<EcmaString> thisString = JSTaggedValue::ToString(thread, thisTag);
2150
2151    // 3. ReturnIfAbrupt(S).
2152    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2153    JSHandle<JSTaggedValue> intStart = GetCallArg(argv, 0);
2154    // 4. Let intStart be ToInteger(start).
2155    JSTaggedNumber numStart = JSTaggedValue::ToInteger(thread, intStart);
2156    // 5. ReturnIfAbrupt(intStart).
2157    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2158    int32_t start = base::NumberHelper::DoubleInRangeInt32(numStart.GetNumber());
2159    JSHandle<JSTaggedValue> lengthTag = GetCallArg(argv, 1);
2160    // 6. If length is undefined, let end be +; otherwise let end be ToInteger(length).
2161    int32_t end = 0;
2162    if (lengthTag->IsUndefined()) {
2163        end = INT_MAX;
2164    } else {
2165        JSTaggedNumber lengthNumber = JSTaggedValue::ToInteger(thread, lengthTag);
2166        // 7. ReturnIfAbrupt(end).
2167        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2168        end = base::NumberHelper::DoubleInRangeInt32(lengthNumber.GetNumber());
2169    }
2170    // 8. Let size be the number of code units in S.
2171    int32_t size = static_cast<int32_t>(EcmaStringAccessor(thisString).GetLength());
2172    // 9. If intStart < 0, let intStart be max(size + intStart,0).
2173    if (start < 0) {
2174        start = std::max(size + start, 0);
2175    }
2176    // 10. Let resultLength be min(max(end,0), size – intStart).
2177    int32_t resultLength = std::min(std::max(end, 0), size - start);
2178    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2179    // 11. If resultLength  0, return the empty String "".
2180    if (resultLength <= 0) {
2181        return factory->GetEmptyString().GetTaggedValue();
2182    }
2183    return JSTaggedValue(EcmaStringAccessor::FastSubString(thread->GetEcmaVM(), thisString, start, resultLength));
2184}
2185
2186// 22.1.3.1
2187JSTaggedValue BuiltinsString::At(EcmaRuntimeCallInfo *argv)
2188{
2189    ASSERT(argv);
2190    BUILTINS_API_TRACE(argv->GetThread(), String, At);
2191    JSThread *thread = argv->GetThread();
2192    [[maybe_unused]] EcmaHandleScope handleScope(thread);
2193
2194    // 1. Let O be RequireObjectCoercible(this value).
2195    // 2. Let S be ToString(O).
2196    JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
2197    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2198    JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2199    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2200
2201    // 3. Let len be the length of S.
2202    int32_t thisLen = static_cast<int32_t>(EcmaStringAccessor(thisHandle).GetLength());
2203
2204    // 4. Let relativeIndex be ? ToIntegerOrInfinity(index).
2205    JSHandle<JSTaggedValue> indexTag = BuiltinsString::GetCallArg(argv, 0);
2206    JSTaggedNumber indexVal = JSTaggedValue::ToInteger(thread, indexTag);
2207    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2208    int32_t relativeIndex = base::NumberHelper::DoubleInRangeInt32(indexVal.GetNumber());
2209
2210    // 5. If relativeIndex ≥ 0, then Let k be relativeIndex. 6. Else, Let k be len + relativeIndex.
2211    int32_t k = 0;
2212    if (relativeIndex >= 0) {
2213        k = relativeIndex;
2214    } else {
2215        k = thisLen + relativeIndex;
2216    }
2217    // 7. If k < 0 or k ≥ len, return undefined.
2218    if (k < 0 || k >= thisLen) {
2219        return JSTaggedValue::Undefined();
2220    }
2221    // 8. Return the substring of S from k to k + 1.
2222    return JSTaggedValue(EcmaStringAccessor::FastSubString(thread->GetEcmaVM(), thisHandle, k, 1));
2223}
2224
2225JSTaggedValue BuiltinsString::GetLength(EcmaRuntimeCallInfo *argv)
2226{
2227    ASSERT(argv);
2228    JSThread *thread = argv->GetThread();
2229    BUILTINS_API_TRACE(thread, String, GetLength);
2230    [[maybe_unused]] EcmaHandleScope handleScope(thread);
2231    JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
2232
2233    JSHandle<EcmaString> thisString = JSTaggedValue::ToString(thread, thisHandle);
2234    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2235    return GetTaggedInt(EcmaStringAccessor(thisString).GetLength());
2236}
2237
2238// 21.1.3
2239JSTaggedValue BuiltinsString::ThisStringValue(JSThread *thread, JSTaggedValue value)
2240{
2241    BUILTINS_API_TRACE(thread, String, ThisStringValue);
2242    if (value.IsString()) {
2243        return value;
2244    }
2245    if (value.IsECMAObject()) {
2246        auto jshclass = value.GetTaggedObject()->GetClass();
2247        if (jshclass->GetObjectType() == JSType::JS_PRIMITIVE_REF) {
2248            JSTaggedValue primitive = JSPrimitiveRef::Cast(value.GetTaggedObject())->GetValue();
2249            if (primitive.IsString()) {
2250                return primitive;
2251            }
2252        }
2253    }
2254    THROW_TYPE_ERROR_AND_RETURN(thread, "can not convert to String", JSTaggedValue::Exception());
2255}
2256
2257JSTaggedValue BuiltinsString::Pad(EcmaRuntimeCallInfo *argv, bool isStart)
2258{
2259    JSThread *thread = argv->GetThread();
2260    BUILTINS_API_TRACE(thread, String, Pad);
2261    [[maybe_unused]] EcmaHandleScope handleScope(thread);
2262
2263    JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, BuiltinsString::GetThis(argv));
2264    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2265    JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2266    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2267    JSHandle<JSTaggedValue> lengthTag = GetCallArg(argv, 0);
2268    JSTaggedNumber number = JSTaggedValue::ToNumber(thread, lengthTag);
2269    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2270    int64_t intMaxLength  = base::NumberHelper::DoubleToInt64(number.GetNumber());
2271    int32_t stringLength = static_cast<int32_t>(EcmaStringAccessor(thisHandle).GetLength());
2272    if (intMaxLength <= stringLength) {
2273        return thisHandle.GetTaggedValue();
2274    }
2275    JSHandle<JSTaggedValue> fillString = GetCallArg(argv, 1);
2276    std::u16string stringBuilder;
2277    if (fillString->IsUndefined()) {
2278        stringBuilder = u" ";
2279    } else {
2280        JSHandle<EcmaString> filler = JSTaggedValue::ToString(thread, fillString);
2281        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2282        stringBuilder = EcmaStringAccessor(filler).ToU16String();
2283    }
2284    if (stringBuilder.size() == 0) {
2285        return thisHandle.GetTaggedValue();
2286    }
2287    std::u16string u16strSearch = EcmaStringAccessor(thisHandle).ToU16String();
2288    int64_t fillLen = intMaxLength - stringLength;
2289    int64_t len = static_cast<int64_t>(stringBuilder.length());
2290    if (static_cast<size_t>(intMaxLength) >= EcmaString::MAX_STRING_LENGTH) {
2291        THROW_RANGE_ERROR_AND_RETURN(thread, "Invalid string length", JSTaggedValue::Exception());
2292    }
2293    std::u16string fiString;
2294    for (int32_t i = 0; i < fillLen; ++i) {
2295        fiString += stringBuilder[i % len];
2296    }
2297    std::u16string resultString;
2298    if (isStart) {
2299        resultString = fiString + u16strSearch;
2300    } else {
2301        resultString = u16strSearch + fiString;
2302    }
2303    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2304    return factory->NewFromUtf16Literal(reinterpret_cast<const uint16_t *>(resultString.c_str()),
2305                                        resultString.size()).GetTaggedValue();
2306}
2307
2308int32_t BuiltinsString::ConvertDoubleToInt(double d)
2309{
2310    if (std::isnan(d) || d == -base::POSITIVE_INFINITY) {
2311        return 0;
2312    }
2313    if (d >= static_cast<double>(INT_MAX)) {
2314        return INT_MAX;
2315    }
2316    if (d <= static_cast<double>(INT_MIN)) {
2317        return INT_MIN;
2318    }
2319    return base::NumberHelper::DoubleToInt(d, base::INT32_BITS);
2320}
2321
2322JSTaggedValue BuiltinsString::StringToList(JSThread *thread, JSHandle<EcmaString> &str)
2323{
2324    JSHandle<StringToListResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetStringToListResultCache());
2325    JSTaggedValue cacheResult = StringToListResultCache::FindCachedResult(thread, cacheTable, str);
2326    if (cacheResult != JSTaggedValue::Undefined()) {
2327        JSHandle<JSTaggedValue> resultArray(JSArray::CreateArrayFromList(thread,
2328            JSHandle<TaggedArray>(thread, cacheResult)));
2329        return resultArray.GetTaggedValue();
2330    }
2331
2332    JSTaggedValue newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue();
2333    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2334    JSHandle<JSObject> newArrayHandle(thread, newArray);
2335    JSHandle<EcmaString> iteratedString(str);
2336    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2337    JSHandle<TaggedArray> oldElements(thread, newArrayHandle->GetElements());
2338    uint32_t totalElements = EcmaStringAccessor(iteratedString).GetLength();
2339    JSHandle<TaggedArray> elements = (oldElements->GetLength() < totalElements) ?
2340        factory->ExtendArray(oldElements, totalElements) : oldElements;
2341    uint32_t index = 0;
2342    newArrayHandle->SetElements(thread, elements);
2343    while (index < totalElements) {
2344        uint16_t c = EcmaStringAccessor(iteratedString).Get(index);
2345        JSHandle<EcmaString> newStr = factory->NewFromUtf16Literal(&c, 1);
2346        ElementAccessor::Set(thread, newArrayHandle, index, newStr, true);
2347        index++;
2348        thread->CheckSafepointIfSuspended();
2349    }
2350    JSHandle<JSArray>(newArrayHandle)->SetArrayLength(thread, totalElements);
2351
2352    StringToListResultCache::SetCachedResult(thread, cacheTable, str, elements);
2353
2354    return newArrayHandle.GetTaggedValue();
2355}
2356
2357JSTaggedValue BuiltinsString::StringToSList(JSThread *thread, JSHandle<EcmaString> &str)
2358{
2359    JSHandle<StringToListResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetStringToListResultCache());
2360    JSTaggedValue cacheResult = StringToListResultCache::FindCachedResult(thread, cacheTable, str);
2361    if (cacheResult != JSTaggedValue::Undefined()) {
2362        JSHandle<JSTaggedValue> resultArray(
2363            JSSharedArray::CreateArrayFromList(thread, JSHandle<TaggedArray>(thread, cacheResult)));
2364        return resultArray.GetTaggedValue();
2365    }
2366
2367    JSTaggedValue newSharedArray = JSSharedArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue();
2368    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2369    JSHandle<JSObject> newSharedArrayHandle(thread, newSharedArray);
2370    JSHandle<EcmaString> iteratedString(str);
2371    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2372    JSHandle<TaggedArray> oldElements(thread, newSharedArrayHandle->GetElements());
2373    uint32_t totalElements = EcmaStringAccessor(iteratedString).GetLength();
2374    JSHandle<TaggedArray> elements =
2375        (oldElements->GetLength() < totalElements)
2376            ? factory->ExtendArray(oldElements, totalElements, JSTaggedValue::Hole(), MemSpaceType::SHARED_OLD_SPACE)
2377            : oldElements;
2378    uint32_t index = 0;
2379    newSharedArrayHandle->SetElements(thread, elements);
2380    while (index < totalElements) {
2381        uint16_t c = EcmaStringAccessor(iteratedString).Get(index);
2382        JSHandle<EcmaString> newStr = factory->NewFromUtf16Literal(&c, 1);
2383        ElementAccessor::Set(thread, newSharedArrayHandle, index, newStr, true);
2384        index++;
2385        thread->CheckSafepointIfSuspended();
2386    }
2387    JSHandle<JSSharedArray>(newSharedArrayHandle)->SetArrayLength(thread, totalElements);
2388
2389    StringToListResultCache::SetCachedResult(thread, cacheTable, str, elements);
2390    newSharedArrayHandle->GetJSHClass()->SetExtensible(false);
2391    return newSharedArrayHandle.GetTaggedValue();
2392}
2393
2394JSTaggedValue StringSplitResultCache::CreateCacheTable(const JSThread *thread)
2395{
2396    int length = CACHE_SIZE * ENTRY_SIZE;
2397    auto table = static_cast<StringSplitResultCache*>(
2398        *thread->GetEcmaVM()->GetFactory()->NewTaggedArray(length, JSTaggedValue::Undefined()));
2399    return JSTaggedValue(table);
2400}
2401
2402JSTaggedValue StringSplitResultCache::FindCachedResult(const JSThread *thread,
2403    const JSHandle<StringSplitResultCache> &cache, const JSHandle<EcmaString> &thisString,
2404    const JSHandle<EcmaString> &pattern, bool isOneByte)
2405{
2406    uint32_t hash = EcmaStringAccessor(thisString).GetHashcode();
2407    uint32_t entry = hash & (CACHE_SIZE - 1);
2408    uint32_t index = entry * ENTRY_SIZE;
2409    JSTaggedValue cacheThis = cache->Get(index + STRING_INDEX);
2410    JSTaggedValue cachePattern = cache->Get(index + PATTERN_INDEX);
2411    if (!cacheThis.IsString() || !cachePattern.IsString()) {
2412        return JSTaggedValue::Undefined();
2413    }
2414    JSHandle<EcmaString> cacheStringHandle(thread, cacheThis);
2415    JSHandle<EcmaString> cachePatternHandle(thread, cachePattern);
2416
2417    if (EcmaStringAccessor::StringsAreEqual(thread->GetEcmaVM(), thisString, cacheStringHandle) &&
2418        EcmaStringAccessor::StringsAreEqual(thread->GetEcmaVM(), pattern, cachePatternHandle)) {
2419        JSHandle<TaggedArray> cacheArray(thread, cache->Get(index + ARRAY_INDEX));
2420        uint32_t arrayLength = cacheArray->GetLength();
2421        ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2422        JSHandle<TaggedArray> copyArray;
2423        if (isOneByte) {
2424            copyArray = factory->NewAndCopyTaggedArraySkipBarrier(cacheArray, arrayLength, arrayLength);
2425        } else {
2426            copyArray = factory->NewAndCopyTaggedArray(cacheArray, arrayLength, arrayLength);
2427        }
2428        return copyArray.GetTaggedValue();
2429    }
2430    return JSTaggedValue::Undefined();
2431}
2432
2433void StringSplitResultCache::SetCachedResult(const JSThread *thread, const JSHandle<StringSplitResultCache> &cache,
2434    const JSHandle<EcmaString> &thisString, const JSHandle<EcmaString> &pattern,
2435    const JSHandle<TaggedArray> &resultArray)
2436{
2437    // clone to cache array
2438    uint32_t arrayLength = resultArray->GetLength();
2439    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2440    JSHandle<TaggedArray> newElements(factory->NewTaggedArray(arrayLength));
2441    for (uint32_t i = 0; i < arrayLength; i++) {
2442        newElements->Set(thread, i, resultArray->Get(i));
2443    }
2444    uint32_t hash = EcmaStringAccessor(thisString).GetHashcode();
2445    uint32_t entry = hash & (CACHE_SIZE - 1);
2446    uint32_t index = entry * ENTRY_SIZE;
2447
2448    cache->Set(thread, index + STRING_INDEX, thisString);
2449    cache->Set(thread, index + PATTERN_INDEX, pattern);
2450    cache->Set(thread, index + ARRAY_INDEX, newElements);
2451}
2452
2453JSTaggedValue StringToListResultCache::CreateCacheTable(const JSThread *thread)
2454{
2455    int length = CACHE_SIZE * ENTRY_SIZE;
2456    auto table = static_cast<StringToListResultCache*>(
2457        *thread->GetEcmaVM()->GetFactory()->NewTaggedArray(length, JSTaggedValue::Undefined()));
2458    return JSTaggedValue(table);
2459}
2460
2461JSTaggedValue StringToListResultCache::FindCachedResult(const JSThread *thread,
2462    const JSHandle<StringToListResultCache> &cache, const JSHandle<EcmaString> &thisString)
2463{
2464    if (EcmaStringAccessor(thisString).GetLength() > MAX_STRING_LENGTH) {
2465        return JSTaggedValue::Undefined();
2466    }
2467    uint32_t hash = EcmaStringAccessor(thisString).GetHashcode();
2468    uint32_t entry = hash & (CACHE_SIZE - 1);
2469    uint32_t index = entry * ENTRY_SIZE;
2470    JSHandle<JSTaggedValue> cacheThis(thread, cache->Get(index + STRING_INDEX));
2471    if (!cacheThis->IsString()) {
2472        return JSTaggedValue::Undefined();
2473    }
2474    JSHandle<EcmaString> cacheStr(cacheThis);
2475    if (EcmaStringAccessor::StringsAreEqual(thread->GetEcmaVM(), thisString, cacheStr)) {
2476        return cache->Get(index + ARRAY_INDEX);
2477    }
2478    return JSTaggedValue::Undefined();
2479}
2480
2481void StringToListResultCache::SetCachedResult(const JSThread *thread, const JSHandle<StringToListResultCache> &cache,
2482    const JSHandle<EcmaString> &thisString, const JSHandle<TaggedArray> &resultArray)
2483{
2484    if (EcmaStringAccessor(thisString).GetLength() > MAX_STRING_LENGTH ||
2485        EcmaStringAccessor(thisString).GetLength() == 0) {
2486        return;
2487    }
2488    if (!EcmaStringAccessor(thisString).IsInternString()) {
2489        return;
2490    }
2491    // clone to cache array
2492    uint32_t arrayLength = resultArray->GetLength();
2493    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2494    JSHandle<TaggedArray> newElements;
2495    if (resultArray.GetTaggedValue().IsInSharedHeap()) {
2496        newElements = JSHandle<TaggedArray>(factory->NewSCOWTaggedArray(arrayLength));
2497    } else {
2498        newElements = JSHandle<TaggedArray>(factory->NewCOWTaggedArray(arrayLength));
2499    }
2500    for (uint32_t i = 0; i < arrayLength; i++) {
2501        newElements->Set(thread, i, resultArray->Get(i));
2502    }
2503    uint32_t hash = EcmaStringAccessor(thisString).GetHashcode();
2504    uint32_t entry = hash & (CACHE_SIZE - 1);
2505    uint32_t index = entry * ENTRY_SIZE;
2506    cache->Set(thread, index + STRING_INDEX, thisString);
2507    cache->Set(thread, index + ARRAY_INDEX, newElements);
2508}
2509}  // namespace panda::ecmascript::builtins
2510