• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "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             EcmaRuntimeCallInfo *info =
624                 EcmaInterpreter::NewRuntimeCallInfo(thread, matcher, regexp, undefined, 1);
625             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
626             info->SetCallArg(thisTag.GetTaggedValue());
627             return JSFunction::Call(info);
628         }
629     }
630     JSHandle<EcmaString> thisVal = JSTaggedValue::ToString(thread, thisTag);
631     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
632     JSHandle<JSTaggedValue> undifinedHandle = globalConst->GetHandledUndefined();
633     JSHandle<JSTaggedValue> rx(thread, BuiltinsRegExp::RegExpCreate(thread, regexp, undifinedHandle));
634     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
635     EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, rx, undefined, 1);
636     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
637     info->SetCallArg(thisVal.GetTaggedValue());
638     return JSFunction::Invoke(info, matchTag);
639 }
640 
MatchAll(EcmaRuntimeCallInfo * argv)641 JSTaggedValue BuiltinsString::MatchAll(EcmaRuntimeCallInfo *argv)
642 {
643     ASSERT(argv);
644     BUILTINS_API_TRACE(argv->GetThread(), String, MatchAll);
645     JSThread *thread = argv->GetThread();
646     [[maybe_unused]] EcmaHandleScope handleScope(thread);
647     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
648     // 1. Let O be ? RequireObjectCoercible(this value).
649     JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
650     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
651     JSHandle<JSTaggedValue> regexp = BuiltinsString::GetCallArg(argv, 0);
652     EcmaVM *ecmaVm = thread->GetEcmaVM();
653     JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
654     JSHandle<JSTaggedValue> matchAllTag = env->GetMatchAllSymbol();
655     JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
656 
657     // 2. If regexp is neither undefined nor null, then
658     if (!regexp->IsUndefined() && !regexp->IsNull()) {
659         // a. Let isRegExp be ? IsRegExp(searchValue).
660         if (regexp->IsECMAObject() &&
661             BuiltinsRegExp::IsFastRegExp(thread, regexp, BuiltinsRegExp::RegExpSymbol::MATCH)) {
662             bool isGlobal = BuiltinsRegExp::GetOriginalFlag(thread, regexp, RegExpParser::FLAG_GLOBAL);
663             if (!isGlobal) {
664                 THROW_TYPE_ERROR_AND_RETURN(thread,
665                                             "matchAll called with a non-global RegExp argument",
666                                             JSTaggedValue::Exception());
667             }
668         } else if (JSObject::IsRegExp(thread, regexp)) {
669             // i. Let flags be ? Get(searchValue, "flags").
670             JSHandle<JSTaggedValue> flagsString(globalConst->GetHandledFlagsString());
671             JSHandle<JSTaggedValue> flags = JSObject::GetProperty(thread, regexp, flagsString).GetValue();
672             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
673             // ii. Perform ? RequireObjectCoercible(flags).
674             JSTaggedValue::RequireObjectCoercible(thread, flags);
675             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
676             // iii. If ? ToString(flags) does not contain "g", throw a TypeError exception.
677             JSHandle<EcmaString> flagString = JSTaggedValue::ToString(thread, flags);
678             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
679             int32_t pos = EcmaStringAccessor::IndexOf(ecmaVm,
680                 flagString, ecmaVm->GetFactory()->NewFromASCII("g"));
681             if (pos == -1) {
682                 THROW_TYPE_ERROR_AND_RETURN(thread,
683                                             "matchAll called with a non-global RegExp argument",
684                                             JSTaggedValue::Exception());
685             }
686         }
687 
688         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
689         if (thisTag->IsString() && regexp->IsECMAObject()) {
690             if (PropertyDetector::IsRegExpSpeciesDetectorValid(env) &&
691                 BuiltinsRegExp::IsFastRegExp(thread, regexp, BuiltinsRegExp::RegExpSymbol::MATCHALL)) {
692                 JSHandle<EcmaString> string = JSHandle<EcmaString>::Cast(thisTag);
693                 return BuiltinsRegExp::RegExpMatchAll(thread, regexp, string, true);
694             }
695         }
696         // c. Let matcher be ? GetMethod(regexp, @@matchAll).
697         // d. If matcher is not undefined, then
698         bool canSkip = (PropertyDetector::IsNumberStringNotRegexpLikeDetectorValid(env) &&
699                        (regexp->IsString() || regexp->IsNumber()));
700         if (!canSkip) {
701             JSHandle<JSTaggedValue> matcher = JSObject::GetMethod(thread, regexp, matchAllTag);
702             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
703             if (!matcher->IsUndefined()) {
704                 // i. Return ? Call(matcher, regexp, « O »).
705                 EcmaRuntimeCallInfo *info =
706                     EcmaInterpreter::NewRuntimeCallInfo(thread, matcher, regexp, undefined, 1);
707                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
708                 info->SetCallArg(thisTag.GetTaggedValue());
709                 return JSFunction::Call(info);
710             }
711         }
712     }
713     // 3. Let S be ? ToString(O).
714     JSHandle<EcmaString> thisVal = JSTaggedValue::ToString(thread, thisTag);
715     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
716     // 4. Let rx be ? RegExpCreate(regexp, "g").
717     JSHandle<JSTaggedValue> rx(thread, BuiltinsRegExp::RegExpCreate(
718         thread, regexp, JSHandle<JSTaggedValue>(ecmaVm->GetFactory()->NewFromASCII("g"))));
719     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
720     EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, rx, undefined, 1);
721     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
722     info->SetCallArg(thisVal.GetTaggedValue());
723     return JSFunction::Invoke(info, matchAllTag);
724 }
725 
IsWellFormed(EcmaRuntimeCallInfo * argv)726 JSTaggedValue BuiltinsString::IsWellFormed(EcmaRuntimeCallInfo *argv)
727 {
728     ASSERT(argv);
729     BUILTINS_API_TRACE(argv->GetThread(), String, IsWellFormed);
730     JSThread *thread = argv->GetThread();
731     [[maybe_unused]] EcmaHandleScope handleScope(thread);
732 
733     // 1. Let O be ? RequireObjectCoercible(this value).
734     JSHandle<JSTaggedValue> obj(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
735     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
736 
737     // 2. Let S be ? ToString(O).
738     JSHandle<EcmaString> string = JSTaggedValue::ToString(thread, obj);
739     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
740 
741     // 3. Return IsStringWellFormedUnicode(S).
742     uint32_t size = EcmaStringAccessor(string).GetLength();
743     uint32_t position = 0;
744     while (position < size) {
745         // i.Let first be the code unit at index position within string.
746         uint16_t first = EcmaStringAccessor(string).Get(position);
747         uint32_t cp = first - CHAR16_LETTER_NULL;
748         uint8_t codeUnitCount = 0;
749         bool isUnpairedSurrogate = false;
750         // ii. If first is neither a leading surrogate nor a trailing surrogate, then
751         //   a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: false }.
752         if (!IsUTF16HighSurrogate(first) && !IsUTF16LowSurrogate(first)) {
753             codeUnitCount = 1; // 1 means: code unit count
754             isUnpairedSurrogate = false;
755         } else if (IsUTF16HighSurrogate(first) && position + 1 == size) {
756             // iii. If first is a trailing surrogate or position + 1 = size, then
757             //   a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }.
758             codeUnitCount = 1;
759             isUnpairedSurrogate = true;
760         } else {
761             // iv. Let second be the code unit at index position + 1 within string.
762             uint16_t second = EcmaStringAccessor(string).Get(position + 1);
763             // v. If second is not a trailing surrogate, then
764             //   a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }.
765             if (!IsUTF16LowSurrogate(second)) {
766                 codeUnitCount = 1; // 1 means: code unit count
767                 isUnpairedSurrogate = true;
768             } else {
769             // vi. Set cp to UTF16SurrogatePairToCodePoint(first, second).
770             // vii. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 2, [[IsUnpairedSurrogate]]: false }.
771                 cp = UTF16SurrogatePairToCodePoint(first, second);
772                 codeUnitCount = 2; // 2 means: code unit count
773                 isUnpairedSurrogate = false;
774             }
775         }
776         if (isUnpairedSurrogate) {
777             return JSTaggedValue::False();
778         } else {
779             position = position + codeUnitCount;
780         }
781         thread->CheckSafepointIfSuspended();
782     }
783     return JSTaggedValue::True();
784 }
785 
ToWellFormed(EcmaRuntimeCallInfo * argv)786 JSTaggedValue BuiltinsString::ToWellFormed(EcmaRuntimeCallInfo *argv)
787 {
788     ASSERT(argv);
789     BUILTINS_API_TRACE(argv->GetThread(), String, ToWellFormed);
790     JSThread *thread = argv->GetThread();
791     [[maybe_unused]] EcmaHandleScope handleScope(thread);
792     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
793 
794     // 1. Let O be ? RequireObjectCoercible(this value).
795     JSHandle<JSTaggedValue> obj(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
796     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
797 
798     // 2. Let S be ? ToString(O).
799     JSHandle<EcmaString> string = JSTaggedValue::ToString(thread, obj);
800     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
801 
802     // 3. Let strLen be the length of S.
803     // 4. Let k be 0.
804     uint32_t size = EcmaStringAccessor(string).GetLength();
805     uint32_t position = 0;
806 
807     // 5. Let result be the empty String.
808     std::u16string r;
809 
810     // Repeat, while k < strLen,
811     //     a. Let cp be CodePointAt(S, k).
812     //     b. If cp.[[IsUnpairedSurrogate]] is true, then
813     //         i. Set result to the string-concatenation of result and 0xFFFD (REPLACEMENT CHARACTER).
814     //     c. Else,
815     //         i. Set result to the string-concatenation of result and UTF16EncodeCodePoint(cp.[[CodePoint]]).
816     //     d. Set k to k + cp.[[CodeUnitCount]].
817     while (position < size) {
818         // i.Let first be the code unit at index position within string.
819         uint16_t first = EcmaStringAccessor(string).Get(position);
820         uint32_t cp = first - CHAR16_LETTER_NULL;
821         uint8_t codeUnitCount = 0;
822         bool isUnpairedSurrogate = false;
823         // ii. If first is neither a leading surrogate nor a trailing surrogate, then
824         //   a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: false }.
825         if (!IsUTF16HighSurrogate(first) && !IsUTF16LowSurrogate(first)) {
826             codeUnitCount = 1; // 1 means: code unit count
827             isUnpairedSurrogate = false;
828         } else if (IsUTF16HighSurrogate(first) && position + 1 == size) {
829             // iii. If first is a trailing surrogate or position + 1 = size, then
830             //   a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }.
831             codeUnitCount = 1;
832             isUnpairedSurrogate = true;
833         } else {
834             // iv. Let second be the code unit at index position + 1 within string.
835             uint16_t second = EcmaStringAccessor(string).Get(position + 1);
836             // v. If second is not a trailing surrogate, then
837             //   a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }.
838             if (!IsUTF16LowSurrogate(second)) {
839                 codeUnitCount = 1; // 1 means: code unit count
840                 isUnpairedSurrogate = true;
841             } else {
842             // vi. Set cp to UTF16SurrogatePairToCodePoint(first, second).
843             // vii. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 2, [[IsUnpairedSurrogate]]: false }.
844                 cp = UTF16SurrogatePairToCodePoint(first, second);
845                 codeUnitCount = 2; // 2 means: code unit count
846                 isUnpairedSurrogate = false;
847             }
848         }
849         if (isUnpairedSurrogate) {
850             r.push_back(0xFFFD);
851         } else {
852             if (cp < 0 || cp > ENCODE_MAX_UTF16) {
853                 THROW_RANGE_ERROR_AND_RETURN(thread, "CodePoint < 0 or CodePoint > 0x10FFFF",
854                                              JSTaggedValue::Exception());
855             }
856             if (cp > UINT16_MAX) {
857                 uint16_t cu1 = std::floor((static_cast<uint32_t>(cp) - ENCODE_SECOND_FACTOR) /
858                                            ENCODE_FIRST_FACTOR) + ENCODE_LEAD_LOW;
859                 uint16_t cu2 = ((static_cast<uint32_t>(cp) - ENCODE_SECOND_FACTOR) %
860                                  ENCODE_FIRST_FACTOR) + ENCODE_TRAIL_LOW;
861                 std::u16string nextU16str1 = base::StringHelper::Utf16ToU16String(&cu1, 1);
862                 std::u16string nextU16str2 = base::StringHelper::Utf16ToU16String(&cu2, 1);
863                 base::StringHelper::InplaceAppend(r, nextU16str1);
864                 base::StringHelper::InplaceAppend(r, nextU16str2);
865             } else {
866                 auto u16tCp = static_cast<uint16_t>(cp);
867                 std::u16string nextU16str = base::StringHelper::Utf16ToU16String(&u16tCp, 1);
868                 base::StringHelper::InplaceAppend(r, nextU16str);
869             }
870         }
871         position = position + codeUnitCount;
872         thread->CheckSafepointIfSuspended();
873     }
874     const char16_t *constChar16tData = r.data();
875     auto *char16tData = const_cast<char16_t *>(constChar16tData);
876     auto *uint16tData = reinterpret_cast<uint16_t *>(char16tData);
877     uint32_t u16strSize = r.size();
878     return factory->NewFromUtf16Literal(uint16tData, u16strSize).GetTaggedValue();
879 }
880 
881 // Static Semantics: UTF16SurrogatePairToCodePoint ( lead, trail )
UTF16SurrogatePairToCodePoint(uint16_t lead,uint16_t trail)882 uint32_t BuiltinsString::UTF16SurrogatePairToCodePoint(uint16_t lead, uint16_t trail)
883 {
884     // 1. Assert: lead is a leading surrogate and trail is a trailing surrogate.
885     ASSERT(IsUTF16HighSurrogate(lead) && IsUTF16LowSurrogate(trail));
886     // 2. Let cp be (lead - 0xD800) × 0x400 + (trail - 0xDC00) + 0x10000.
887     uint32_t cp = ((lead - 0xD800) << 10UL) + (trail - 0xDC00) + 0x10000;
888     // 3. Return the code point cp.
889     return cp;
890 }
891 
892 // 21.1.3.12
Normalize(EcmaRuntimeCallInfo * argv)893 JSTaggedValue BuiltinsString::Normalize(EcmaRuntimeCallInfo *argv)
894 {
895     ASSERT(argv);
896     BUILTINS_API_TRACE(argv->GetThread(), String, Normalize);
897     JSThread *thread = argv->GetThread();
898     [[maybe_unused]] EcmaHandleScope handleScope(thread);
899     auto vm = thread->GetEcmaVM();
900     ObjectFactory *factory = vm->GetFactory();
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     JSHandle<EcmaString> nfc = JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledNfcString());
918     JSHandle<EcmaString> nfd = factory->NewFromASCII("NFD");
919     JSHandle<EcmaString> nfkc = factory->NewFromASCII("NFKC");
920     JSHandle<EcmaString> nfkd = factory->NewFromASCII("NFKD");
921     UNormalizationMode uForm;
922     if (EcmaStringAccessor::StringsAreEqual(vm, formValue, nfc)) {
923         uForm = UNORM_NFC;
924     } else if (EcmaStringAccessor::StringsAreEqual(vm, formValue, nfd)) {
925         uForm = UNORM_NFD;
926     } else if (EcmaStringAccessor::StringsAreEqual(vm, formValue, nfkc)) {
927         uForm = UNORM_NFKC;
928     } else if (EcmaStringAccessor::StringsAreEqual(vm, formValue, nfkd)) {
929         uForm = UNORM_NFKD;
930     } else {
931         THROW_RANGE_ERROR_AND_RETURN(thread, "compare not equal", JSTaggedValue::Exception());
932     }
933 
934     std::u16string u16strThis = EcmaStringAccessor(thisHandle).ToU16String();
935     const char16_t *constChar16tData = u16strThis.data();
936     icu::UnicodeString src(constChar16tData, u16strThis.size());
937     icu::UnicodeString res;
938     UErrorCode errorCode = U_ZERO_ERROR;
939     int32_t option = 0;
940 
941     icu::Normalizer::normalize(src, uForm, option, res, errorCode);
942     JSHandle<EcmaString> str = intl::LocaleHelper::UStringToString(thread, res);
943     return JSTaggedValue(*str);
944 }
945 
PadStart(EcmaRuntimeCallInfo * argv)946 JSTaggedValue BuiltinsString::PadStart(EcmaRuntimeCallInfo *argv)
947 {
948     ASSERT(argv);
949     BUILTINS_API_TRACE(argv->GetThread(), String, PadStart);
950     return BuiltinsString::Pad(argv, true);
951 }
952 
PadEnd(EcmaRuntimeCallInfo * argv)953 JSTaggedValue BuiltinsString::PadEnd(EcmaRuntimeCallInfo *argv)
954 {
955     ASSERT(argv);
956     BUILTINS_API_TRACE(argv->GetThread(), String, PadEnd);
957     return BuiltinsString::Pad(argv, false);
958 }
959 
960 // 21.1.3.13
Repeat(EcmaRuntimeCallInfo * argv)961 JSTaggedValue BuiltinsString::Repeat(EcmaRuntimeCallInfo *argv)
962 {
963     ASSERT(argv);
964     BUILTINS_API_TRACE(argv->GetThread(), String, Repeat);
965     JSThread *thread = argv->GetThread();
966     [[maybe_unused]] EcmaHandleScope handleScope(thread);
967     JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
968     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
969     JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
970     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
971     uint32_t thisLen = EcmaStringAccessor(thisHandle).GetLength();
972     JSHandle<JSTaggedValue> countTag = BuiltinsString::GetCallArg(argv, 0);
973     int32_t count = 0;
974     if (countTag->IsInt()) {
975         count = countTag->GetInt();
976     } else {
977         JSTaggedNumber num = JSTaggedValue::ToInteger(thread, countTag);
978         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
979         double d = num.GetNumber();
980         if (d == base::POSITIVE_INFINITY) {
981             THROW_RANGE_ERROR_AND_RETURN(thread, "is infinity", JSTaggedValue::Exception());
982         }
983         count = base::NumberHelper::DoubleInRangeInt32(d);
984     }
985     if (count < 0) {
986         THROW_RANGE_ERROR_AND_RETURN(thread, "less than 0", JSTaggedValue::Exception());
987     }
988     if (count == 0) {
989         auto emptyStr = thread->GetEcmaVM()->GetFactory()->GetEmptyString();
990         return emptyStr.GetTaggedValue();
991     }
992     if (thisLen == 0) {
993         return thisHandle.GetTaggedValue();
994     }
995     if (static_cast<uint32_t>(count) >= static_cast<uint32_t>(EcmaString::MAX_STRING_LENGTH) / thisLen) {
996         THROW_RANGE_ERROR_AND_RETURN(thread, "Invalid string length", JSTaggedValue::Exception());
997     }
998     bool isUtf8 = EcmaStringAccessor(thisHandle).IsUtf8();
999     EcmaString *result = EcmaStringAccessor::CreateLineString(thread->GetEcmaVM(), thisLen * count, isUtf8);
1000     for (uint32_t index = 0; index < static_cast<uint32_t>(count); ++index) {
1001         EcmaStringAccessor::ReadData(result, *thisHandle, index * thisLen, (count - index) * thisLen, thisLen);
1002     }
1003     return JSTaggedValue(result);
1004 }
1005 
1006 // 21.1.3.14
Replace(EcmaRuntimeCallInfo * argv)1007 JSTaggedValue BuiltinsString::Replace(EcmaRuntimeCallInfo *argv)
1008 {
1009     ASSERT(argv);
1010     BUILTINS_API_TRACE(argv->GetThread(), String, Replace);
1011     JSThread *thread = argv->GetThread();
1012     [[maybe_unused]] EcmaHandleScope handleScope(thread);
1013     JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, BuiltinsString::GetThis(argv));
1014     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1015 
1016     auto ecmaVm = thread->GetEcmaVM();
1017     JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
1018     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1019     JSHandle<JSTaggedValue> searchTag = BuiltinsString::GetCallArg(argv, 0);
1020     JSHandle<JSTaggedValue> replaceTag = BuiltinsString::GetCallArg(argv, 1);
1021 
1022     ObjectFactory *factory = ecmaVm->GetFactory();
1023 
1024     if (searchTag->IsJSRegExp() && replaceTag->IsString()) {
1025         JSHandle<RegExpExecResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetRegExpCache());
1026         JSHandle<JSRegExp> re(searchTag);
1027         JSHandle<JSTaggedValue> pattern(thread, re->GetOriginalSource());
1028         JSHandle<JSTaggedValue> flags(thread, re->GetOriginalFlags());
1029         bool isFastPath = BuiltinsRegExp::IsFastRegExp(thread, searchTag);
1030         if (isFastPath) {
1031             uint32_t lastIndex = static_cast<uint32_t>(BuiltinsRegExp::GetLastIndex(thread, searchTag, true));
1032             JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, thisTag,
1033                 RegExpExecResultCache::REPLACE_TYPE, searchTag, JSTaggedValue(lastIndex),
1034                 replaceTag);
1035             if (!cacheResult.IsUndefined()) {
1036                 return cacheResult;
1037             }
1038         }
1039     }
1040 
1041     if (searchTag->IsJSRegExp() && PropertyDetector::IsRegExpReplaceDetectorValid(env)) {
1042         JSTaggedValue proto = JSObject::GetPrototype(JSHandle<JSObject>(searchTag));
1043         if (proto == env->GetTaggedRegExpPrototype()) {
1044             return BuiltinsRegExp::ReplaceInternal(thread, searchTag, thisTag, replaceTag);
1045         }
1046     }
1047 
1048     // If searchValue is neither undefined nor null, then
1049     if (!searchTag->IsUndefined() && !searchTag->IsNull()) {
1050         JSHandle<JSTaggedValue> replaceKey = env->GetReplaceSymbol();
1051         // Let replacer be GetMethod(searchValue, @@replace).
1052         JSHandle<JSTaggedValue> replaceMethod = JSObject::GetMethod(thread, searchTag, replaceKey);
1053         // ReturnIfAbrupt(replacer).
1054         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1055         // If replacer is not undefined, then
1056         if (!replaceMethod->IsUndefined()) {
1057             // Return Call(replacer, searchValue, «O, replaceValue»).
1058             const uint32_t argsLength = 2;
1059             JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
1060             EcmaRuntimeCallInfo *info =
1061                 EcmaInterpreter::NewRuntimeCallInfo(thread, replaceMethod, searchTag, undefined, argsLength);
1062             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1063             info->SetCallArg(thisTag.GetTaggedValue(), replaceTag.GetTaggedValue());
1064             return JSFunction::Call(info);
1065         }
1066     }
1067 
1068     // Let string be ToString(O).
1069     JSHandle<EcmaString> thisString = JSTaggedValue::ToString(thread, thisTag);
1070     // ReturnIfAbrupt(string).
1071     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1072     // Let searchString be ToString(searchValue).
1073     JSHandle<EcmaString> searchString = JSTaggedValue::ToString(thread, searchTag);
1074     // ReturnIfAbrupt(searchString).
1075     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1076     // Let functionalReplace be IsCallable(replaceValue).
1077     if (!replaceTag->IsCallable()) {
1078         // If functionalReplace is false, then
1079         // Let replaceValue be ToString(replaceValue).
1080         // ReturnIfAbrupt(replaceValue)
1081         replaceTag = JSHandle<JSTaggedValue>(JSTaggedValue::ToString(thread, replaceTag));
1082         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1083     }
1084     // Search string for the first occurrence of searchString and let pos be the index within string of the first code
1085     // unit of the matched substring and let matched be searchString. If no occurrences of searchString were found,
1086     // return string.
1087     int32_t pos = EcmaStringAccessor::IndexOf(ecmaVm, thisString, searchString);
1088     if (pos == -1) {
1089         return thisString.GetTaggedValue();
1090     }
1091     JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
1092     JSMutableHandle<JSTaggedValue> replHandle(thread, factory->GetEmptyString().GetTaggedValue());
1093     // If functionalReplace is true, then
1094     if (replaceTag->IsCallable()) {
1095         // Let replValue be Call(replaceValue, undefined,«matched, pos, and string»).
1096         const uint32_t argsLength = 3; // 3: «matched, pos, and string»
1097         EcmaRuntimeCallInfo *info =
1098             EcmaInterpreter::NewRuntimeCallInfo(thread, replaceTag, undefined, undefined, argsLength);
1099         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1100         info->SetCallArg(searchString.GetTaggedValue(), JSTaggedValue(pos), thisString.GetTaggedValue());
1101         JSTaggedValue replStrDeocodeValue = JSFunction::Call(info);
1102         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1103         replHandle.Update(replStrDeocodeValue);
1104     } else {
1105         // Let captures be an empty List.
1106         JSHandle<TaggedArray> capturesList = factory->EmptyArray();
1107         ASSERT_PRINT(replaceTag->IsString(), "replace must be string");
1108         JSHandle<EcmaString> replacement(thread, replaceTag->GetTaggedObject());
1109         // Let replStr be GetSubstitution(matched, string, pos, captures, replaceValue)
1110         replHandle.Update(GetSubstitution(thread, searchString, thisString, pos, capturesList, undefined, replacement));
1111     }
1112     JSHandle<EcmaString> realReplaceStr = JSTaggedValue::ToString(thread, replHandle);
1113     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1114     // Let tailPos be pos + the number of code units in matched.
1115     int32_t tailPos = pos + static_cast<int32_t>(EcmaStringAccessor(searchString).GetLength());
1116     // Let newString be the String formed by concatenating the first pos code units of string,
1117     // replStr, and the trailing
1118     // substring of string starting at index tailPos. If pos is 0,
1119     // the first element of the concatenation will be the
1120     // empty String.
1121     // Return newString.
1122     JSHandle<EcmaString> prefixString(thread, EcmaStringAccessor::FastSubString(ecmaVm, thisString, 0, pos));
1123     auto thisLen = EcmaStringAccessor(thisString).GetLength();
1124     JSHandle<EcmaString> suffixString(thread,
1125         EcmaStringAccessor::FastSubString(ecmaVm, thisString, tailPos, thisLen - tailPos));
1126     EcmaString *tempStr = EcmaStringAccessor::Concat(ecmaVm, prefixString, realReplaceStr);
1127     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1128     JSHandle<EcmaString> tempString(thread, tempStr);
1129     EcmaString *resultStr = EcmaStringAccessor::Concat(ecmaVm, tempString, suffixString);
1130     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1131     return JSTaggedValue(resultStr);
1132 }
1133 
ReplaceAll(EcmaRuntimeCallInfo * argv)1134 JSTaggedValue BuiltinsString::ReplaceAll(EcmaRuntimeCallInfo *argv)
1135 {
1136     ASSERT(argv);
1137     JSThread *thread = argv->GetThread();
1138     BUILTINS_API_TRACE(thread, String, ReplaceAll);
1139     [[maybe_unused]] EcmaHandleScope handleScope(thread);
1140     JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, BuiltinsString::GetThis(argv));
1141     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1142 
1143     auto ecmaVm = thread->GetEcmaVM();
1144     JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
1145     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1146     JSHandle<JSTaggedValue> searchTag = BuiltinsString::GetCallArg(argv, 0);
1147     JSHandle<JSTaggedValue> replaceTag = BuiltinsString::GetCallArg(argv, 1);
1148 
1149     ObjectFactory *factory = ecmaVm->GetFactory();
1150 
1151     if (!searchTag->IsUndefined() && !searchTag->IsNull()) {
1152         // a. Let isRegExp be ? IsRegExp(searchValue).
1153         bool isJSRegExp = JSObject::IsRegExp(thread, searchTag);
1154         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1155         // b. If isRegExp is true, then
1156         if (isJSRegExp) {
1157             // i. Let flags be ? Get(searchValue, "flags").
1158             JSHandle<JSTaggedValue> flagsString(globalConst->GetHandledFlagsString());
1159             JSHandle<JSTaggedValue> flags = JSObject::GetProperty(thread, searchTag, flagsString).GetValue();
1160             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1161             // ii. Perform ? RequireObjectCoercible(flags).
1162             JSTaggedValue::RequireObjectCoercible(thread, flags);
1163             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1164             // iii. If ? ToString(flags) does not contain "g", throw a TypeError exception.
1165             JSHandle<EcmaString> flagString = JSTaggedValue::ToString(thread, flags);
1166             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1167             JSHandle<EcmaString> gString(globalConst->GetHandledGString());
1168             int32_t pos = EcmaStringAccessor::IndexOf(ecmaVm, flagString, gString);
1169             if (pos == -1) {
1170                 THROW_TYPE_ERROR_AND_RETURN(thread,
1171                                             "string.prototype.replaceAll called with a non-global RegExp argument",
1172                                             JSTaggedValue::Exception());
1173             }
1174         }
1175         // c. Let replacer be ? GetMethod(searchValue, @@replace).
1176         JSHandle<JSTaggedValue> replaceKey = env->GetReplaceSymbol();
1177         JSHandle<JSTaggedValue> replaceMethod = JSObject::GetMethod(thread, searchTag, replaceKey);
1178         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1179         // d. If replacer is not undefined, then
1180         if (!replaceMethod->IsUndefined()) {
1181             // i. Return ? Call(replacer, searchValue, «O, replaceValue»).
1182             const size_t argsLength = 2;
1183             JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
1184             EcmaRuntimeCallInfo *info =
1185                 EcmaInterpreter::NewRuntimeCallInfo(thread, replaceMethod, searchTag, undefined, argsLength);
1186             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1187             info->SetCallArg(thisTag.GetTaggedValue(), replaceTag.GetTaggedValue());
1188             return JSFunction::Call(info);
1189         }
1190     }
1191 
1192     // 3. Let string be ? ToString(O).
1193     JSHandle<EcmaString> thisString = JSTaggedValue::ToString(thread, thisTag);
1194     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1195     // 4. Let searchString be ? ToString(searchValue).
1196     JSHandle<EcmaString> searchString = JSTaggedValue::ToString(thread, searchTag);
1197     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1198     // 5. Let functionalReplace be IsCallable(replaceValue).
1199     // 6. If functionalReplace is false, then
1200     if (!replaceTag->IsCallable()) {
1201         // a. Set replaceValue to ? ToString(replaceValue).
1202         replaceTag = JSHandle<JSTaggedValue>(JSTaggedValue::ToString(thread, replaceTag));
1203         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1204     }
1205 
1206     // 7. Let searchLength be the length of searchString.
1207     // 8. Let advanceBy be max(1, searchLength).
1208     int32_t searchLength = static_cast<int32_t>(EcmaStringAccessor(searchString).GetLength());
1209     int32_t advanceBy = std::max(1, searchLength);
1210     // 9. Let matchPositions be a new empty List.
1211     JSMutableHandle<EcmaString> accumulatedResult(thread, factory->GetEmptyString());
1212     // 10. Let position be ! StringIndexOf(string, searchString, 0).
1213     int32_t pos = EcmaStringAccessor::IndexOf(ecmaVm, thisString, searchString);
1214     int32_t endOfLastMatch = 0;
1215     JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
1216     JSMutableHandle<JSTaggedValue> replHandle(thread, factory->GetEmptyString().GetTaggedValue());
1217     while (pos != -1) {
1218         // If functionalReplace is true, then
1219         if (replaceTag->IsCallable()) {
1220             // Let replValue be Call(replaceValue, undefined,«matched, pos, and string»).
1221             const uint32_t argsLength = 3; // 3: «matched, pos, and string»
1222             EcmaRuntimeCallInfo *info =
1223                 EcmaInterpreter::NewRuntimeCallInfo(thread, replaceTag, undefined, undefined, argsLength);
1224             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1225             info->SetCallArg(searchString.GetTaggedValue(), JSTaggedValue(pos), thisString.GetTaggedValue());
1226             JSTaggedValue replStrDeocodeValue = JSFunction::Call(info);
1227             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1228             replHandle.Update(replStrDeocodeValue);
1229         } else {
1230             // Let captures be an empty List.
1231             JSHandle<TaggedArray> capturesList = factory->NewTaggedArray(0);
1232             ASSERT_PRINT(replaceTag->IsString(), "replace must be string");
1233             JSHandle<EcmaString> replacement(thread, replaceTag->GetTaggedObject());
1234             // Let replStr be GetSubstitution(matched, string, pos, captures, replaceValue)
1235             replHandle.Update(GetSubstitution(thread, searchString, thisString, pos,
1236                                               capturesList, undefined, replacement));
1237         }
1238         JSHandle<EcmaString> realReplaceStr = JSTaggedValue::ToString(thread, replHandle);
1239         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1240         // Let tailPos be pos + the number of code units in matched.
1241         // Let newString be the String formed by concatenating the first pos code units of string,
1242         // replStr, and the trailing substring of string starting at index tailPos.
1243         // If pos is 0, the first element of the concatenation will be the
1244         // empty String.
1245         // Return newString.
1246         JSHandle<EcmaString> prefixString(thread,
1247                                           EcmaStringAccessor::FastSubString(ecmaVm, thisString, endOfLastMatch,
1248                                                                             pos - endOfLastMatch));
1249         accumulatedResult.Update(JSTaggedValue(EcmaStringAccessor::Concat(ecmaVm, accumulatedResult, prefixString)));
1250         accumulatedResult.Update(JSTaggedValue(EcmaStringAccessor::Concat(ecmaVm, accumulatedResult, realReplaceStr)));
1251         endOfLastMatch = pos + searchLength;
1252         pos = EcmaStringAccessor::IndexOf(ecmaVm, thisString, searchString, pos + advanceBy);
1253         thread->CheckSafepointIfSuspended();
1254     }
1255 
1256     if (endOfLastMatch < static_cast<int32_t>(EcmaStringAccessor(thisString).GetLength())) {
1257         auto thisLen = EcmaStringAccessor(thisString).GetLength();
1258         JSHandle<EcmaString> suffixString(thread,
1259             EcmaStringAccessor::FastSubString(ecmaVm, thisString, endOfLastMatch, thisLen - endOfLastMatch));
1260         accumulatedResult.Update(JSTaggedValue(EcmaStringAccessor::Concat(ecmaVm, accumulatedResult, suffixString)));
1261     }
1262 
1263     return accumulatedResult.GetTaggedValue();
1264 }
1265 
1266 // Handle $& - match case
ProcessDollarAmpersand(std::u16string & stringBuilder,const JSHandle<EcmaString> & matched,bool & canBeCompress)1267 void ProcessDollarAmpersand(std::u16string &stringBuilder, const JSHandle<EcmaString> &matched, bool &canBeCompress)
1268 {
1269     stringBuilder += EcmaStringAccessor(matched).ToU16String();
1270     if (EcmaStringAccessor(matched).IsUtf16()) {
1271         canBeCompress = false;
1272     }
1273 }
1274 
1275 // Handle $` - prefix case
ProcessDollarBacktick(EcmaVM * ecmaVm,std::u16string & stringBuilder,const JSHandle<EcmaString> & srcString,int position,bool & canBeCompress)1276 void ProcessDollarBacktick(EcmaVM *ecmaVm, std::u16string &stringBuilder, const JSHandle<EcmaString> &srcString,
1277                            int position, bool &canBeCompress)
1278 {
1279     if (position > 0) {
1280         EcmaString *prefix = EcmaStringAccessor::FastSubString(ecmaVm, srcString, 0, position);
1281         stringBuilder += EcmaStringAccessor(prefix).ToU16String();
1282         if (EcmaStringAccessor(prefix).IsUtf16()) {
1283             canBeCompress = false;
1284         }
1285     }
1286 }
1287 
1288 // Handle $' - suffix case
ProcessDollarSingleQuote(EcmaVM * ecmaVm,std::u16string & stringBuilder,const JSHandle<EcmaString> & srcString,int tailPos,bool & canBeCompress)1289 void ProcessDollarSingleQuote(EcmaVM *ecmaVm, std::u16string &stringBuilder, const JSHandle<EcmaString> &srcString,
1290                               int tailPos, bool &canBeCompress)
1291 {
1292     int32_t srcLength = static_cast<int32_t>(EcmaStringAccessor(srcString).GetLength());
1293     if (tailPos < srcLength) {
1294         EcmaString *suffix = EcmaStringAccessor::FastSubString(ecmaVm, srcString, tailPos, srcLength - tailPos);
1295         stringBuilder += EcmaStringAccessor(suffix).ToU16String();
1296         if (EcmaStringAccessor(suffix).IsUtf16()) {
1297             canBeCompress = false;
1298         }
1299     }
1300 }
1301 
ProcessDigitCapture(const JSHandle<EcmaString> & replacementFlat,uint32_t peekIndex,uint32_t replaceLength,const JSHandle<TaggedArray> & captureList,std::u16string & stringBuilder)1302 std::pair<int32_t, bool> ProcessDigitCapture(const JSHandle<EcmaString> &replacementFlat, uint32_t peekIndex,
1303                                              uint32_t replaceLength, const JSHandle<TaggedArray> &captureList,
1304                                              std::u16string &stringBuilder)
1305 {
1306     uint32_t capturesLength = captureList->GetLength();
1307     uint16_t peek = EcmaStringAccessor(replacementFlat).Get(peekIndex);
1308     uint32_t scaledIndex = peek - '0';
1309     int32_t advance = 1;
1310     bool canBeCompress = true;
1311 
1312     if (peekIndex + 1 < replaceLength) {
1313         uint16_t nextPeek = EcmaStringAccessor(replacementFlat).Get(peekIndex + 1);
1314         if (nextPeek >= '0' && nextPeek <= '9') {
1315             constexpr uint32_t tenBase = 10;
1316             uint32_t newScaledIndex = scaledIndex * tenBase + (nextPeek - '0');
1317             if (newScaledIndex <= capturesLength) {
1318                 scaledIndex = newScaledIndex;
1319                 advance = 2;  // 2: 2 means from index needs to add two.
1320             }
1321         }
1322     }
1323 
1324     if (scaledIndex == 0 || scaledIndex > capturesLength) {
1325         stringBuilder += '$';
1326         return {peekIndex, canBeCompress};  // No change in compressibility, just return the next index.
1327     }
1328 
1329     JSTaggedValue capturesVal(captureList->Get(scaledIndex - 1));
1330     if (!capturesVal.IsUndefined()) {
1331         EcmaString *captureString = EcmaString::Cast(capturesVal.GetTaggedObject());
1332         stringBuilder += EcmaStringAccessor(captureString).ToU16String();
1333         if (EcmaStringAccessor(captureString).IsUtf16()) {
1334             canBeCompress = false;
1335         }
1336     }
1337     return {static_cast<int32_t>(peekIndex) + advance, canBeCompress};
1338 }
1339 
1340 // Handle $< case
ProcessNamedCaptures(JSThread * thread,const JSHandle<EcmaString> & replacementFlat,int32_t peekIndex,const JSHandle<JSTaggedValue> & namedCaptures,std::u16string & stringBuilder)1341 std::pair<int32_t, bool> ProcessNamedCaptures(JSThread *thread, const JSHandle<EcmaString> &replacementFlat,
1342                                               int32_t peekIndex, const JSHandle<JSTaggedValue> &namedCaptures,
1343                                               std::u16string &stringBuilder)
1344 {
1345     bool canBeCompress = true;
1346     if (namedCaptures->IsUndefined()) {
1347         stringBuilder += '$';
1348         return {peekIndex, canBeCompress};
1349     }
1350     auto ecmaVm = thread->GetEcmaVM();
1351     ObjectFactory *factory = ecmaVm->GetFactory();
1352     JSHandle<EcmaString> greaterSymString = factory->NewFromASCII(">");
1353     int32_t pos = EcmaStringAccessor::IndexOf(ecmaVm, replacementFlat, greaterSymString, peekIndex);
1354     if (pos == -1) {
1355         stringBuilder += '$';
1356         return {peekIndex, canBeCompress};
1357     }
1358     JSHandle<EcmaString> groupName = JSHandle<EcmaString>(
1359         thread, EcmaStringAccessor::FastSubString(ecmaVm, replacementFlat, peekIndex + 1, pos - peekIndex - 1));
1360     JSHandle<JSTaggedValue> names(groupName);
1361     JSHandle<JSTaggedValue> capture = JSObject::GetProperty(thread, namedCaptures, names).GetValue();
1362     if (capture->IsUndefined()) {
1363         return {pos + 1, canBeCompress};
1364     }
1365     JSHandle<EcmaString> captureName = JSTaggedValue::ToString(thread, capture);
1366     stringBuilder += EcmaStringAccessor(captureName).ToU16String();
1367     if (EcmaStringAccessor(captureName).IsUtf16()) {
1368         canBeCompress = false;
1369     }
1370     return {pos + 1, canBeCompress};
1371 }
1372 
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)1373 JSTaggedValue BuiltinsString::GetSubstitution(JSThread *thread, const JSHandle<EcmaString> &matched,
1374                                               const JSHandle<EcmaString> &srcString, int position,
1375                                               const JSHandle<TaggedArray> &captureList,
1376                                               const JSHandle<JSTaggedValue> &namedCaptures,
1377                                               const JSHandle<EcmaString> &replacement)
1378 {
1379     BUILTINS_API_TRACE(thread, String, GetSubstitution);
1380     auto ecmaVm = thread->GetEcmaVM();
1381     ObjectFactory *factory = ecmaVm->GetFactory();
1382     JSHandle<EcmaString> dollarString = JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledDollarString());
1383     JSHandle<EcmaString> replacementFlat(thread, EcmaStringAccessor::Flatten(ecmaVm, replacement));
1384     int32_t replaceLength = static_cast<int32_t>(EcmaStringAccessor(replacementFlat).GetLength());
1385     int32_t tailPos = position + static_cast<int32_t>(EcmaStringAccessor(matched).GetLength());
1386 
1387     int32_t nextDollarIndex = EcmaStringAccessor::IndexOf(ecmaVm, replacementFlat, dollarString);
1388     if (nextDollarIndex < 0) {
1389         return replacementFlat.GetTaggedValue();
1390     }
1391     std::u16string stringBuilder;
1392     bool canBeCompress = true;
1393     if (nextDollarIndex > 0) {
1394         stringBuilder = EcmaStringAccessor(replacementFlat).ToU16String(nextDollarIndex);
1395         if (EcmaStringAccessor(replacementFlat).IsUtf16()) {
1396             canBeCompress = false;
1397         }
1398     }
1399 
1400     while (true) {
1401         int peekIndex = nextDollarIndex + 1;
1402         if (peekIndex >= replaceLength) {
1403             stringBuilder += '$';
1404             auto *char16tData = const_cast<char16_t *>(stringBuilder.c_str());
1405             auto *uint16tData = reinterpret_cast<uint16_t *>(char16tData);
1406             return canBeCompress ?
1407                    factory->NewFromUtf16LiteralCompress(uint16tData, stringBuilder.length()).GetTaggedValue() :
1408                    factory->NewFromUtf16LiteralNotCompress(uint16tData, stringBuilder.length()).GetTaggedValue();
1409         }
1410         int continueFromIndex = -1;
1411         uint16_t peek = EcmaStringAccessor(replacementFlat).Get(peekIndex);
1412         switch (peek) {
1413             case '$':  // $$
1414                 stringBuilder += '$';
1415                 continueFromIndex = peekIndex + 1;
1416                 break;
1417             case '&':  // $& - match
1418                 ProcessDollarAmpersand(stringBuilder, matched, canBeCompress);
1419                 continueFromIndex = peekIndex + 1;
1420                 break;
1421             case '`':  // $` - prefix
1422                 ProcessDollarBacktick(ecmaVm, stringBuilder, srcString, position, canBeCompress);
1423                 continueFromIndex = peekIndex + 1;
1424                 break;
1425             case '\'': {  // $' - suffix
1426                 ProcessDollarSingleQuote(ecmaVm, stringBuilder, srcString, tailPos, canBeCompress);
1427                 continueFromIndex = peekIndex + 1;
1428                 break;
1429             }
1430             case '0':
1431             case '1':
1432             case '2':
1433             case '3':
1434             case '4':
1435             case '5':
1436             case '6':
1437             case '7':
1438             case '8':
1439             case '9': {
1440                 auto result =
1441                     ProcessDigitCapture(replacementFlat, peekIndex, replaceLength, captureList, stringBuilder);
1442                 continueFromIndex = result.first;
1443                 canBeCompress = result.second && canBeCompress;  // 保留canBeCompress的值,只在需要时更新为false
1444                 break;
1445             }
1446             case '<': {
1447                 auto result = ProcessNamedCaptures(thread, replacementFlat, peekIndex, namedCaptures, stringBuilder);
1448                 continueFromIndex = result.first;
1449                 canBeCompress = result.second && canBeCompress;  // 保留canBeCompress的值,只在需要时更新为false
1450                 break;
1451             }
1452             default:
1453                 stringBuilder += '$';
1454                 continueFromIndex = peekIndex;
1455                 break;
1456         }
1457         // Go the the next $ in the replacement.
1458         nextDollarIndex = EcmaStringAccessor::IndexOf(ecmaVm, replacementFlat, dollarString, continueFromIndex);
1459         if (nextDollarIndex < 0) {
1460             if (continueFromIndex < replaceLength) {
1461                 EcmaString *nextAppend = EcmaStringAccessor::FastSubString(ecmaVm, replacementFlat, continueFromIndex,
1462                                                                            replaceLength - continueFromIndex);
1463                 stringBuilder += EcmaStringAccessor(nextAppend).ToU16String();
1464                 if (EcmaStringAccessor(nextAppend).IsUtf16()) {
1465                     canBeCompress = false;
1466                 }
1467             }
1468             auto *char16tData = const_cast<char16_t *>(stringBuilder.c_str());
1469             auto *uint16tData = reinterpret_cast<uint16_t *>(char16tData);
1470             return canBeCompress ?
1471                    factory->NewFromUtf16LiteralCompress(uint16tData, stringBuilder.length()).GetTaggedValue() :
1472                    factory->NewFromUtf16LiteralNotCompress(uint16tData, stringBuilder.length()).GetTaggedValue();
1473         }
1474         // Append substring between the previous and the next $ character.
1475         if (nextDollarIndex > continueFromIndex) {
1476             EcmaString *nextAppend = EcmaStringAccessor::FastSubString(
1477                 ecmaVm, replacementFlat, continueFromIndex, nextDollarIndex - continueFromIndex);
1478             stringBuilder += EcmaStringAccessor(nextAppend).ToU16String();
1479             if (EcmaStringAccessor(nextAppend).IsUtf16()) {
1480                 canBeCompress = false;
1481             }
1482         }
1483         thread->CheckSafepointIfSuspended();
1484     }
1485     LOG_ECMA(FATAL) << "this branch is unreachable";
1486     UNREACHABLE();
1487 }
1488 
1489 // 21.1.3.15
Search(EcmaRuntimeCallInfo * argv)1490 JSTaggedValue BuiltinsString::Search(EcmaRuntimeCallInfo *argv)
1491 {
1492     ASSERT(argv);
1493     BUILTINS_API_TRACE(argv->GetThread(), String, Search);
1494     JSThread *thread = argv->GetThread();
1495     [[maybe_unused]] EcmaHandleScope handleScope(thread);
1496     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1497     JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1498     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1499     JSHandle<JSTaggedValue> regexp = BuiltinsString::GetCallArg(argv, 0);
1500     if (thisTag->IsString() && regexp->IsECMAObject()) {
1501         if (BuiltinsRegExp::IsFastRegExp(thread, regexp, BuiltinsRegExp::RegExpSymbol::SEARCH)) {
1502             return BuiltinsRegExp::RegExpSearchFast(thread, regexp, thisTag);
1503         }
1504     }
1505     JSHandle<JSTaggedValue> searchTag = thread->GetEcmaVM()->GetGlobalEnv()->GetSearchSymbol();
1506     JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
1507     if (!regexp->IsUndefined() && !regexp->IsNull()) {
1508         JSHandle<JSTaggedValue> searcher = JSObject::GetMethod(thread, regexp, searchTag);
1509         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1510         if (!searcher->IsUndefined()) {
1511             ASSERT(searcher->IsJSFunctionBase());
1512             EcmaRuntimeCallInfo *info =
1513                 EcmaInterpreter::NewRuntimeCallInfo(thread, searcher, regexp, undefined, 1);
1514             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1515             info->SetCallArg(thisTag.GetTaggedValue());
1516             return JSFunction::Call(info);
1517         }
1518     }
1519     JSHandle<EcmaString> thisVal = JSTaggedValue::ToString(thread, thisTag);
1520     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1521     JSHandle<JSTaggedValue> rx(thread, BuiltinsRegExp::RegExpCreate(thread, regexp, undefined));
1522     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1523     EcmaRuntimeCallInfo *info =
1524         EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, rx, undefined, 1);
1525     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1526     info->SetCallArg(thisVal.GetTaggedValue());
1527     return JSFunction::Invoke(info, searchTag);
1528 }
1529 
1530 // 21.1.3.16
Slice(EcmaRuntimeCallInfo * argv)1531 JSTaggedValue BuiltinsString::Slice(EcmaRuntimeCallInfo *argv)
1532 {
1533     ASSERT(argv);
1534     BUILTINS_API_TRACE(argv->GetThread(), String, Slice);
1535     JSThread *thread = argv->GetThread();
1536     [[maybe_unused]] EcmaHandleScope handleScope(thread);
1537 
1538     JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1539     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1540     JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
1541     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1542     int32_t thisLen = static_cast<int32_t>(EcmaStringAccessor(thisHandle).GetLength());
1543     JSHandle<JSTaggedValue> startTag = BuiltinsString::GetCallArg(argv, 0);
1544     JSTaggedNumber startVal = JSTaggedValue::ToInteger(thread, startTag);
1545     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1546     int32_t start = ConvertDoubleToInt(startVal.GetNumber());
1547     int32_t end = 0;
1548     JSHandle<JSTaggedValue> endTag = BuiltinsString::GetCallArg(argv, 1);
1549     if (endTag->IsUndefined()) {
1550         end = thisLen;
1551     } else {
1552         JSTaggedNumber endVal = JSTaggedValue::ToInteger(thread, endTag);
1553         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1554         end = ConvertDoubleToInt(endVal.GetNumber());
1555     }
1556     int32_t from = 0;
1557     int32_t to = 0;
1558     if (start < 0) {
1559         from = std::max(start + thisLen, 0);
1560     } else {
1561         from = std::min(start, thisLen);
1562     }
1563     if (end < 0) {
1564         to = std::max(end + thisLen, 0);
1565     } else {
1566         to = std::min(end, thisLen);
1567     }
1568     int32_t len = std::max(to - from, 0);
1569     return JSTaggedValue(EcmaStringAccessor::FastSubString(thread->GetEcmaVM(), thisHandle, from, len));
1570 }
1571 
1572 // 21.1.3.17
Split(EcmaRuntimeCallInfo * argv)1573 JSTaggedValue BuiltinsString::Split(EcmaRuntimeCallInfo *argv)
1574 {
1575     ASSERT(argv);
1576     BUILTINS_API_TRACE(argv->GetThread(), String, Split);
1577     JSThread *thread = argv->GetThread();
1578     [[maybe_unused]] EcmaHandleScope handleScope(thread);
1579     auto ecmaVm = thread->GetEcmaVM();
1580     JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
1581 
1582     // Let O be RequireObjectCoercible(this value).
1583     JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv));
1584     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1585     JSHandle<JSTaggedValue> seperatorTag = BuiltinsString::GetCallArg(argv, 0);
1586     JSHandle<JSTaggedValue> limitTag = BuiltinsString::GetCallArg(argv, 1);
1587 
1588     if (thisTag->IsString() && seperatorTag->IsECMAObject()) {
1589         // this condition need change, all regexp should use RegExpSplit
1590         if (BuiltinsRegExp::IsFastRegExp(thread, seperatorTag)) {
1591             return BuiltinsRegExp::RegExpSplit(thread, seperatorTag, thisTag, limitTag, true);
1592         }
1593     }
1594     if (thisTag->IsString() && seperatorTag->IsString()) {
1595         JSHandle<EcmaString> thisString(thisTag);
1596         JSHandle<EcmaString> seperatorString(seperatorTag);
1597         auto thisLength = EcmaStringAccessor(thisString).GetLength();
1598         auto seperatorLength = EcmaStringAccessor(seperatorString).GetLength();
1599         if (limitTag->IsUndefined() && thisLength != 0 && seperatorLength != 0) {
1600             return CreateArrayThisStringAndSeperatorStringAreNotEmpty(
1601                 thread, ecmaVm, thisString, seperatorString, thisLength, seperatorLength);
1602         }
1603         uint32_t lim = UINT32_MAX - 1;
1604         if (!limitTag->IsUndefined()) {
1605             JSTaggedNumber limitIntValue = JSTaggedValue::ToInteger(thread, limitTag);
1606             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1607             lim = limitIntValue.ToUint32();
1608         }
1609         // ReturnIfAbrupt(lim).
1610         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1611         if (lim == 0) {
1612             JSHandle<JSObject> resultArray(JSArray::ArrayCreate(thread, JSTaggedNumber(0)));
1613             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1614             return resultArray.GetTaggedValue();
1615         }
1616         return CreateArrayBySplitString(thread, ecmaVm, thisString, seperatorString, thisLength, seperatorLength, lim);
1617     }
1618 
1619     // If separator is neither undefined nor null, then
1620     if (!seperatorTag->IsUndefined() && !seperatorTag->IsNull()) {
1621         JSHandle<JSTaggedValue> splitKey = env->GetSplitSymbol();
1622         // Let splitter be GetMethod(separator, @@split).
1623         JSHandle<JSTaggedValue> splitter = JSObject::GetMethod(thread, seperatorTag, splitKey);
1624         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1625         if (!splitter->IsUndefined()) {
1626             // Return Call(splitter, separator, «‍O, limit»).
1627             const uint32_t argsLength = 2;
1628             JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1629             EcmaRuntimeCallInfo *info =
1630                 EcmaInterpreter::NewRuntimeCallInfo(thread, splitter, seperatorTag, undefined, argsLength);
1631             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1632             info->SetCallArg(thisTag.GetTaggedValue(), limitTag.GetTaggedValue());
1633             return JSFunction::Call(info);
1634         }
1635     }
1636     // Let S be ToString(O).
1637     JSHandle<EcmaString> thisString = JSTaggedValue::ToString(thread, thisTag);
1638     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1639 
1640     // If limit is undefined, let lim = 2^53–1; else let lim = ToLength(limit).
1641     uint32_t lim = UINT32_MAX - 1;
1642     if (!limitTag->IsUndefined()) {
1643         JSTaggedNumber limitIntValue = JSTaggedValue::ToInteger(thread, limitTag);
1644         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1645         lim = limitIntValue.ToUint32();
1646     }
1647     // ReturnIfAbrupt(lim).
1648     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1649     // Let s be the number of elements in S.
1650     auto thisLength = EcmaStringAccessor(thisString).GetLength();
1651     JSHandle<EcmaString> seperatorString = JSTaggedValue::ToString(thread, seperatorTag);
1652     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1653     // If lim = 0, return A.
1654     if (lim == 0) {
1655         JSHandle<JSObject> resultArray(JSArray::ArrayCreate(thread, JSTaggedNumber(0)));
1656         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1657         return resultArray.GetTaggedValue();
1658     }
1659     auto seperatorLength = EcmaStringAccessor(seperatorString).GetLength();
1660     // If S is undefined or (this.length = 0 and S.length != 0), return array of size is 1 containing this string
1661     if (seperatorTag->IsUndefined()) {
1662         JSHandle<JSObject> resultArray(JSArray::ArrayCreate(thread, JSTaggedNumber(1)));
1663         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1664         // Perform CreateDataProperty(A, "0", S), CreateDataProperty's fast path
1665         JSObject::CreateDataProperty(thread, resultArray, 0, JSHandle<JSTaggedValue>(thisString));
1666         ASSERT_PRINT(!thread->HasPendingException(), "CreateDataProperty(A, \"0\", S) can't throw exception");
1667         return resultArray.GetTaggedValue();
1668     }
1669     return CreateArrayBySplitString(thread, ecmaVm, thisString, seperatorString, thisLength, seperatorLength, lim);
1670 }
1671 
CreateArrayFromString(JSThread * thread,EcmaVM * ecmaVm,const JSHandle<EcmaString> & thisString,uint32_t thisLength,uint32_t lim)1672 JSTaggedValue BuiltinsString::CreateArrayFromString(JSThread *thread, EcmaVM *ecmaVm,
1673     const JSHandle<EcmaString> &thisString, uint32_t thisLength, uint32_t lim)
1674 {
1675     bool isUtf8 = EcmaStringAccessor(thisString).IsUtf8();
1676     bool canBeCompressed = false;
1677     if (EcmaStringAccessor(thisString).IsLineOrConstantString()) {
1678         canBeCompressed = EcmaStringAccessor::CanBeCompressed(*thisString);
1679     }
1680     bool isOneByte = isUtf8 & canBeCompressed;
1681     JSHandle<EcmaString> seperatorString = thread->GetEcmaVM()->GetFactory()->GetEmptyString();
1682     if (lim == UINT32_MAX - 1) {
1683         JSHandle<StringSplitResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetStringSplitResultCache());
1684         JSTaggedValue cacheResult = StringSplitResultCache::FindCachedResult(thread, cacheTable, thisString,
1685             seperatorString, isOneByte);
1686         if (cacheResult != JSTaggedValue::Undefined()) {
1687             JSHandle<JSTaggedValue> resultArray(JSArray::CreateArrayFromList(thread,
1688                 JSHandle<TaggedArray>(thread, cacheResult)));
1689             return resultArray.GetTaggedValue();
1690         }
1691     }
1692     uint32_t actualLength = std::min(thisLength, lim);
1693     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1694     JSHandle<TaggedArray> array = factory->NewTaggedArray(actualLength);
1695     for (uint32_t i = 0; i < actualLength; ++i) {
1696         EcmaString *elementString = EcmaStringAccessor::GetSubString(ecmaVm, thisString, i, 1);
1697         // Perform CreateDataProperty(A, "0", S), CreateDataProperty's fast path
1698         if (isOneByte) {
1699             array->Set<false>(thread, i, JSTaggedValue(elementString));
1700         } else {
1701             array->Set(thread, i, JSTaggedValue(elementString));
1702         }
1703         ASSERT_PRINT(!thread->HasPendingException(), "CreateDataProperty can't throw exception");
1704     }
1705     JSHandle<JSArray> resultArray = JSArray::CreateArrayFromList(thread, array);
1706     if (lim == UINT32_MAX - 1) {
1707         JSHandle<StringSplitResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetStringSplitResultCache());
1708         StringSplitResultCache::SetCachedResult(thread, cacheTable, thisString, seperatorString, array);
1709     }
1710     return resultArray.GetTaggedValue();
1711 }
1712 
CreateArrayBySplitString(JSThread * thread,EcmaVM * ecmaVm,const JSHandle<EcmaString> & thisString,const JSHandle<EcmaString> & seperatorString,uint32_t thisLength,uint32_t seperatorLength,uint32_t lim)1713 JSTaggedValue BuiltinsString::CreateArrayBySplitString(JSThread *thread, EcmaVM *ecmaVm,
1714     const JSHandle<EcmaString> &thisString, const JSHandle<EcmaString> &seperatorString,
1715     uint32_t thisLength, uint32_t seperatorLength, uint32_t lim)
1716 {
1717     if (thisLength != 0) {
1718         if (seperatorLength != 0) {
1719             return CreateArrayThisStringAndSeperatorStringAreNotEmpty(
1720                 thread, ecmaVm, thisString, seperatorString, thisLength, seperatorLength, lim);
1721         }
1722         return CreateArrayFromString(thread, ecmaVm, thisString, thisLength, lim);
1723     } else {
1724         if (seperatorLength != 0) {
1725             JSHandle<JSObject> resultArray(JSArray::ArrayCreate(thread, JSTaggedNumber(1)));
1726             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1727             // Perform CreateDataProperty(A, "0", S), CreateDataProperty's fast path
1728             JSObject::CreateDataProperty(thread, resultArray, 0, JSHandle<JSTaggedValue>(thisString));
1729             ASSERT_PRINT(!thread->HasPendingException(), "CreateDataProperty(A, \"0\", S) can't throw exception");
1730             return resultArray.GetTaggedValue();
1731         }
1732         JSHandle<JSObject> resultArray(JSArray::ArrayCreate(thread, JSTaggedNumber(0)));
1733         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1734         return resultArray.GetTaggedValue();
1735     }
1736 }
1737 
CreateArrayThisStringAndSeperatorStringAreNotEmpty(JSThread * thread,EcmaVM * ecmaVm,const JSHandle<EcmaString> & thisString,const JSHandle<EcmaString> & seperatorString,uint32_t thisLength,uint32_t seperatorLength,uint32_t lim)1738 JSTaggedValue BuiltinsString::CreateArrayThisStringAndSeperatorStringAreNotEmpty(JSThread *thread,
1739     EcmaVM *ecmaVm, const JSHandle<EcmaString> &thisString, const JSHandle<EcmaString> &seperatorString,
1740     uint32_t thisLength, uint32_t seperatorLength, uint32_t lim)
1741 {
1742     if (lim == UINT32_MAX - 1) {
1743         JSHandle<StringSplitResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetStringSplitResultCache());
1744         JSTaggedValue cacheResult = StringSplitResultCache::FindCachedResult(thread, cacheTable, thisString,
1745             seperatorString);
1746         if (cacheResult != JSTaggedValue::Undefined()) {
1747             JSHandle<JSTaggedValue> resultArray(JSArray::CreateArrayFromList(thread,
1748                 JSHandle<TaggedArray>(thread, cacheResult)));
1749             return resultArray.GetTaggedValue();
1750         }
1751     }
1752     uint32_t arrayLength = 0;
1753     std::vector<int32_t> posArray;
1754     int32_t index = 0;
1755     int32_t pos = EcmaStringAccessor::IndexOf(ecmaVm, thisString, seperatorString);
1756     while (pos != -1) {
1757         posArray.emplace_back(pos);
1758         ++arrayLength;
1759         if (arrayLength == lim) {
1760             break;
1761         }
1762         index = pos + static_cast<int32_t>(seperatorLength);
1763         pos = EcmaStringAccessor::IndexOf(ecmaVm, thisString, seperatorString, index);
1764         thread->CheckSafepointIfSuspended();
1765     }
1766     uint32_t posArrLength = posArray.size();
1767     arrayLength = lim > posArrLength ? posArrLength + 1 : posArrLength;
1768     return JSArray::ArrayCreateWithInit(thread, arrayLength,
1769         [thread, ecmaVm, &thisString, &seperatorString, &posArray, thisLength, seperatorLength, lim, posArrLength]
1770         (const JSHandle<TaggedArray> &newElements, [[maybe_unused]] uint32_t length) {
1771         int32_t index = 0;
1772         int32_t pos = 0;
1773         JSMutableHandle<JSTaggedValue> elementTag(thread, JSTaggedValue::Undefined());
1774         for (uint32_t i = 0; i < posArrLength; i++) {
1775             pos = posArray[i];
1776             EcmaString *elementString = EcmaStringAccessor::GetSubString(ecmaVm, thisString, index, pos - index);
1777             elementTag.Update(JSTaggedValue(elementString));
1778             newElements->Set(thread, i, elementTag);
1779             index = pos + static_cast<int32_t>(seperatorLength);
1780         }
1781         if (lim > posArrLength) {
1782             EcmaString *elementString =
1783                 EcmaStringAccessor::GetSubString(ecmaVm, thisString, index, thisLength - index);
1784             elementTag.Update(JSTaggedValue(elementString));
1785             newElements->Set(thread, posArrLength, elementTag);
1786         }
1787         if (lim == UINT32_MAX - 1) {
1788             JSHandle<StringSplitResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetStringSplitResultCache());
1789             StringSplitResultCache::SetCachedResult(thread, cacheTable, thisString, seperatorString, newElements);
1790         }
1791     });
1792 }
1793 
1794 // 21.1.3.18
StartsWith(EcmaRuntimeCallInfo * argv)1795 JSTaggedValue BuiltinsString::StartsWith(EcmaRuntimeCallInfo *argv)
1796 {
1797     ASSERT(argv);
1798     BUILTINS_API_TRACE(argv->GetThread(), String, StartsWith);
1799     JSThread *thread = argv->GetThread();
1800     [[maybe_unused]] EcmaHandleScope handleScope(thread);
1801     JSHandle<JSTaggedValue> searchTag = BuiltinsString::GetCallArg(argv, 0);
1802 
1803     JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1804     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1805     JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
1806     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1807     bool isRegexp = JSObject::IsRegExp(thread, searchTag);
1808     if (isRegexp) {
1809         THROW_TYPE_ERROR_AND_RETURN(thread, "is regexp", JSTaggedValue::Exception());
1810     }
1811 
1812     JSHandle<EcmaString> searchHandle = JSTaggedValue::ToString(thread, searchTag);
1813     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1814     uint32_t thisLen = EcmaStringAccessor(thisHandle).GetLength();
1815     uint32_t searchLen = EcmaStringAccessor(searchHandle).GetLength();
1816     int32_t pos = 0;
1817     JSHandle<JSTaggedValue> posTag = BuiltinsString::GetCallArg(argv, 1);
1818     if (posTag->IsUndefined()) {
1819         pos = 0;
1820     } else if (posTag->IsInt()) {
1821         pos = posTag->GetInt();
1822     } else {
1823         JSTaggedNumber posVal = JSTaggedValue::ToInteger(thread, posTag);
1824         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1825         if (posVal.GetNumber() == BuiltinsNumber::POSITIVE_INFINITY) {
1826             pos = thisLen;
1827         } else {
1828             pos = posVal.ToInt32();
1829         }
1830     }
1831     pos = std::min(std::max(pos, 0), static_cast<int32_t>(thisLen));
1832     if (static_cast<uint32_t>(pos) + searchLen > thisLen) {
1833         return BuiltinsString::GetTaggedBoolean(false);
1834     }
1835 
1836     bool result = EcmaStringAccessor::IsSubStringAt(thread->GetEcmaVM(), thisHandle, searchHandle, pos);
1837 
1838     return BuiltinsString::GetTaggedBoolean(result);
1839 }
1840 
1841 // 21.1.3.19
Substring(EcmaRuntimeCallInfo * argv)1842 JSTaggedValue BuiltinsString::Substring(EcmaRuntimeCallInfo *argv)
1843 {
1844     ASSERT(argv);
1845     BUILTINS_API_TRACE(argv->GetThread(), String, Substring);
1846     JSThread *thread = argv->GetThread();
1847     [[maybe_unused]] EcmaHandleScope handleScope(thread);
1848 
1849     JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1850     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1851     JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
1852     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1853     int32_t thisLen = static_cast<int32_t>(EcmaStringAccessor(thisHandle).GetLength());
1854     JSHandle<JSTaggedValue> startTag = BuiltinsString::GetCallArg(argv, 0);
1855     JSTaggedNumber startVal = JSTaggedValue::ToInteger(thread, startTag);
1856     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1857     int32_t start = ConvertDoubleToInt(startVal.GetNumber());
1858     int32_t end = 0;
1859     JSHandle<JSTaggedValue> endTag = BuiltinsString::GetCallArg(argv, 1);
1860     if (endTag->IsUndefined()) {
1861         end = thisLen;
1862     } else {
1863         JSTaggedNumber endVal = JSTaggedValue::ToInteger(thread, endTag);
1864         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1865         end = ConvertDoubleToInt(endVal.GetNumber());
1866     }
1867     start = std::min(std::max(start, 0), thisLen);
1868     end = std::min(std::max(end, 0), thisLen);
1869     int32_t from = std::min(start, end);
1870     int32_t to = std::max(start, end);
1871     int32_t len = to - from;
1872     return JSTaggedValue(EcmaStringAccessor::GetSubString(thread->GetEcmaVM(), thisHandle, from, len));
1873 }
1874 
1875 // 21.1.3.20
ToLocaleLowerCase(EcmaRuntimeCallInfo * argv)1876 JSTaggedValue BuiltinsString::ToLocaleLowerCase(EcmaRuntimeCallInfo *argv)
1877 {
1878     ASSERT(argv);
1879     BUILTINS_API_TRACE(argv->GetThread(), String, ToLocaleLowerCase);
1880     JSThread *thread = argv->GetThread();
1881     EcmaVM *ecmaVm = thread->GetEcmaVM();
1882     [[maybe_unused]] EcmaHandleScope handleScope(thread);
1883 
1884     // Let O be RequireObjectCoercible(this value).
1885     JSHandle<JSTaggedValue> obj(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1886     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1887     // Let S be ? ToString(O).
1888     JSHandle<EcmaString> string = JSTaggedValue::ToString(thread, obj);
1889     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1890 
1891     // Let requestedLocales be ? CanonicalizeLocaleList(locales).
1892     JSHandle<JSTaggedValue> locales = GetCallArg(argv, 0);
1893     // Fast path
1894     if (locales->IsUndefined() && EcmaStringAccessor(string).IsUtf8()) {
1895         EcmaString *result = EcmaStringAccessor::TryToLower(ecmaVm, string);
1896         return JSTaggedValue(result);
1897     }
1898     JSHandle<TaggedArray> requestedLocales = intl::LocaleHelper::CanonicalizeLocaleList(thread, locales);
1899     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1900 
1901     // If requestedLocales is not an empty List, then Let requestedLocale be requestedLocales[0].
1902     // Else, Let requestedLocale be DefaultLocale().
1903     JSHandle<EcmaString> requestedLocale = intl::LocaleHelper::DefaultLocale(thread);
1904     if (requestedLocales->GetLength() != 0) {
1905         requestedLocale = JSHandle<EcmaString>(thread, requestedLocales->Get(0));
1906     }
1907 
1908     // Let noExtensionsLocale be the String value that is requestedLocale with all Unicode locale extension sequences
1909     // removed.
1910     intl::LocaleHelper::ParsedLocale noExtensionsLocale = intl::LocaleHelper::HandleLocale(requestedLocale);
1911     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1912 
1913     // Let availableLocales be a List with language tags that includes the languages for which the Unicode Character
1914     // Database contains language sensitive case mappings. Implementations may add additional language tags
1915     // if they support case mapping for additional locales.
1916     std::vector<std::string> availableLocales = intl::LocaleHelper::GetAvailableLocales(thread, nullptr, nullptr);
1917     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1918 
1919     // Let locale be BestAvailableLocale(availableLocales, noExtensionsLocale).
1920     std::string locale = intl::LocaleHelper::BestAvailableLocale(availableLocales, noExtensionsLocale.base);
1921     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1922 
1923     // If locale is undefined, let locale be "und".
1924     if (locale.empty()) {
1925         locale = "und";
1926     }
1927 
1928     // Let uString be a List containing in order the code points of S as defined in ES2020, 6.1.4,
1929     // starting at the first element of S.
1930     // Transform those elements in uString to the to the Unicode Default Case Conversion algorithm
1931     icu::Locale icuLocale = icu::Locale::createFromName(locale.c_str());
1932     EcmaString *result = EcmaStringAccessor::ToLocaleLower(ecmaVm, string, icuLocale);
1933     return JSTaggedValue(result);
1934 }
1935 
1936 // 21.1.3.21
ToLocaleUpperCase(EcmaRuntimeCallInfo * argv)1937 JSTaggedValue BuiltinsString::ToLocaleUpperCase(EcmaRuntimeCallInfo *argv)
1938 {
1939     ASSERT(argv);
1940     BUILTINS_API_TRACE(argv->GetThread(), String, ToLocaleLowerCase);
1941     JSThread *thread = argv->GetThread();
1942     EcmaVM *ecmaVm = thread->GetEcmaVM();
1943     [[maybe_unused]] EcmaHandleScope handleScope(thread);
1944 
1945     // Let O be RequireObjectCoercible(this value).
1946     JSHandle<JSTaggedValue> obj(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1947     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1948     // Let S be ? ToString(O).
1949     JSHandle<EcmaString> string = JSTaggedValue::ToString(thread, obj);
1950     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1951 
1952     // Let requestedLocales be ? CanonicalizeLocaleList(locales).
1953     JSHandle<JSTaggedValue> locales = GetCallArg(argv, 0);
1954     // Fast path
1955     if (locales->IsUndefined() && EcmaStringAccessor(string).IsUtf8()) {
1956         EcmaString *result = EcmaStringAccessor::TryToUpper(ecmaVm, string);
1957         return JSTaggedValue(result);
1958     }
1959     JSHandle<TaggedArray> requestedLocales = intl::LocaleHelper::CanonicalizeLocaleList(thread, locales);
1960     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1961 
1962     // If requestedLocales is not an empty List, then Let requestedLocale be requestedLocales[0].
1963     // Else, Let requestedLocale be DefaultLocale().
1964     JSHandle<EcmaString> requestedLocale = intl::LocaleHelper::DefaultLocale(thread);
1965     if (requestedLocales->GetLength() != 0) {
1966         requestedLocale = JSHandle<EcmaString>(thread, requestedLocales->Get(0));
1967     }
1968 
1969     // Let noExtensionsLocale be the String value that is requestedLocale with all Unicode locale extension sequences
1970     // removed.
1971     intl::LocaleHelper::ParsedLocale noExtensionsLocale = intl::LocaleHelper::HandleLocale(requestedLocale);
1972     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1973 
1974     // Let availableLocales be a List with language tags that includes the languages for which the Unicode Character
1975     // Database contains language sensitive case mappings. Implementations may add additional language tags
1976     // if they support case mapping for additional locales.
1977     std::vector<std::string> availableLocales = intl::LocaleHelper::GetAvailableLocales(thread, nullptr, nullptr);
1978     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1979 
1980     // Let locale be BestAvailableLocale(availableLocales, noExtensionsLocale).
1981     std::string locale = intl::LocaleHelper::BestAvailableLocale(availableLocales, noExtensionsLocale.base);
1982     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1983 
1984     // If locale is undefined, let locale be "und".
1985     if (locale.empty()) {
1986         locale = "und";
1987     }
1988 
1989     // Let uString be a List containing in order the code points of S as defined in ES2020, 6.1.4,
1990     // starting at the first element of S.
1991     // Transform those elements in uString to the to the Unicode Default Case Conversion algorithm
1992     icu::Locale icuLocale = icu::Locale::createFromName(locale.c_str());
1993     EcmaString *result = EcmaStringAccessor::ToLocaleUpper(ecmaVm, string, icuLocale);
1994     return JSTaggedValue(result);
1995 }
1996 
1997 // 21.1.3.22
ToLowerCase(EcmaRuntimeCallInfo * argv)1998 JSTaggedValue BuiltinsString::ToLowerCase(EcmaRuntimeCallInfo *argv)
1999 {
2000     ASSERT(argv);
2001     BUILTINS_API_TRACE(argv->GetThread(), String, ToLowerCase);
2002     JSThread *thread = argv->GetThread();
2003     [[maybe_unused]] EcmaHandleScope handleScope(thread);
2004     JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
2005     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2006     JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2007     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2008     EcmaString *result = EcmaStringAccessor::ToLower(thread->GetEcmaVM(), thisHandle);
2009     return JSTaggedValue(result);
2010 }
2011 
2012 // 21.1.3.23
ToString(EcmaRuntimeCallInfo * argv)2013 JSTaggedValue BuiltinsString::ToString(EcmaRuntimeCallInfo *argv)
2014 {
2015     ASSERT(argv);
2016     return ThisStringValue(argv->GetThread(), GetThis(argv).GetTaggedValue());
2017 }
2018 
2019 // 21.1.3.24
ToUpperCase(EcmaRuntimeCallInfo * argv)2020 JSTaggedValue BuiltinsString::ToUpperCase(EcmaRuntimeCallInfo *argv)
2021 {
2022     ASSERT(argv);
2023     BUILTINS_API_TRACE(argv->GetThread(), String, ToUpperCase);
2024     JSThread *thread = argv->GetThread();
2025     [[maybe_unused]] EcmaHandleScope handleScope(thread);
2026 
2027     JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
2028     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2029     JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2030     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2031     EcmaString *result = EcmaStringAccessor::ToUpper(thread->GetEcmaVM(), thisHandle);
2032     return JSTaggedValue(result);
2033 }
2034 
2035 // 21.1.3.25
Trim(EcmaRuntimeCallInfo * argv)2036 JSTaggedValue BuiltinsString::Trim(EcmaRuntimeCallInfo *argv)
2037 {
2038     ASSERT(argv);
2039     BUILTINS_API_TRACE(argv->GetThread(), String, Trim);
2040     JSThread *thread = argv->GetThread();
2041     [[maybe_unused]] EcmaHandleScope handleScope(thread);
2042     JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
2043     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2044     JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2045     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2046     EcmaString *res = EcmaStringAccessor::Trim(thread, thisHandle, EcmaString::TrimMode::TRIM);
2047     return JSTaggedValue(res);
2048 }
2049 
TrimStart(EcmaRuntimeCallInfo * argv)2050 JSTaggedValue BuiltinsString::TrimStart(EcmaRuntimeCallInfo *argv)
2051 {
2052     ASSERT(argv);
2053     BUILTINS_API_TRACE(argv->GetThread(), String, TrimStart);
2054     JSThread *thread = argv->GetThread();
2055     [[maybe_unused]] EcmaHandleScope handleScope(thread);
2056     JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv));
2057     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2058     JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2059     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2060     EcmaString *res = EcmaStringAccessor::Trim(thread, thisHandle, EcmaString::TrimMode::TRIM_START);
2061     return JSTaggedValue(res);
2062 }
2063 
TrimEnd(EcmaRuntimeCallInfo * argv)2064 JSTaggedValue BuiltinsString::TrimEnd(EcmaRuntimeCallInfo *argv)
2065 {
2066     ASSERT(argv);
2067     BUILTINS_API_TRACE(argv->GetThread(), String, TrimEnd);
2068     JSThread *thread = argv->GetThread();
2069     [[maybe_unused]] EcmaHandleScope handleScope(thread);
2070     JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv));
2071     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2072     JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2073     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2074     EcmaString *res = EcmaStringAccessor::Trim(thread, thisHandle, EcmaString::TrimMode::TRIM_END);
2075     return JSTaggedValue(res);
2076 }
2077 
TrimLeft(EcmaRuntimeCallInfo * argv)2078 JSTaggedValue BuiltinsString::TrimLeft(EcmaRuntimeCallInfo *argv)
2079 {
2080     ASSERT(argv);
2081     BUILTINS_API_TRACE(argv->GetThread(), String, TrimLeft);
2082     JSThread *thread = argv->GetThread();
2083     [[maybe_unused]] EcmaHandleScope handleScope(thread);
2084     JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv));
2085     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2086     JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2087     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2088     EcmaString *res = EcmaStringAccessor::Trim(thread, thisHandle, EcmaString::TrimMode::TRIM_START);
2089     return JSTaggedValue(res);
2090 }
2091 
TrimRight(EcmaRuntimeCallInfo * argv)2092 JSTaggedValue BuiltinsString::TrimRight(EcmaRuntimeCallInfo *argv)
2093 {
2094     ASSERT(argv);
2095     BUILTINS_API_TRACE(argv->GetThread(), String, TrimRight);
2096     JSThread *thread = argv->GetThread();
2097     [[maybe_unused]] EcmaHandleScope handleScope(thread);
2098     JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv));
2099     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2100     JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2101     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2102     EcmaString *res = EcmaStringAccessor::Trim(thread, thisHandle, EcmaString::TrimMode::TRIM_END);
2103     return JSTaggedValue(res);
2104 }
2105 
2106 // 21.1.3.26
ValueOf(EcmaRuntimeCallInfo * argv)2107 JSTaggedValue BuiltinsString::ValueOf(EcmaRuntimeCallInfo *argv)
2108 {
2109     ASSERT(argv);
2110     return ThisStringValue(argv->GetThread(), GetThis(argv).GetTaggedValue());
2111 }
2112 
2113 // 21.1.3.27
GetStringIterator(EcmaRuntimeCallInfo * argv)2114 JSTaggedValue BuiltinsString::GetStringIterator(EcmaRuntimeCallInfo *argv)
2115 {
2116     ASSERT(argv);
2117     BUILTINS_API_TRACE(argv->GetThread(), String, GetStringIterator);
2118     JSThread *thread = argv->GetThread();
2119     [[maybe_unused]] EcmaHandleScope handleScope(thread);
2120     // 1. Let O be RequireObjectCoercible(this value).
2121     JSHandle<JSTaggedValue> current(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
2122     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2123     // Let S be ToString(O).
2124 
2125     JSHandle<EcmaString> string = JSTaggedValue::ToString(thread, current);
2126     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(argv->GetThread());
2127     // Return CreateStringIterator(S).
2128     return JSStringIterator::CreateStringIterator(thread, string).GetTaggedValue();
2129 }
2130 
2131 //  B.2.3.1
SubStr(EcmaRuntimeCallInfo * argv)2132 JSTaggedValue BuiltinsString::SubStr(EcmaRuntimeCallInfo *argv)
2133 {
2134     ASSERT(argv);
2135     BUILTINS_API_TRACE(argv->GetThread(), String, SubStr);
2136     JSThread *thread = argv->GetThread();
2137 
2138     [[maybe_unused]] EcmaHandleScope handleScope(thread);
2139 
2140     // 1. Let O be RequireObjectCoercible(this value).
2141     // 2. Let S be ToString(O).
2142 
2143     JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
2144     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2145     JSHandle<EcmaString> thisString = JSTaggedValue::ToString(thread, thisTag);
2146 
2147     // 3. ReturnIfAbrupt(S).
2148     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2149     JSHandle<JSTaggedValue> intStart = GetCallArg(argv, 0);
2150     // 4. Let intStart be ToInteger(start).
2151     JSTaggedNumber numStart = JSTaggedValue::ToInteger(thread, intStart);
2152     // 5. ReturnIfAbrupt(intStart).
2153     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2154     int32_t start = base::NumberHelper::DoubleInRangeInt32(numStart.GetNumber());
2155     JSHandle<JSTaggedValue> lengthTag = GetCallArg(argv, 1);
2156     // 6. If length is undefined, let end be +; otherwise let end be ToInteger(length).
2157     int32_t end = 0;
2158     if (lengthTag->IsUndefined()) {
2159         end = INT_MAX;
2160     } else {
2161         JSTaggedNumber lengthNumber = JSTaggedValue::ToInteger(thread, lengthTag);
2162         // 7. ReturnIfAbrupt(end).
2163         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2164         end = base::NumberHelper::DoubleInRangeInt32(lengthNumber.GetNumber());
2165     }
2166     // 8. Let size be the number of code units in S.
2167     int32_t size = static_cast<int32_t>(EcmaStringAccessor(thisString).GetLength());
2168     // 9. If intStart < 0, let intStart be max(size + intStart,0).
2169     if (start < 0) {
2170         start = std::max(size + start, 0);
2171     }
2172     // 10. Let resultLength be min(max(end,0), size – intStart).
2173     int32_t resultLength = std::min(std::max(end, 0), size - start);
2174     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2175     // 11. If resultLength  0, return the empty String "".
2176     if (resultLength <= 0) {
2177         return factory->GetEmptyString().GetTaggedValue();
2178     }
2179     return JSTaggedValue(EcmaStringAccessor::FastSubString(thread->GetEcmaVM(), thisString, start, resultLength));
2180 }
2181 
2182 // 22.1.3.1
At(EcmaRuntimeCallInfo * argv)2183 JSTaggedValue BuiltinsString::At(EcmaRuntimeCallInfo *argv)
2184 {
2185     ASSERT(argv);
2186     BUILTINS_API_TRACE(argv->GetThread(), String, At);
2187     JSThread *thread = argv->GetThread();
2188     [[maybe_unused]] EcmaHandleScope handleScope(thread);
2189 
2190     // 1. Let O be RequireObjectCoercible(this value).
2191     // 2. Let S be ToString(O).
2192     JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
2193     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2194     JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2195     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2196 
2197     // 3. Let len be the length of S.
2198     int32_t thisLen = static_cast<int32_t>(EcmaStringAccessor(thisHandle).GetLength());
2199 
2200     // 4. Let relativeIndex be ? ToIntegerOrInfinity(index).
2201     JSHandle<JSTaggedValue> indexTag = BuiltinsString::GetCallArg(argv, 0);
2202     JSTaggedNumber indexVal = JSTaggedValue::ToInteger(thread, indexTag);
2203     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2204     int32_t relativeIndex = base::NumberHelper::DoubleInRangeInt32(indexVal.GetNumber());
2205 
2206     // 5. If relativeIndex ≥ 0, then Let k be relativeIndex. 6. Else, Let k be len + relativeIndex.
2207     int32_t k = 0;
2208     if (relativeIndex >= 0) {
2209         k = relativeIndex;
2210     } else {
2211         k = thisLen + relativeIndex;
2212     }
2213     // 7. If k < 0 or k ≥ len, return undefined.
2214     if (k < 0 || k >= thisLen) {
2215         return JSTaggedValue::Undefined();
2216     }
2217     // 8. Return the substring of S from k to k + 1.
2218     return JSTaggedValue(EcmaStringAccessor::FastSubString(thread->GetEcmaVM(), thisHandle, k, 1));
2219 }
2220 
GetLength(EcmaRuntimeCallInfo * argv)2221 JSTaggedValue BuiltinsString::GetLength(EcmaRuntimeCallInfo *argv)
2222 {
2223     ASSERT(argv);
2224     JSThread *thread = argv->GetThread();
2225     BUILTINS_API_TRACE(thread, String, GetLength);
2226     [[maybe_unused]] EcmaHandleScope handleScope(thread);
2227     JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
2228 
2229     JSHandle<EcmaString> thisString = JSTaggedValue::ToString(thread, thisHandle);
2230     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2231     return GetTaggedInt(EcmaStringAccessor(thisString).GetLength());
2232 }
2233 
2234 // 21.1.3
ThisStringValue(JSThread * thread,JSTaggedValue value)2235 JSTaggedValue BuiltinsString::ThisStringValue(JSThread *thread, JSTaggedValue value)
2236 {
2237     BUILTINS_API_TRACE(thread, String, ThisStringValue);
2238     if (value.IsString()) {
2239         return value;
2240     }
2241     if (value.IsECMAObject()) {
2242         auto jshclass = value.GetTaggedObject()->GetClass();
2243         if (jshclass->GetObjectType() == JSType::JS_PRIMITIVE_REF) {
2244             JSTaggedValue primitive = JSPrimitiveRef::Cast(value.GetTaggedObject())->GetValue();
2245             if (primitive.IsString()) {
2246                 return primitive;
2247             }
2248         }
2249     }
2250     THROW_TYPE_ERROR_AND_RETURN(thread, "can not convert to String", JSTaggedValue::Exception());
2251 }
2252 
Pad(EcmaRuntimeCallInfo * argv,bool isStart)2253 JSTaggedValue BuiltinsString::Pad(EcmaRuntimeCallInfo *argv, bool isStart)
2254 {
2255     JSThread *thread = argv->GetThread();
2256     BUILTINS_API_TRACE(thread, String, Pad);
2257     [[maybe_unused]] EcmaHandleScope handleScope(thread);
2258 
2259     JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, BuiltinsString::GetThis(argv));
2260     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2261     JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
2262     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2263     JSHandle<JSTaggedValue> lengthTag = GetCallArg(argv, 0);
2264     JSTaggedNumber number = JSTaggedValue::ToNumber(thread, lengthTag);
2265     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2266     int64_t intMaxLength  = base::NumberHelper::DoubleToInt64(number.GetNumber());
2267     int32_t stringLength = static_cast<int32_t>(EcmaStringAccessor(thisHandle).GetLength());
2268     if (intMaxLength <= stringLength) {
2269         return thisHandle.GetTaggedValue();
2270     }
2271     JSHandle<JSTaggedValue> fillString = GetCallArg(argv, 1);
2272     std::u16string stringBuilder;
2273     if (fillString->IsUndefined()) {
2274         stringBuilder = u" ";
2275     } else {
2276         JSHandle<EcmaString> filler = JSTaggedValue::ToString(thread, fillString);
2277         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2278         stringBuilder = EcmaStringAccessor(filler).ToU16String();
2279     }
2280     if (stringBuilder.size() == 0) {
2281         return thisHandle.GetTaggedValue();
2282     }
2283     std::u16string u16strSearch = EcmaStringAccessor(thisHandle).ToU16String();
2284     int64_t fillLen = intMaxLength - stringLength;
2285     int64_t len = static_cast<int64_t>(stringBuilder.length());
2286     if (static_cast<size_t>(intMaxLength) >= EcmaString::MAX_STRING_LENGTH) {
2287         THROW_RANGE_ERROR_AND_RETURN(thread, "Invalid string length", JSTaggedValue::Exception());
2288     }
2289     std::u16string fiString;
2290     for (int32_t i = 0; i < fillLen; ++i) {
2291         fiString += stringBuilder[i % len];
2292     }
2293     std::u16string resultString;
2294     if (isStart) {
2295         resultString = fiString + u16strSearch;
2296     } else {
2297         resultString = u16strSearch + fiString;
2298     }
2299     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2300     return factory->NewFromUtf16Literal(reinterpret_cast<const uint16_t *>(resultString.c_str()),
2301                                         resultString.size()).GetTaggedValue();
2302 }
2303 
ConvertDoubleToInt(double d)2304 int32_t BuiltinsString::ConvertDoubleToInt(double d)
2305 {
2306     if (std::isnan(d) || d == -base::POSITIVE_INFINITY) {
2307         return 0;
2308     }
2309     if (d >= static_cast<double>(INT_MAX)) {
2310         return INT_MAX;
2311     }
2312     if (d <= static_cast<double>(INT_MIN)) {
2313         return INT_MIN;
2314     }
2315     return base::NumberHelper::DoubleToInt(d, base::INT32_BITS);
2316 }
2317 
StringToList(JSThread * thread,JSHandle<EcmaString> & str)2318 JSTaggedValue BuiltinsString::StringToList(JSThread *thread, JSHandle<EcmaString> &str)
2319 {
2320     JSHandle<StringToListResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetStringToListResultCache());
2321     JSTaggedValue cacheResult = StringToListResultCache::FindCachedResult(thread, cacheTable, str);
2322     if (cacheResult != JSTaggedValue::Undefined()) {
2323         JSHandle<JSTaggedValue> resultArray(JSArray::CreateArrayFromList(thread,
2324             JSHandle<TaggedArray>(thread, cacheResult)));
2325         return resultArray.GetTaggedValue();
2326     }
2327 
2328     JSTaggedValue newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue();
2329     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2330     JSHandle<JSObject> newArrayHandle(thread, newArray);
2331     JSHandle<EcmaString> iteratedString(str);
2332     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2333     JSHandle<TaggedArray> oldElements(thread, newArrayHandle->GetElements());
2334     uint32_t totalElements = EcmaStringAccessor(iteratedString).GetLength();
2335     JSHandle<TaggedArray> elements = (oldElements->GetLength() < totalElements) ?
2336         factory->ExtendArray(oldElements, totalElements) : oldElements;
2337     uint32_t index = 0;
2338     newArrayHandle->SetElements(thread, elements);
2339     while (index < totalElements) {
2340         uint16_t c = EcmaStringAccessor(iteratedString).Get(index);
2341         JSHandle<EcmaString> newStr = factory->NewFromUtf16Literal(&c, 1);
2342         ElementAccessor::Set(thread, newArrayHandle, index, newStr, true);
2343         index++;
2344         thread->CheckSafepointIfSuspended();
2345     }
2346     JSHandle<JSArray>(newArrayHandle)->SetArrayLength(thread, totalElements);
2347 
2348     StringToListResultCache::SetCachedResult(thread, cacheTable, str, elements);
2349 
2350     return newArrayHandle.GetTaggedValue();
2351 }
2352 
StringToSList(JSThread * thread,JSHandle<EcmaString> & str)2353 JSTaggedValue BuiltinsString::StringToSList(JSThread *thread, JSHandle<EcmaString> &str)
2354 {
2355     JSHandle<StringToListResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetStringToListResultCache());
2356     JSTaggedValue cacheResult = StringToListResultCache::FindCachedResult(thread, cacheTable, str);
2357     if (cacheResult != JSTaggedValue::Undefined()) {
2358         JSHandle<JSTaggedValue> resultArray(
2359             JSSharedArray::CreateArrayFromList(thread, JSHandle<TaggedArray>(thread, cacheResult)));
2360         return resultArray.GetTaggedValue();
2361     }
2362 
2363     JSTaggedValue newSharedArray = JSSharedArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue();
2364     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
2365     JSHandle<JSObject> newSharedArrayHandle(thread, newSharedArray);
2366     JSHandle<EcmaString> iteratedString(str);
2367     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2368     JSHandle<TaggedArray> oldElements(thread, newSharedArrayHandle->GetElements());
2369     uint32_t totalElements = EcmaStringAccessor(iteratedString).GetLength();
2370     JSHandle<TaggedArray> elements =
2371         (oldElements->GetLength() < totalElements)
2372             ? factory->ExtendArray(oldElements, totalElements, JSTaggedValue::Hole(), MemSpaceType::SHARED_OLD_SPACE)
2373             : oldElements;
2374     uint32_t index = 0;
2375     newSharedArrayHandle->SetElements(thread, elements);
2376     while (index < totalElements) {
2377         uint16_t c = EcmaStringAccessor(iteratedString).Get(index);
2378         JSHandle<EcmaString> newStr = factory->NewFromUtf16Literal(&c, 1);
2379         ElementAccessor::Set(thread, newSharedArrayHandle, index, newStr, true);
2380         index++;
2381         thread->CheckSafepointIfSuspended();
2382     }
2383     JSHandle<JSSharedArray>(newSharedArrayHandle)->SetArrayLength(thread, totalElements);
2384 
2385     StringToListResultCache::SetCachedResult(thread, cacheTable, str, elements);
2386     newSharedArrayHandle->GetJSHClass()->SetExtensible(false);
2387     return newSharedArrayHandle.GetTaggedValue();
2388 }
2389 
CreateCacheTable(const JSThread * thread)2390 JSTaggedValue StringSplitResultCache::CreateCacheTable(const JSThread *thread)
2391 {
2392     int length = CACHE_SIZE * ENTRY_SIZE;
2393     auto table = static_cast<StringSplitResultCache*>(
2394         *thread->GetEcmaVM()->GetFactory()->NewTaggedArray(length, JSTaggedValue::Undefined()));
2395     return JSTaggedValue(table);
2396 }
2397 
FindCachedResult(const JSThread * thread,const JSHandle<StringSplitResultCache> & cache,const JSHandle<EcmaString> & thisString,const JSHandle<EcmaString> & pattern,bool isOneByte)2398 JSTaggedValue StringSplitResultCache::FindCachedResult(const JSThread *thread,
2399     const JSHandle<StringSplitResultCache> &cache, const JSHandle<EcmaString> &thisString,
2400     const JSHandle<EcmaString> &pattern, bool isOneByte)
2401 {
2402     uint32_t hash = EcmaStringAccessor(thisString).GetHashcode();
2403     uint32_t entry = hash & (CACHE_SIZE - 1);
2404     uint32_t index = entry * ENTRY_SIZE;
2405     JSTaggedValue cacheThis = cache->Get(index + STRING_INDEX);
2406     JSTaggedValue cachePattern = cache->Get(index + PATTERN_INDEX);
2407     if (!cacheThis.IsString() || !cachePattern.IsString()) {
2408         return JSTaggedValue::Undefined();
2409     }
2410     JSHandle<EcmaString> cacheStringHandle(thread, cacheThis);
2411     JSHandle<EcmaString> cachePatternHandle(thread, cachePattern);
2412 
2413     if (EcmaStringAccessor::StringsAreEqual(thread->GetEcmaVM(), thisString, cacheStringHandle) &&
2414         EcmaStringAccessor::StringsAreEqual(thread->GetEcmaVM(), pattern, cachePatternHandle)) {
2415         JSHandle<TaggedArray> cacheArray(thread, cache->Get(index + ARRAY_INDEX));
2416         uint32_t arrayLength = cacheArray->GetLength();
2417         ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2418         JSHandle<TaggedArray> copyArray;
2419         if (isOneByte) {
2420             copyArray = factory->NewAndCopyTaggedArraySkipBarrier(cacheArray, arrayLength, arrayLength);
2421         } else {
2422             copyArray = factory->NewAndCopyTaggedArray(cacheArray, arrayLength, arrayLength);
2423         }
2424         return copyArray.GetTaggedValue();
2425     }
2426     return JSTaggedValue::Undefined();
2427 }
2428 
SetCachedResult(const JSThread * thread,const JSHandle<StringSplitResultCache> & cache,const JSHandle<EcmaString> & thisString,const JSHandle<EcmaString> & pattern,const JSHandle<TaggedArray> & resultArray)2429 void StringSplitResultCache::SetCachedResult(const JSThread *thread, const JSHandle<StringSplitResultCache> &cache,
2430     const JSHandle<EcmaString> &thisString, const JSHandle<EcmaString> &pattern,
2431     const JSHandle<TaggedArray> &resultArray)
2432 {
2433     // clone to cache array
2434     uint32_t arrayLength = resultArray->GetLength();
2435     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2436     JSHandle<TaggedArray> newElements(factory->NewTaggedArray(arrayLength));
2437     for (uint32_t i = 0; i < arrayLength; i++) {
2438         newElements->Set(thread, i, resultArray->Get(i));
2439     }
2440     uint32_t hash = EcmaStringAccessor(thisString).GetHashcode();
2441     uint32_t entry = hash & (CACHE_SIZE - 1);
2442     uint32_t index = entry * ENTRY_SIZE;
2443 
2444     cache->Set(thread, index + STRING_INDEX, thisString);
2445     cache->Set(thread, index + PATTERN_INDEX, pattern);
2446     cache->Set(thread, index + ARRAY_INDEX, newElements);
2447 }
2448 
CreateCacheTable(const JSThread * thread)2449 JSTaggedValue StringToListResultCache::CreateCacheTable(const JSThread *thread)
2450 {
2451     int length = CACHE_SIZE * ENTRY_SIZE;
2452     auto table = static_cast<StringToListResultCache*>(
2453         *thread->GetEcmaVM()->GetFactory()->NewTaggedArray(length, JSTaggedValue::Undefined()));
2454     return JSTaggedValue(table);
2455 }
2456 
FindCachedResult(const JSThread * thread,const JSHandle<StringToListResultCache> & cache,const JSHandle<EcmaString> & thisString)2457 JSTaggedValue StringToListResultCache::FindCachedResult(const JSThread *thread,
2458     const JSHandle<StringToListResultCache> &cache, const JSHandle<EcmaString> &thisString)
2459 {
2460     if (EcmaStringAccessor(thisString).GetLength() > MAX_STRING_LENGTH) {
2461         return JSTaggedValue::Undefined();
2462     }
2463     uint32_t hash = EcmaStringAccessor(thisString).GetHashcode();
2464     uint32_t entry = hash & (CACHE_SIZE - 1);
2465     uint32_t index = entry * ENTRY_SIZE;
2466     JSHandle<JSTaggedValue> cacheThis(thread, cache->Get(index + STRING_INDEX));
2467     if (!cacheThis->IsString()) {
2468         return JSTaggedValue::Undefined();
2469     }
2470     JSHandle<EcmaString> cacheStr(cacheThis);
2471     if (EcmaStringAccessor::StringsAreEqual(thread->GetEcmaVM(), thisString, cacheStr)) {
2472         return cache->Get(index + ARRAY_INDEX);
2473     }
2474     return JSTaggedValue::Undefined();
2475 }
2476 
SetCachedResult(const JSThread * thread,const JSHandle<StringToListResultCache> & cache,const JSHandle<EcmaString> & thisString,const JSHandle<TaggedArray> & resultArray)2477 void StringToListResultCache::SetCachedResult(const JSThread *thread, const JSHandle<StringToListResultCache> &cache,
2478     const JSHandle<EcmaString> &thisString, const JSHandle<TaggedArray> &resultArray)
2479 {
2480     if (EcmaStringAccessor(thisString).GetLength() > MAX_STRING_LENGTH ||
2481         EcmaStringAccessor(thisString).GetLength() == 0) {
2482         return;
2483     }
2484     if (!EcmaStringAccessor(thisString).IsInternString()) {
2485         return;
2486     }
2487     // clone to cache array
2488     uint32_t arrayLength = resultArray->GetLength();
2489     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2490     JSMutableHandle<TaggedArray> newElements(thread, JSTaggedValue::Undefined());
2491     if (resultArray.GetTaggedValue().IsInSharedHeap()) {
2492         newElements.Update(factory->NewSCOWTaggedArray(arrayLength));
2493     } else {
2494         newElements.Update(factory->NewCOWTaggedArray(arrayLength));
2495     }
2496     for (uint32_t i = 0; i < arrayLength; i++) {
2497         newElements->Set(thread, i, resultArray->Get(i));
2498     }
2499     uint32_t hash = EcmaStringAccessor(thisString).GetHashcode();
2500     uint32_t entry = hash & (CACHE_SIZE - 1);
2501     uint32_t index = entry * ENTRY_SIZE;
2502     cache->Set(thread, index + STRING_INDEX, thisString);
2503     cache->Set(thread, index + ARRAY_INDEX, newElements);
2504 }
2505 }  // namespace panda::ecmascript::builtins
2506