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