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