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 
44 namespace panda::ecmascript::builtins {
45 using ObjectFactory = ecmascript::ObjectFactory;
46 using JSArray = ecmascript::JSArray;
47 constexpr std::uint16_t CHAR16_LETTER_NULL = u'\0';
48 
49 // 21.1.1.1 String(value)
StringConstructor(EcmaRuntimeCallInfo *argv)50 JSTaggedValue 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
FromCharCode(EcmaRuntimeCallInfo *argv)80 JSTaggedValue 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
FromCodePoint(EcmaRuntimeCallInfo *argv)114 JSTaggedValue 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
Raw(EcmaRuntimeCallInfo *argv)161 JSTaggedValue 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
CharAt(EcmaRuntimeCallInfo *argv)224 JSTaggedValue 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
CharCodeAt(EcmaRuntimeCallInfo *argv)264 JSTaggedValue 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
CodePointAt(EcmaRuntimeCallInfo *argv)299 JSTaggedValue 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
Concat(EcmaRuntimeCallInfo *argv)332 JSTaggedValue 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
EndsWith(EcmaRuntimeCallInfo *argv)360 JSTaggedValue 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
Includes(EcmaRuntimeCallInfo *argv)406 JSTaggedValue 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
IndexOf(EcmaRuntimeCallInfo *argv)442 JSTaggedValue 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
LastIndexOf(EcmaRuntimeCallInfo *argv)480 JSTaggedValue 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
LocaleCompare(EcmaRuntimeCallInfo *argv)518 JSTaggedValue 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 
DoLocaleCompare(JSThread *thread, const JSHandle<EcmaString> &thisHandle, const JSHandle<EcmaString> &thatHandle, const JSHandle<JSTaggedValue> &locales, const JSHandle<JSTaggedValue> &options)537 JSTaggedValue 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 
LocaleCompareGC(JSThread *thread, const JSHandle<EcmaString> &thisHandle, const JSHandle<EcmaString> &thatHandle, const JSHandle<JSTaggedValue> &locales, const JSHandle<JSTaggedValue> &options, CompareStringsOption csOption, bool cacheable)572 JSTaggedValue 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
Match(EcmaRuntimeCallInfo *argv)601 JSTaggedValue 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 
MatchAll(EcmaRuntimeCallInfo *argv)642 JSTaggedValue 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 
IsWellFormed(EcmaRuntimeCallInfo *argv)727 JSTaggedValue 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 
ToWellFormed(EcmaRuntimeCallInfo *argv)787 JSTaggedValue 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 )
UTF16SurrogatePairToCodePoint(uint16_t lead, uint16_t trail)883 uint32_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
Normalize(EcmaRuntimeCallInfo *argv)894 JSTaggedValue 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 
PadStart(EcmaRuntimeCallInfo *argv)950 JSTaggedValue BuiltinsString::PadStart(EcmaRuntimeCallInfo *argv)
951 {
952     ASSERT(argv);
953     BUILTINS_API_TRACE(argv->GetThread(), String, PadStart);
954     return BuiltinsString::Pad(argv, true);
955 }
956 
PadEnd(EcmaRuntimeCallInfo *argv)957 JSTaggedValue 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
Repeat(EcmaRuntimeCallInfo *argv)965 JSTaggedValue 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
Replace(EcmaRuntimeCallInfo *argv)1011 JSTaggedValue 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 
ReplaceAll(EcmaRuntimeCallInfo *argv)1138 JSTaggedValue 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
ProcessDollarAmpersand(std::u16string &stringBuilder, const JSHandle<EcmaString> &matched, bool &canBeCompress)1271 void 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
ProcessDollarBacktick(EcmaVM *ecmaVm, std::u16string &stringBuilder, const JSHandle<EcmaString> &srcString, int position, bool &canBeCompress)1280 void 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
ProcessDollarSingleQuote(EcmaVM *ecmaVm, std::u16string &stringBuilder, const JSHandle<EcmaString> &srcString, int tailPos, bool &canBeCompress)1293 void 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 
ProcessDigitCapture(const JSHandle<EcmaString> &replacementFlat, uint32_t peekIndex, uint32_t replaceLength, const JSHandle<TaggedArray> &captureList, std::u16string &stringBuilder)1306 std::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
ProcessNamedCaptures(JSThread *thread, const JSHandle<EcmaString> &replacementFlat, int32_t peekIndex, const JSHandle<JSTaggedValue> &namedCaptures, std::u16string &stringBuilder)1345 std::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 
GetSubstitution(JSThread *thread, const JSHandle<EcmaString> &matched, const JSHandle<EcmaString> &srcString, int position, const JSHandle<TaggedArray> &captureList, const JSHandle<JSTaggedValue> &namedCaptures, const JSHandle<EcmaString> &replacement)1377 JSTaggedValue 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
Search(EcmaRuntimeCallInfo *argv)1494 JSTaggedValue 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
Slice(EcmaRuntimeCallInfo *argv)1535 JSTaggedValue 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
Split(EcmaRuntimeCallInfo *argv)1577 JSTaggedValue 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 
CreateArrayFromString(JSThread *thread, EcmaVM *ecmaVm, const JSHandle<EcmaString> &thisString, uint32_t thisLength, uint32_t lim)1676 JSTaggedValue 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 
CreateArrayBySplitString(JSThread *thread, EcmaVM *ecmaVm, const JSHandle<EcmaString> &thisString, const JSHandle<EcmaString> &seperatorString, uint32_t thisLength, uint32_t seperatorLength, uint32_t lim)1717 JSTaggedValue 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 
CreateArrayThisStringAndSeperatorStringAreNotEmpty(JSThread *thread, EcmaVM *ecmaVm, const JSHandle<EcmaString> &thisString, const JSHandle<EcmaString> &seperatorString, uint32_t thisLength, uint32_t seperatorLength, uint32_t lim)1742 JSTaggedValue 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
StartsWith(EcmaRuntimeCallInfo *argv)1799 JSTaggedValue 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
Substring(EcmaRuntimeCallInfo *argv)1846 JSTaggedValue 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
ToLocaleLowerCase(EcmaRuntimeCallInfo *argv)1880 JSTaggedValue 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
ToLocaleUpperCase(EcmaRuntimeCallInfo *argv)1941 JSTaggedValue 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
ToLowerCase(EcmaRuntimeCallInfo *argv)2002 JSTaggedValue 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
ToString(EcmaRuntimeCallInfo *argv)2017 JSTaggedValue BuiltinsString::ToString(EcmaRuntimeCallInfo *argv)
2018 {
2019     ASSERT(argv);
2020     return ThisStringValue(argv->GetThread(), GetThis(argv).GetTaggedValue());
2021 }
2022 
2023 // 21.1.3.24
ToUpperCase(EcmaRuntimeCallInfo *argv)2024 JSTaggedValue 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
Trim(EcmaRuntimeCallInfo *argv)2040 JSTaggedValue 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 
TrimStart(EcmaRuntimeCallInfo *argv)2054 JSTaggedValue 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 
TrimEnd(EcmaRuntimeCallInfo *argv)2068 JSTaggedValue 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 
TrimLeft(EcmaRuntimeCallInfo *argv)2082 JSTaggedValue 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 
TrimRight(EcmaRuntimeCallInfo *argv)2096 JSTaggedValue 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
ValueOf(EcmaRuntimeCallInfo *argv)2111 JSTaggedValue BuiltinsString::ValueOf(EcmaRuntimeCallInfo *argv)
2112 {
2113     ASSERT(argv);
2114     return ThisStringValue(argv->GetThread(), GetThis(argv).GetTaggedValue());
2115 }
2116 
2117 // 21.1.3.27
GetStringIterator(EcmaRuntimeCallInfo *argv)2118 JSTaggedValue 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
SubStr(EcmaRuntimeCallInfo *argv)2136 JSTaggedValue 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
At(EcmaRuntimeCallInfo *argv)2187 JSTaggedValue 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 
GetLength(EcmaRuntimeCallInfo *argv)2225 JSTaggedValue 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
ThisStringValue(JSThread *thread, JSTaggedValue value)2239 JSTaggedValue 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 
Pad(EcmaRuntimeCallInfo *argv, bool isStart)2257 JSTaggedValue 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 
ConvertDoubleToInt(double d)2308 int32_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 
StringToList(JSThread *thread, JSHandle<EcmaString> &str)2322 JSTaggedValue 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 
StringToSList(JSThread *thread, JSHandle<EcmaString> &str)2357 JSTaggedValue 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 
CreateCacheTable(const JSThread *thread)2394 JSTaggedValue 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 
FindCachedResult(const JSThread *thread, const JSHandle<StringSplitResultCache> &cache, const JSHandle<EcmaString> &thisString, const JSHandle<EcmaString> &pattern, bool isOneByte)2402 JSTaggedValue 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 
SetCachedResult(const JSThread *thread, const JSHandle<StringSplitResultCache> &cache, const JSHandle<EcmaString> &thisString, const JSHandle<EcmaString> &pattern, const JSHandle<TaggedArray> &resultArray)2433 void 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 
CreateCacheTable(const JSThread *thread)2453 JSTaggedValue 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 
FindCachedResult(const JSThread *thread, const JSHandle<StringToListResultCache> &cache, const JSHandle<EcmaString> &thisString)2461 JSTaggedValue 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 
SetCachedResult(const JSThread *thread, const JSHandle<StringToListResultCache> &cache, const JSHandle<EcmaString> &thisString, const JSHandle<TaggedArray> &resultArray)2481 void 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