• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "ecmascript/builtins/builtins_string.h"
17 
18 #include <algorithm>
19 #include <vector>
20 #include <map>
21 
22 #include "ecmascript/intl/locale_helper.h"
23 #include "ecmascript/base/number_helper.h"
24 #include "ecmascript/base/string_helper.h"
25 #include "ecmascript/builtins/builtins_json.h"
26 #include "ecmascript/builtins/builtins_regexp.h"
27 #include "ecmascript/builtins/builtins_symbol.h"
28 #include "ecmascript/ecma_runtime_call_info.h"
29 #include "ecmascript/ecma_string-inl.h"
30 #include "ecmascript/ecma_vm.h"
31 #include "ecmascript/global_env.h"
32 #include "ecmascript/interpreter/fast_runtime_stub-inl.h"
33 #include "ecmascript/interpreter/interpreter.h"
34 #include "ecmascript/js_array.h"
35 #include "ecmascript/js_hclass.h"
36 #include "ecmascript/js_object-inl.h"
37 #include "ecmascript/js_primitive_ref.h"
38 #include "ecmascript/js_regexp.h"
39 #include "ecmascript/js_string_iterator.h"
40 #include "ecmascript/js_tagged_value-inl.h"
41 #include "ecmascript/mem/c_containers.h"
42 #include "ecmascript/object_factory.h"
43 #include "ecmascript/tagged_array-inl.h"
44 #include "ecmascript/tagged_array.h"
45 #ifdef ARK_SUPPORT_INTL
46 #include "ecmascript/js_collator.h"
47 #include "ecmascript/js_locale.h"
48 #else
49 #ifndef ARK_NOT_SUPPORT_INTL_GLOBAL
50 #include "ecmascript/intl/global_intl_helper.h"
51 #endif
52 #endif
53 
54 #include "unicode/normalizer2.h"
55 #include "unicode/normlzr.h"
56 #include "unicode/unistr.h"
57 
58 namespace panda::ecmascript::builtins {
59 using ObjectFactory = ecmascript::ObjectFactory;
60 using JSArray = ecmascript::JSArray;
61 
62 // 21.1.1.1 String(value)
StringConstructor(EcmaRuntimeCallInfo * argv)63 JSTaggedValue BuiltinsString::StringConstructor(EcmaRuntimeCallInfo *argv)
64 {
65     ASSERT(argv);
66     BUILTINS_API_TRACE(argv->GetThread(), String, Constructor);
67     JSThread *thread = argv->GetThread();
68     [[maybe_unused]] EcmaHandleScope handleScope(thread);
69     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
70     JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
71     if (argv->GetArgsNumber() > 0) {
72         JSHandle<JSTaggedValue> valTagNew = GetCallArg(argv, 0);
73         if (newTarget->IsUndefined() && valTagNew->IsSymbol()) {
74             return BuiltinsSymbol::SymbolDescriptiveString(thread, valTagNew.GetTaggedValue());
75         }
76         JSHandle<EcmaString> str = JSTaggedValue::ToString(thread, valTagNew);
77         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
78         if (newTarget->IsUndefined()) {
79             return str.GetTaggedValue();
80         }
81         JSHandle<JSTaggedValue> strTag(str);
82         return JSPrimitiveRef::StringCreate(thread, strTag, newTarget).GetTaggedValue();
83     }
84     JSHandle<EcmaString> val = factory->GetEmptyString();
85     JSHandle<JSTaggedValue> valTag(val);
86     if (newTarget->IsUndefined()) {
87         return factory->GetEmptyString().GetTaggedValue();
88     }
89     return JSPrimitiveRef::StringCreate(thread, valTag, newTarget).GetTaggedValue();
90 }
91 
92 // 21.1.2.1
FromCharCode(EcmaRuntimeCallInfo * argv)93 JSTaggedValue BuiltinsString::FromCharCode(EcmaRuntimeCallInfo *argv)
94 {
95     ASSERT(argv);
96     BUILTINS_API_TRACE(argv->GetThread(), String, FromCharCode);
97     JSThread *thread = argv->GetThread();
98     [[maybe_unused]] EcmaHandleScope handleScope(thread);
99     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
100     uint32_t argLength = argv->GetArgsNumber();
101     if (argLength == 0) {
102         return factory->GetEmptyString().GetTaggedValue();
103     }
104     if (argLength == 1) {
105         JSHandle<JSTaggedValue> codePointTag = BuiltinsString::GetCallArg(argv, 0);
106         uint16_t codePointValue = JSTaggedValue::ToUint16(thread, codePointTag);
107         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
108         JSHandle<EcmaString> strHandle = factory->NewFromUtf16Literal(&codePointValue, 1);
109         return strHandle.GetTaggedValue();
110     }
111     CVector<uint16_t> valueTable;
112     valueTable.reserve(argLength);
113     for (uint32_t i = 0; i < argLength; i++) {
114         JSHandle<JSTaggedValue> nextCp = BuiltinsString::GetCallArg(argv, i);
115         uint16_t nextCv = JSTaggedValue::ToUint16(thread, nextCp);
116         valueTable.emplace_back(nextCv);
117         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
118     }
119     std::u16string u16str = base::StringHelper::Utf16ToU16String(valueTable.data(), argLength);
120     const char16_t *constChar16tData = u16str.data();
121     auto *char16tData = const_cast<char16_t *>(constChar16tData);
122     auto *uint16tData = reinterpret_cast<uint16_t *>(char16tData);
123     uint32_t u16strSize = u16str.size();
124     return factory->NewFromUtf16Literal(uint16tData, u16strSize).GetTaggedValue();
125 }
126 
127 // 21.1.2.2
FromCodePoint(EcmaRuntimeCallInfo * argv)128 JSTaggedValue BuiltinsString::FromCodePoint(EcmaRuntimeCallInfo *argv)
129 {
130     ASSERT(argv);
131     BUILTINS_API_TRACE(argv->GetThread(), String, FromCodePoint);
132     JSThread *thread = argv->GetThread();
133     [[maybe_unused]] EcmaHandleScope handleScope(thread);
134     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
135     uint32_t argLength = argv->GetArgsNumber();
136     if (argLength == 0) {
137         return factory->GetEmptyString().GetTaggedValue();
138     }
139     std::u16string u16str;
140     uint32_t u16strSize = argLength;
141     for (uint32_t i = 0; i < argLength; i++) {
142         JSHandle<JSTaggedValue> nextCpTag = BuiltinsString::GetCallArg(argv, i);
143         JSTaggedNumber nextCpVal = JSTaggedValue::ToNumber(thread, nextCpTag);
144         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
145         if (!nextCpVal.IsInteger()) {
146             THROW_RANGE_ERROR_AND_RETURN(thread, "is not integer", JSTaggedValue::Exception());
147         }
148         int32_t cp = nextCpVal.ToInt32();
149         if (cp < 0 || cp > ENCODE_MAX_UTF16) {
150             THROW_RANGE_ERROR_AND_RETURN(thread, "CodePoint < 0 or CodePoint > 0x10FFFF", JSTaggedValue::Exception());
151         }
152         if (cp == 0) {
153             CVector<uint16_t> data {0x00};
154             return factory->NewFromUtf16Literal(data.data(), 1).GetTaggedValue();
155         }
156         if (cp > UINT16_MAX) {
157             uint16_t cu1 =
158                 std::floor((static_cast<uint32_t>(cp) - ENCODE_SECOND_FACTOR) / ENCODE_FIRST_FACTOR) + ENCODE_LEAD_LOW;
159             uint16_t cu2 =
160                 ((static_cast<uint32_t>(cp) - ENCODE_SECOND_FACTOR) % ENCODE_FIRST_FACTOR) + ENCODE_TRAIL_LOW;
161             std::u16string nextU16str1 = base::StringHelper::Utf16ToU16String(&cu1, 1);
162             std::u16string nextU16str2 = base::StringHelper::Utf16ToU16String(&cu2, 1);
163             u16str = base::StringHelper::Append(u16str, nextU16str1);
164             u16str = base::StringHelper::Append(u16str, nextU16str2);
165             u16strSize++;
166         } else {
167             auto u16tCp = static_cast<uint16_t>(cp);
168             std::u16string nextU16str = base::StringHelper::Utf16ToU16String(&u16tCp, 1);
169             u16str = base::StringHelper::Append(u16str, nextU16str);
170         }
171     }
172     const char16_t *constChar16tData = u16str.data();
173     auto *char16tData = const_cast<char16_t *>(constChar16tData);
174     auto *uint16tData = reinterpret_cast<uint16_t *>(char16tData);
175     return factory->NewFromUtf16Literal(uint16tData, u16strSize).GetTaggedValue();
176 }
177 
178 // 21.1.2.4
Raw(EcmaRuntimeCallInfo * argv)179 JSTaggedValue BuiltinsString::Raw(EcmaRuntimeCallInfo *argv)
180 {
181     ASSERT(argv);
182     BUILTINS_API_TRACE(argv->GetThread(), String, Raw);
183     JSThread *thread = argv->GetThread();
184     [[maybe_unused]] EcmaHandleScope handleScope(thread);
185     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
186     // Let cooked be ToObject(template).
187     JSHandle<JSObject> cooked = JSTaggedValue::ToObject(thread, BuiltinsString::GetCallArg(argv, 0));
188     // ReturnIfAbrupt(cooked).
189     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
190     // Let raw be ToObject(Get(cooked, "raw")).
191     JSHandle<JSTaggedValue> rawKey(factory->NewFromASCII("raw"));
192     JSHandle<JSTaggedValue> rawTag =
193         JSObject::GetProperty(thread, JSHandle<JSTaggedValue>::Cast(cooked), rawKey).GetValue();
194     JSHandle<JSObject> rawObj = JSTaggedValue::ToObject(thread, rawTag);
195     // ReturnIfAbrupt(rawObj).
196     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
197     JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
198     JSHandle<JSTaggedValue> rawLen =
199         JSObject::GetProperty(thread, JSHandle<JSTaggedValue>::Cast(rawObj), lengthKey).GetValue();
200     // ReturnIfAbrupt(rawLen).
201     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
202     JSTaggedNumber lengthNumber = JSTaggedValue::ToLength(thread, rawLen);
203     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
204     int length = static_cast<int>(lengthNumber.ToUint32());
205     if (length <= 0) {
206         return factory->GetEmptyString().GetTaggedValue();
207     }
208 
209     std::u16string u16str;
210     uint32_t argc = argv->GetArgsNumber() - 1;
211     bool canBeCompress = true;
212     for (uint32_t i = 0, argsI = 1; i < static_cast<uint32_t>(length); ++i, ++argsI) {
213         // Let nextSeg be ToString(Get(raw, nextKey)).
214         JSHandle<JSTaggedValue> elementString =
215             JSObject::GetProperty(thread, JSHandle<JSTaggedValue>::Cast(rawObj), i).GetValue();
216         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
217         EcmaString *nextSeg = *JSTaggedValue::ToString(thread, elementString);
218         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
219         u16str += EcmaStringAccessor(nextSeg).ToU16String();
220         if (EcmaStringAccessor(nextSeg).IsUtf16()) {
221             canBeCompress = false;
222         }
223         if (i + 1 == static_cast<uint32_t>(length)) {
224             break;
225         }
226         if (argsI <= argc) {
227             EcmaString *nextSub = *JSTaggedValue::ToString(thread, GetCallArg(argv, argsI));
228             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
229             u16str += EcmaStringAccessor(nextSub).ToU16String();
230             if (EcmaStringAccessor(nextSub).IsUtf16()) {
231                 canBeCompress = false;
232             }
233         }
234     }
235     // return the result string
236     auto *uint16tData = reinterpret_cast<uint16_t *>(const_cast<char16_t *>(u16str.data()));
237     return canBeCompress ? factory->NewFromUtf16LiteralCompress(uint16tData, u16str.size()).GetTaggedValue() :
238                            factory->NewFromUtf16LiteralNotCompress(uint16tData, u16str.size()).GetTaggedValue();
239 }
240 
241 // 21.1.3.1
CharAt(EcmaRuntimeCallInfo * argv)242 JSTaggedValue BuiltinsString::CharAt(EcmaRuntimeCallInfo *argv)
243 {
244     ASSERT(argv);
245     BUILTINS_API_TRACE(argv->GetThread(), String, CharAt);
246     JSThread *thread = argv->GetThread();
247     [[maybe_unused]] EcmaHandleScope handleScope(thread);
248     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
249     JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
250     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
251     JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
252     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
253     JSHandle<EcmaString> thisFlat(thread, EcmaStringAccessor::Flatten(thread->GetEcmaVM(), thisHandle));
254     int32_t thisLen = static_cast<int32_t>(EcmaStringAccessor(thisFlat).GetLength());
255     JSHandle<JSTaggedValue> posTag = BuiltinsString::GetCallArg(argv, 0);
256     int32_t pos = 0;
257     if (posTag->IsInt()) {
258         pos = posTag->GetInt();
259     } else if (posTag->IsUndefined()) {
260         pos = 0;
261     } else {
262         JSTaggedNumber posVal = JSTaggedValue::ToInteger(thread, posTag);
263         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
264         pos = posVal.ToInt32();
265     }
266     if (pos < 0 || pos >= thisLen) {
267         return factory->GetEmptyString().GetTaggedValue();
268     }
269     uint16_t res = EcmaStringAccessor(thisFlat).Get<false>(pos);
270     return factory->NewFromUtf16Literal(&res, 1).GetTaggedValue();
271 }
272 
273 // 21.1.3.2
CharCodeAt(EcmaRuntimeCallInfo * argv)274 JSTaggedValue BuiltinsString::CharCodeAt(EcmaRuntimeCallInfo *argv)
275 {
276     ASSERT(argv);
277     BUILTINS_API_TRACE(argv->GetThread(), String, CharCodeAt);
278     JSThread *thread = argv->GetThread();
279     [[maybe_unused]] EcmaHandleScope handleScope(thread);
280     JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
281     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
282     JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
283     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
284     JSHandle<EcmaString> thisFlat(thread, EcmaStringAccessor::Flatten(thread->GetEcmaVM(), thisHandle));
285     int32_t thisLen = static_cast<int32_t>(EcmaStringAccessor(thisFlat).GetLength());
286     JSHandle<JSTaggedValue> posTag = BuiltinsString::GetCallArg(argv, 0);
287     int32_t pos = 0;
288     if (posTag->IsInt()) {
289         pos = posTag->GetInt();
290     } else if (posTag->IsUndefined()) {
291         pos = 0;
292     } else {
293         JSTaggedNumber posVal = JSTaggedValue::ToInteger(thread, posTag);
294         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
295         pos = posVal.ToInt32();
296     }
297     if (pos < 0 || pos >= thisLen) {
298         return GetTaggedDouble(base::NAN_VALUE);
299     }
300     uint16_t ret = EcmaStringAccessor(thisFlat).Get<false>(pos);
301     return GetTaggedInt(ret);
302 }
303 
304 // 21.1.3.3
CodePointAt(EcmaRuntimeCallInfo * argv)305 JSTaggedValue BuiltinsString::CodePointAt(EcmaRuntimeCallInfo *argv)
306 {
307     ASSERT(argv);
308     BUILTINS_API_TRACE(argv->GetThread(), String, CodePointAt);
309     JSThread *thread = argv->GetThread();
310     [[maybe_unused]] EcmaHandleScope handleScope(thread);
311     JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
312     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
313     JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
314     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
315     JSHandle<EcmaString> thisFlat(thread, EcmaStringAccessor::Flatten(thread->GetEcmaVM(), thisHandle));
316     JSHandle<JSTaggedValue> posTag = BuiltinsString::GetCallArg(argv, 0);
317 
318     JSTaggedNumber posVal = JSTaggedValue::ToNumber(thread, posTag);
319     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
320     int32_t pos = base::NumberHelper::DoubleInRangeInt32(posVal.GetNumber());
321     int32_t thisLen = static_cast<int32_t>(EcmaStringAccessor(thisFlat).GetLength());
322     if (pos < 0 || pos >= thisLen) {
323         return JSTaggedValue::Undefined();
324     }
325     uint16_t first = EcmaStringAccessor(thisFlat).Get<false>(pos);
326     if (first < base::utf_helper::DECODE_LEAD_LOW || first > base::utf_helper::DECODE_LEAD_HIGH || pos + 1 == thisLen) {
327         return GetTaggedInt(first);
328     }
329     uint16_t second = EcmaStringAccessor(thisFlat).Get<false>(pos + 1);
330     if (second < base::utf_helper::DECODE_TRAIL_LOW || second > base::utf_helper::DECODE_TRAIL_HIGH) {
331         return GetTaggedInt(first);
332     }
333     uint32_t res = base::utf_helper::UTF16Decode(first, second);
334     return GetTaggedInt(res);
335 }
336 
337 // 21.1.3.4
Concat(EcmaRuntimeCallInfo * argv)338 JSTaggedValue BuiltinsString::Concat(EcmaRuntimeCallInfo *argv)
339 {
340     ASSERT(argv);
341     BUILTINS_API_TRACE(argv->GetThread(), String, Concat);
342     JSThread *thread = argv->GetThread();
343     [[maybe_unused]] EcmaHandleScope handleScope(thread);
344     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
345     JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
346     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
347     JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
348     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
349     uint32_t argLength = argv->GetArgsNumber();
350     if (argLength == 0) {
351         return thisHandle.GetTaggedValue();
352     }
353     std::u16string u16strThis;
354     std::u16string u16strNext;
355     bool canBeCompress = true;
356     u16strThis = EcmaStringAccessor(thisHandle).ToU16String();
357     if (EcmaStringAccessor(thisHandle).IsUtf16()) {
358         canBeCompress = false;
359     }
360     for (uint32_t i = 0; i < argLength; i++) {
361         JSHandle<JSTaggedValue> nextTag = BuiltinsString::GetCallArg(argv, i);
362         JSHandle<EcmaString> nextHandle = JSTaggedValue::ToString(thread, nextTag);
363         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
364         u16strNext = EcmaStringAccessor(nextHandle).ToU16String();
365         if (EcmaStringAccessor(nextHandle).IsUtf16()) {
366             canBeCompress = false;
367         }
368         u16strThis = base::StringHelper::Append(u16strThis, u16strNext);
369     }
370     const char16_t *constChar16tData = u16strThis.data();
371     auto *char16tData = const_cast<char16_t *>(constChar16tData);
372     auto *uint16tData = reinterpret_cast<uint16_t *>(char16tData);
373     uint32_t u16strSize = u16strThis.size();
374     return canBeCompress ? factory->NewFromUtf16LiteralCompress(uint16tData, u16strSize).GetTaggedValue() :
375                            factory->NewFromUtf16LiteralNotCompress(uint16tData, u16strSize).GetTaggedValue();
376 }
377 
378 // 21.1.3.5 String.prototype.constructor
379 // 21.1.3.6
EndsWith(EcmaRuntimeCallInfo * argv)380 JSTaggedValue BuiltinsString::EndsWith(EcmaRuntimeCallInfo *argv)
381 {
382     ASSERT(argv);
383     BUILTINS_API_TRACE(argv->GetThread(), String, EndsWith);
384     JSThread *thread = argv->GetThread();
385     [[maybe_unused]] EcmaHandleScope handleScope(thread);
386     JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
387     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
388     JSHandle<JSTaggedValue> searchTag = BuiltinsString::GetCallArg(argv, 0);
389     JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
390     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
391     bool isRegexp = JSObject::IsRegExp(thread, searchTag);
392     if (isRegexp) {
393         THROW_TYPE_ERROR_AND_RETURN(thread, "is regexp", JSTaggedValue::Exception());
394     }
395     JSHandle<EcmaString> searchHandle = JSTaggedValue::ToString(thread, searchTag);
396     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
397     uint32_t thisLen = EcmaStringAccessor(thisHandle).GetLength();
398     uint32_t searchLen = EcmaStringAccessor(searchHandle).GetLength();
399     uint32_t pos = 0;
400     JSHandle<JSTaggedValue> posTag = BuiltinsString::GetCallArg(argv, 1);
401     if (posTag->IsUndefined()) {
402         pos = thisLen;
403     } else {
404         JSTaggedNumber posVal = JSTaggedValue::ToInteger(thread, posTag);
405         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
406         pos = static_cast<uint32_t>(posVal.ToInt32());
407     }
408     uint32_t end = std::min(std::max(pos, 0U), thisLen);
409     int32_t start = static_cast<int32_t>(end - searchLen);
410     if (start < 0) {
411         return BuiltinsString::GetTaggedBoolean(false);
412     }
413 
414     int32_t idx = EcmaStringAccessor::IndexOf(thread->GetEcmaVM(), thisHandle, searchHandle, start);
415     if (idx == start) {
416         return BuiltinsString::GetTaggedBoolean(true);
417     }
418     return BuiltinsString::GetTaggedBoolean(false);
419 }
420 
421 // 21.1.3.7
Includes(EcmaRuntimeCallInfo * argv)422 JSTaggedValue BuiltinsString::Includes(EcmaRuntimeCallInfo *argv)
423 {
424     ASSERT(argv);
425     BUILTINS_API_TRACE(argv->GetThread(), String, Includes);
426     JSThread *thread = argv->GetThread();
427     [[maybe_unused]] EcmaHandleScope handleScope(thread);
428     JSHandle<JSTaggedValue> searchTag = BuiltinsString::GetCallArg(argv, 0);
429     JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
430     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
431     JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
432     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
433     bool isRegexp = JSObject::IsRegExp(thread, searchTag);
434     if (isRegexp) {
435         THROW_TYPE_ERROR_AND_RETURN(thread, "is regexp", JSTaggedValue::Exception());
436     }
437     JSHandle<EcmaString> searchHandle = JSTaggedValue::ToString(thread, searchTag);
438     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
439     uint32_t thisLen = EcmaStringAccessor(thisHandle).GetLength();
440     int32_t pos = 0;
441     JSHandle<JSTaggedValue> posTag = BuiltinsBase::GetCallArg(argv, 1);
442     if (argv->GetArgsNumber() == 1) {
443         pos = 0;
444     } else {
445         JSTaggedNumber posVal = JSTaggedValue::ToNumber(thread, posTag);
446         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
447         pos = base::NumberHelper::DoubleInRangeInt32(posVal.GetNumber());
448     }
449     int32_t start = std::min(std::max(pos, 0), static_cast<int32_t>(thisLen));
450     int32_t idx = EcmaStringAccessor::IndexOf(thread->GetEcmaVM(), thisHandle, searchHandle, start);
451     if (idx < 0 || idx > static_cast<int32_t>(thisLen)) {
452         return BuiltinsString::GetTaggedBoolean(false);
453     }
454     return BuiltinsString::GetTaggedBoolean(true);
455 }
456 
457 // 21.1.3.8
IndexOf(EcmaRuntimeCallInfo * argv)458 JSTaggedValue BuiltinsString::IndexOf(EcmaRuntimeCallInfo *argv)
459 {
460     ASSERT(argv);
461     BUILTINS_API_TRACE(argv->GetThread(), String, IndexOf);
462     JSThread *thread = argv->GetThread();
463     [[maybe_unused]] EcmaHandleScope handleScope(thread);
464     JSHandle<JSTaggedValue> searchTag = BuiltinsString::GetCallArg(argv, 0);
465     JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
466     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
467     JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
468     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
469     uint32_t thisLen = EcmaStringAccessor(thisHandle).GetLength();
470     JSHandle<EcmaString> searchHandle = JSTaggedValue::ToString(thread, searchTag);
471     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
472     JSHandle<JSTaggedValue> posTag = BuiltinsString::GetCallArg(argv, 1);
473     int32_t pos = 0;
474     if (posTag->IsInt()) {
475         pos = posTag->GetInt();
476     } else if (posTag->IsUndefined()) {
477         pos = 0;
478     } else {
479         JSTaggedNumber posVal = JSTaggedValue::ToInteger(thread, posTag);
480         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
481         pos = posVal.ToInt32();
482     }
483     pos = std::min(std::max(pos, 0), static_cast<int32_t>(thisLen));
484     int32_t res = EcmaStringAccessor::IndexOf(thread->GetEcmaVM(), thisHandle, searchHandle, pos);
485     if (res >= 0 && res < static_cast<int32_t>(thisLen)) {
486         return GetTaggedInt(res);
487     }
488     return GetTaggedInt(-1);
489 }
490 
491 // 21.1.3.9
LastIndexOf(EcmaRuntimeCallInfo * argv)492 JSTaggedValue BuiltinsString::LastIndexOf(EcmaRuntimeCallInfo *argv)
493 {
494     ASSERT(argv);
495     BUILTINS_API_TRACE(argv->GetThread(), String, LastIndexOf);
496     JSThread *thread = argv->GetThread();
497     [[maybe_unused]] EcmaHandleScope handleScope(thread);
498     JSHandle<JSTaggedValue> searchTag = BuiltinsString::GetCallArg(argv, 0);
499     JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
500     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
501     JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
502     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
503     int32_t thisLen = static_cast<int32_t>(EcmaStringAccessor(thisHandle).GetLength());
504     JSHandle<EcmaString> searchHandle = JSTaggedValue::ToString(thread, searchTag);
505     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
506     int32_t pos = 0;
507     if (argv->GetArgsNumber() == 1) {
508         pos = thisLen;
509     } else {
510         JSHandle<JSTaggedValue> posTag = BuiltinsString::GetCallArg(argv, 1);
511         JSTaggedNumber posVal = JSTaggedValue::ToInteger(thread, posTag);
512         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
513         if (std::isnan(JSTaggedValue::ToNumber(thread, posTag).GetNumber())) {
514             pos = thisLen;
515         } else {
516             pos = posVal.ToInt32();
517         }
518     }
519     pos = std::min(std::max(pos, 0), thisLen);
520     int32_t res = EcmaStringAccessor::LastIndexOf(thread->GetEcmaVM(), thisHandle, searchHandle, pos);
521     if (res >= 0 && res < thisLen) {
522         return GetTaggedInt(res);
523     }
524     res = -1;
525     return GetTaggedInt(static_cast<int32_t>(res));
526 }
527 
528 // 21.1.3.10
LocaleCompare(EcmaRuntimeCallInfo * argv)529 JSTaggedValue BuiltinsString::LocaleCompare(EcmaRuntimeCallInfo *argv)
530 {
531     ASSERT(argv);
532     BUILTINS_API_TRACE(argv->GetThread(), String, LocaleCompare);
533     JSThread *thread = argv->GetThread();
534     [[maybe_unused]] EcmaHandleScope handleScope(thread);
535     JSHandle<JSTaggedValue> thatTag = BuiltinsString::GetCallArg(argv, 0);
536     JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
537     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
538     [[maybe_unused]] JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
539     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
540     [[maybe_unused]] JSHandle<EcmaString> thatHandle = JSTaggedValue::ToString(thread, thatTag);
541     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
542 
543     JSHandle<JSTaggedValue> locales = GetCallArg(argv, 1);
544     JSHandle<JSTaggedValue> options = GetCallArg(argv, 2); // 2: the second argument
545     [[maybe_unused]] bool cacheable = (locales->IsUndefined() || locales->IsString()) && options->IsUndefined();
546 #ifdef ARK_SUPPORT_INTL
547     if (cacheable) {
548         auto collator = JSCollator::GetCachedIcuCollator(thread, locales);
549         if (collator != nullptr) {
550             JSTaggedValue result = JSCollator::CompareStrings(collator, thisHandle, thatHandle);
551             return result;
552         }
553     }
554     EcmaVM *ecmaVm = thread->GetEcmaVM();
555     ObjectFactory *factory = ecmaVm->GetFactory();
556     JSHandle<JSTaggedValue> ctor = ecmaVm->GetGlobalEnv()->GetCollatorFunction();
557     JSHandle<JSCollator> collator =
558         JSHandle<JSCollator>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(ctor)));
559     JSHandle<JSCollator> initCollator =
560         JSCollator::InitializeCollator(thread, collator, locales, options, cacheable);
561     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
562     icu::Collator *icuCollator = nullptr;
563     if (cacheable) {
564         icuCollator = JSCollator::GetCachedIcuCollator(thread, locales);
565         ASSERT(icuCollator != nullptr);
566     } else {
567         icuCollator = initCollator->GetIcuCollator();
568     }
569     JSTaggedValue result = JSCollator::CompareStrings(icuCollator, thisHandle, thatHandle);
570     return result;
571 #else
572 #ifdef ARK_NOT_SUPPORT_INTL_GLOBAL
573     ARK_SUPPORT_INTL_RETURN_JSVALUE(thread, "LocaleCompare");
574 #else
575     intl::GlobalIntlHelper gh(thread, intl::GlobalFormatterType::Collator);
576     auto collator = gh.GetGlobalObject<intl::GlobalCollator>(thread,
577         locales, options, intl::GlobalFormatterType::Collator, cacheable);
578     if (collator == nullptr) {
579         LOG_ECMA(ERROR) << "BuiltinsString::LocaleCompare:collator is nullptr";
580     }
581     ASSERT(collator != nullptr);
582     auto result = collator->Compare(EcmaStringAccessor(thisHandle).ToStdString(),
583         EcmaStringAccessor(thatHandle).ToStdString());
584     return JSTaggedValue(result);
585 #endif
586 #endif
587 }
588 
589 // 21.1.3.11
Match(EcmaRuntimeCallInfo * argv)590 JSTaggedValue BuiltinsString::Match(EcmaRuntimeCallInfo *argv)
591 {
592     ASSERT(argv);
593     BUILTINS_API_TRACE(argv->GetThread(), String, Match);
594     JSThread *thread = argv->GetThread();
595     [[maybe_unused]] EcmaHandleScope handleScope(thread);
596     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
597     JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
598     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
599     JSHandle<JSTaggedValue> regexp = BuiltinsString::GetCallArg(argv, 0);
600     JSHandle<JSTaggedValue> matchTag = thread->GetEcmaVM()->GetGlobalEnv()->GetMatchSymbol();
601     JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
602     if (regexp->IsJSRegExp()) {
603         JSHandle<RegExpExecResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetRegExpCache());
604         JSHandle<JSRegExp> re(regexp);
605         JSHandle<JSTaggedValue> pattern(thread, re->GetOriginalSource());
606         JSHandle<JSTaggedValue> flags(thread, re->GetOriginalFlags());
607         JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, pattern, flags, thisTag,
608                                                                  RegExpExecResultCache::MATCH_TYPE, regexp);
609         if (!cacheResult.IsUndefined()) {
610             return cacheResult;
611         }
612     }
613     if (!regexp->IsUndefined() && !regexp->IsNull()) {
614         if (regexp->IsECMAObject()) {
615             JSHandle<JSTaggedValue> matcher = JSObject::GetMethod(thread, regexp, matchTag);
616             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
617             if (!matcher->IsUndefined()) {
618                 ASSERT(matcher->IsJSFunction());
619                 EcmaRuntimeCallInfo *info =
620                     EcmaInterpreter::NewRuntimeCallInfo(thread, matcher, regexp, undefined, 1);
621                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
622                 info->SetCallArg(thisTag.GetTaggedValue());
623                 return JSFunction::Call(info);
624             }
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     JSHandle<JSTaggedValue> matchAllTag = thread->GetEcmaVM()->GetGlobalEnv()->GetMatchAllSymbol();
650     JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
651     auto ecmaVm = thread->GetEcmaVM();
652     ObjectFactory *factory = ecmaVm->GetFactory();
653     JSHandle<JSTaggedValue> gvalue(factory->NewFromASCII("g"));
654 
655     // 2. If regexp is neither undefined nor null, then
656     if (!regexp->IsUndefined() && !regexp->IsNull()) {
657         // a. Let isRegExp be ? IsRegExp(searchValue).
658         bool isJSRegExp = JSObject::IsRegExp(thread, regexp);
659         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
660         // b. If isRegExp is true, then
661         if (isJSRegExp) {
662             // i. Let flags be ? Get(searchValue, "flags").
663             JSHandle<JSTaggedValue> flagsString(globalConst->GetHandledFlagsString());
664             JSHandle<JSTaggedValue> flags = JSObject::GetProperty(thread, regexp, flagsString).GetValue();
665             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
666             // ii. Perform ? RequireObjectCoercible(flags).
667             JSTaggedValue::RequireObjectCoercible(thread, flags);
668             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
669             // iii. If ? ToString(flags) does not contain "g", throw a TypeError exception.
670             JSHandle<EcmaString> flagString = JSTaggedValue::ToString(thread, flags);
671             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
672             int32_t pos = EcmaStringAccessor::IndexOf(ecmaVm,
673                 flagString, JSHandle<EcmaString>(gvalue));
674             if (pos == -1) {
675                 THROW_TYPE_ERROR_AND_RETURN(thread,
676                                             "matchAll called with a non-global RegExp argument",
677                                             JSTaggedValue::Exception());
678             }
679         }
680 
681         if (regexp->IsECMAObject()) {
682             // c. c. Let matcher be ? GetMethod(regexp, @@matchAll).
683             // d. d. If matcher is not undefined, then
684             JSHandle<JSTaggedValue> matcher = JSObject::GetMethod(thread, regexp, matchAllTag);
685             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
686             if (!matcher->IsUndefined()) {
687                 ASSERT(matcher->IsJSFunction());
688                 // i. i. Return ? Call(matcher, regexp, « O »).
689                 EcmaRuntimeCallInfo *info =
690                     EcmaInterpreter::NewRuntimeCallInfo(thread, matcher, regexp, undefined, 1);
691                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
692                 info->SetCallArg(thisTag.GetTaggedValue());
693                 return JSFunction::Call(info);
694             }
695         }
696     }
697     // 3. Let S be ? ToString(O).
698     JSHandle<EcmaString> thisVal = JSTaggedValue::ToString(thread, thisTag);
699     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
700     // 4. Let rx be ? RegExpCreate(regexp, "g").
701     JSHandle<JSTaggedValue> rx(thread, BuiltinsRegExp::RegExpCreate(thread, regexp, gvalue));
702     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
703     EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, rx, undefined, 1);
704     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
705     info->SetCallArg(thisVal.GetTaggedValue());
706     return JSFunction::Invoke(info, matchAllTag);
707 }
708 
709 // 21.1.3.12
Normalize(EcmaRuntimeCallInfo * argv)710 JSTaggedValue BuiltinsString::Normalize(EcmaRuntimeCallInfo *argv)
711 {
712     ASSERT(argv);
713     BUILTINS_API_TRACE(argv->GetThread(), String, Normalize);
714     JSThread *thread = argv->GetThread();
715     [[maybe_unused]] EcmaHandleScope handleScope(thread);
716     auto vm = thread->GetEcmaVM();
717     ObjectFactory *factory = vm->GetFactory();
718     JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
719     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
720     JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
721     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
722     JSHandle<EcmaString> formValue;
723     if (argv->GetArgsNumber() == 0) {
724         formValue = JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledNfcString());
725     } else {
726         JSHandle<JSTaggedValue> formTag = BuiltinsString::GetCallArg(argv, 0);
727         if (formTag->IsUndefined()) {
728             formValue = JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledNfcString());
729         } else {
730             formValue = JSTaggedValue::ToString(thread, formTag);
731             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
732         }
733     }
734     JSHandle<EcmaString> nfc = JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledNfcString());
735     JSHandle<EcmaString> nfd = factory->NewFromASCII("NFD");
736     JSHandle<EcmaString> nfkc = factory->NewFromASCII("NFKC");
737     JSHandle<EcmaString> nfkd = factory->NewFromASCII("NFKD");
738     UNormalizationMode uForm;
739     if (EcmaStringAccessor::StringsAreEqual(vm, formValue, nfc)) {
740         uForm = UNORM_NFC;
741     } else if (EcmaStringAccessor::StringsAreEqual(vm, formValue, nfd)) {
742         uForm = UNORM_NFD;
743     } else if (EcmaStringAccessor::StringsAreEqual(vm, formValue, nfkc)) {
744         uForm = UNORM_NFKC;
745     } else if (EcmaStringAccessor::StringsAreEqual(vm, formValue, nfkd)) {
746         uForm = UNORM_NFKD;
747     } else {
748         THROW_RANGE_ERROR_AND_RETURN(thread, "compare not equal", JSTaggedValue::Exception());
749     }
750 
751     std::u16string u16strThis = EcmaStringAccessor(thisHandle).ToU16String();
752     const char16_t *constChar16tData = u16strThis.data();
753     icu::UnicodeString src(constChar16tData);
754     icu::UnicodeString res;
755     UErrorCode errorCode = U_ZERO_ERROR;
756     int32_t option = 0;
757 
758     icu::Normalizer::normalize(src, uForm, option, res, errorCode);
759     JSHandle<EcmaString> str = intl::LocaleHelper::UStringToString(thread, res);
760     return JSTaggedValue(*str);
761 }
762 
PadStart(EcmaRuntimeCallInfo * argv)763 JSTaggedValue BuiltinsString::PadStart(EcmaRuntimeCallInfo *argv)
764 {
765     ASSERT(argv);
766     BUILTINS_API_TRACE(argv->GetThread(), String, PadStart);
767     return BuiltinsString::Pad(argv, true);
768 }
769 
PadEnd(EcmaRuntimeCallInfo * argv)770 JSTaggedValue BuiltinsString::PadEnd(EcmaRuntimeCallInfo *argv)
771 {
772     ASSERT(argv);
773     BUILTINS_API_TRACE(argv->GetThread(), String, PadEnd);
774     return BuiltinsString::Pad(argv, false);
775 }
776 
777 // 21.1.3.13
Repeat(EcmaRuntimeCallInfo * argv)778 JSTaggedValue BuiltinsString::Repeat(EcmaRuntimeCallInfo *argv)
779 {
780     ASSERT(argv);
781     BUILTINS_API_TRACE(argv->GetThread(), String, Repeat);
782     JSThread *thread = argv->GetThread();
783     [[maybe_unused]] EcmaHandleScope handleScope(thread);
784     JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
785     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
786     JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
787     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
788     uint32_t thisLen = EcmaStringAccessor(thisHandle).GetLength();
789     JSHandle<JSTaggedValue> countTag = BuiltinsString::GetCallArg(argv, 0);
790     int32_t count = 0;
791     if (countTag->IsInt()) {
792         count = countTag->GetInt();
793     } else {
794         JSTaggedNumber num = JSTaggedValue::ToInteger(thread, countTag);
795         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
796         double d = num.GetNumber();
797         if (d == base::POSITIVE_INFINITY) {
798             THROW_RANGE_ERROR_AND_RETURN(thread, "is infinity", JSTaggedValue::Exception());
799         }
800         count = base::NumberHelper::DoubleInRangeInt32(d);
801     }
802     if (count < 0) {
803         THROW_RANGE_ERROR_AND_RETURN(thread, "less than 0", JSTaggedValue::Exception());
804     }
805     if (count == 0) {
806         auto emptyStr = thread->GetEcmaVM()->GetFactory()->GetEmptyString();
807         return emptyStr.GetTaggedValue();
808     }
809     if (thisLen == 0) {
810         return thisHandle.GetTaggedValue();
811     }
812 
813     bool isUtf8 = EcmaStringAccessor(thisHandle).IsUtf8();
814     EcmaString *result = EcmaStringAccessor::CreateLineString(thread->GetEcmaVM(), thisLen * count, isUtf8);
815     for (uint32_t index = 0; index < static_cast<uint32_t>(count); ++index) {
816         EcmaStringAccessor::ReadData(result, *thisHandle, index * thisLen, (count - index) * thisLen, thisLen);
817     }
818     return JSTaggedValue(result);
819 }
820 
821 // 21.1.3.14
Replace(EcmaRuntimeCallInfo * argv)822 JSTaggedValue BuiltinsString::Replace(EcmaRuntimeCallInfo *argv)
823 {
824     ASSERT(argv);
825     BUILTINS_API_TRACE(argv->GetThread(), String, Replace);
826     JSThread *thread = argv->GetThread();
827     [[maybe_unused]] EcmaHandleScope handleScope(thread);
828     JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, BuiltinsString::GetThis(argv));
829     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
830 
831     auto ecmaVm = thread->GetEcmaVM();
832     JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
833     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
834     JSHandle<JSTaggedValue> searchTag = BuiltinsString::GetCallArg(argv, 0);
835     JSHandle<JSTaggedValue> replaceTag = BuiltinsString::GetCallArg(argv, 1);
836 
837     ObjectFactory *factory = ecmaVm->GetFactory();
838 
839     if (searchTag->IsJSRegExp() && replaceTag->IsString()) {
840         JSHandle<RegExpExecResultCache> cacheTable(thread->GetCurrentEcmaContext()->GetRegExpCache());
841         JSHandle<JSRegExp> re(searchTag);
842         JSHandle<JSTaggedValue> pattern(thread, re->GetOriginalSource());
843         JSHandle<JSTaggedValue> flags(thread, re->GetOriginalFlags());
844         JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, pattern, flags, thisTag,
845                                                                  RegExpExecResultCache::REPLACE_TYPE, searchTag,
846                                                                  replaceTag.GetTaggedValue());
847         if (!cacheResult.IsUndefined()) {
848             return cacheResult;
849         }
850     }
851 
852     // If searchValue is neither undefined nor null, then
853     if (searchTag->IsECMAObject()) {
854         JSHandle<JSTaggedValue> replaceKey = env->GetReplaceSymbol();
855         // Let replacer be GetMethod(searchValue, @@replace).
856         JSHandle<JSTaggedValue> replaceMethod = JSObject::GetMethod(thread, searchTag, replaceKey);
857         // ReturnIfAbrupt(replacer).
858         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
859         // If replacer is not undefined, then
860         if (!replaceMethod->IsUndefined()) {
861             // Return Call(replacer, searchValue, «O, replaceValue»).
862             const uint32_t argsLength = 2;
863             JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
864             EcmaRuntimeCallInfo *info =
865                 EcmaInterpreter::NewRuntimeCallInfo(thread, replaceMethod, searchTag, undefined, argsLength);
866             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
867             info->SetCallArg(thisTag.GetTaggedValue(), replaceTag.GetTaggedValue());
868             return JSFunction::Call(info);
869         }
870     }
871 
872     // Let string be ToString(O).
873     JSHandle<EcmaString> thisString = JSTaggedValue::ToString(thread, thisTag);
874     // ReturnIfAbrupt(string).
875     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
876     // Let searchString be ToString(searchValue).
877     JSHandle<EcmaString> searchString = JSTaggedValue::ToString(thread, searchTag);
878     // ReturnIfAbrupt(searchString).
879     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
880     // Let functionalReplace be IsCallable(replaceValue).
881     if (!replaceTag->IsCallable()) {
882         // If functionalReplace is false, then
883         // Let replaceValue be ToString(replaceValue).
884         // ReturnIfAbrupt(replaceValue)
885         replaceTag = JSHandle<JSTaggedValue>(JSTaggedValue::ToString(thread, replaceTag));
886         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
887     }
888     // Search string for the first occurrence of searchString and let pos be the index within string of the first code
889     // unit of the matched substring and let matched be searchString. If no occurrences of searchString were found,
890     // return string.
891     int32_t pos = EcmaStringAccessor::IndexOf(ecmaVm, thisString, searchString);
892     if (pos == -1) {
893         return thisString.GetTaggedValue();
894     }
895     JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
896     JSMutableHandle<JSTaggedValue> replHandle(thread, factory->GetEmptyString().GetTaggedValue());
897     // If functionalReplace is true, then
898     if (replaceTag->IsCallable()) {
899         // Let replValue be Call(replaceValue, undefined,«matched, pos, and string»).
900         const uint32_t argsLength = 3; // 3: «matched, pos, and string»
901         EcmaRuntimeCallInfo *info =
902             EcmaInterpreter::NewRuntimeCallInfo(thread, replaceTag, undefined, undefined, argsLength);
903         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
904         info->SetCallArg(searchString.GetTaggedValue(), JSTaggedValue(pos), thisString.GetTaggedValue());
905         JSTaggedValue replStrDeocodeValue = JSFunction::Call(info);
906         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
907         replHandle.Update(replStrDeocodeValue);
908     } else {
909         // Let captures be an empty List.
910         JSHandle<TaggedArray> capturesList = factory->EmptyArray();
911         ASSERT_PRINT(replaceTag->IsString(), "replace must be string");
912         JSHandle<EcmaString> replacement(thread, replaceTag->GetTaggedObject());
913         // Let replStr be GetSubstitution(matched, string, pos, captures, replaceValue)
914         replHandle.Update(GetSubstitution(thread, searchString, thisString, pos, capturesList, undefined, replacement));
915     }
916     JSHandle<EcmaString> realReplaceStr = JSTaggedValue::ToString(thread, replHandle);
917     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
918     // Let tailPos be pos + the number of code units in matched.
919     int32_t tailPos = pos + static_cast<int32_t>(EcmaStringAccessor(searchString).GetLength());
920     // Let newString be the String formed by concatenating the first pos code units of string,
921     // replStr, and the trailing
922     // substring of string starting at index tailPos. If pos is 0,
923     // the first element of the concatenation will be the
924     // empty String.
925     // Return newString.
926     JSHandle<EcmaString> prefixString(thread, EcmaStringAccessor::FastSubString(ecmaVm, thisString, 0, pos));
927     auto thisLen = EcmaStringAccessor(thisString).GetLength();
928     JSHandle<EcmaString> suffixString(thread,
929         EcmaStringAccessor::FastSubString(ecmaVm, thisString, tailPos, thisLen - tailPos));
930     EcmaString *tempStr = EcmaStringAccessor::Concat(ecmaVm, prefixString, realReplaceStr);
931     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
932     JSHandle<EcmaString> tempString(thread, tempStr);
933     EcmaString *resultStr = EcmaStringAccessor::Concat(ecmaVm, tempString, suffixString);
934     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
935     return JSTaggedValue(resultStr);
936 }
937 
ReplaceAll(EcmaRuntimeCallInfo * argv)938 JSTaggedValue BuiltinsString::ReplaceAll(EcmaRuntimeCallInfo *argv)
939 {
940     ASSERT(argv);
941     JSThread *thread = argv->GetThread();
942     BUILTINS_API_TRACE(thread, String, ReplaceAll);
943     [[maybe_unused]] EcmaHandleScope handleScope(thread);
944     JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, BuiltinsString::GetThis(argv));
945     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
946 
947     auto ecmaVm = thread->GetEcmaVM();
948     JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
949     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
950     JSHandle<JSTaggedValue> searchTag = BuiltinsString::GetCallArg(argv, 0);
951     JSHandle<JSTaggedValue> replaceTag = BuiltinsString::GetCallArg(argv, 1);
952 
953     ObjectFactory *factory = ecmaVm->GetFactory();
954 
955     if (!searchTag->IsUndefined() && !searchTag->IsNull()) {
956         // a. Let isRegExp be ? IsRegExp(searchValue).
957         bool isJSRegExp = JSObject::IsRegExp(thread, searchTag);
958         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
959         // b. If isRegExp is true, then
960         if (isJSRegExp) {
961             // i. Let flags be ? Get(searchValue, "flags").
962             JSHandle<JSTaggedValue> flagsString(globalConst->GetHandledFlagsString());
963             JSHandle<JSTaggedValue> flags = JSObject::GetProperty(thread, searchTag, flagsString).GetValue();
964             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
965             // ii. Perform ? RequireObjectCoercible(flags).
966             JSTaggedValue::RequireObjectCoercible(thread, flags);
967             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
968             // iii. If ? ToString(flags) does not contain "g", throw a TypeError exception.
969             JSHandle<EcmaString> flagString = JSTaggedValue::ToString(thread, flags);
970             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
971             JSHandle<EcmaString> gString(globalConst->GetHandledGString());
972             int32_t pos = EcmaStringAccessor::IndexOf(ecmaVm, flagString, gString);
973             if (pos == -1) {
974                 THROW_TYPE_ERROR_AND_RETURN(thread,
975                                             "string.prototype.replaceAll called with a non-global RegExp argument",
976                                             JSTaggedValue::Exception());
977             }
978         }
979         // c. Let replacer be ? GetMethod(searchValue, @@replace).
980         JSHandle<JSTaggedValue> replaceKey = env->GetReplaceSymbol();
981         JSHandle<JSTaggedValue> replaceMethod = JSObject::GetMethod(thread, searchTag, replaceKey);
982         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
983         // d. If replacer is not undefined, then
984         if (!replaceMethod->IsUndefined()) {
985             // i. Return ? Call(replacer, searchValue, «O, replaceValue»).
986             const size_t argsLength = 2;
987             JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
988             EcmaRuntimeCallInfo *info =
989                 EcmaInterpreter::NewRuntimeCallInfo(thread, replaceMethod, searchTag, undefined, argsLength);
990             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
991             info->SetCallArg(thisTag.GetTaggedValue(), replaceTag.GetTaggedValue());
992             return JSFunction::Call(info);
993         }
994     }
995 
996     // 3. Let string be ? ToString(O).
997     JSHandle<EcmaString> thisString = JSTaggedValue::ToString(thread, thisTag);
998     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
999     // 4. Let searchString be ? ToString(searchValue).
1000     JSHandle<EcmaString> searchString = JSTaggedValue::ToString(thread, searchTag);
1001     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1002     // 5. Let functionalReplace be IsCallable(replaceValue).
1003     // 6. If functionalReplace is false, then
1004     if (!replaceTag->IsCallable()) {
1005         // a. Set replaceValue to ? ToString(replaceValue).
1006         replaceTag = JSHandle<JSTaggedValue>(JSTaggedValue::ToString(thread, replaceTag));
1007         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1008     }
1009 
1010     // 7. Let searchLength be the length of searchString.
1011     // 8. Let advanceBy be max(1, searchLength).
1012     int32_t searchLength = static_cast<int32_t>(EcmaStringAccessor(searchString).GetLength());
1013     int32_t advanceBy = std::max(1, searchLength);
1014     // 9. Let matchPositions be a new empty List.
1015     std::u16string stringBuilder;
1016     std::u16string stringPrefixString;
1017     std::u16string stringRealReplaceStr;
1018     std::u16string stringSuffixString;
1019     // 10. Let position be ! StringIndexOf(string, searchString, 0).
1020     int32_t pos = EcmaStringAccessor::IndexOf(ecmaVm, thisString, searchString);
1021     int32_t endOfLastMatch = 0;
1022     bool canBeCompress = true;
1023     JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
1024     JSMutableHandle<JSTaggedValue> replHandle(thread, factory->GetEmptyString().GetTaggedValue());
1025     while (pos != -1) {
1026         // If functionalReplace is true, then
1027         if (replaceTag->IsCallable()) {
1028             // Let replValue be Call(replaceValue, undefined,«matched, pos, and string»).
1029             const uint32_t argsLength = 3; // 3: «matched, pos, and string»
1030             EcmaRuntimeCallInfo *info =
1031                 EcmaInterpreter::NewRuntimeCallInfo(thread, replaceTag, undefined, undefined, argsLength);
1032             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1033             info->SetCallArg(searchString.GetTaggedValue(), JSTaggedValue(pos), thisString.GetTaggedValue());
1034             JSTaggedValue replStrDeocodeValue = JSFunction::Call(info);
1035             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1036             replHandle.Update(replStrDeocodeValue);
1037         } else {
1038             // Let captures be an empty List.
1039             JSHandle<TaggedArray> capturesList = factory->NewTaggedArray(0);
1040             ASSERT_PRINT(replaceTag->IsString(), "replace must be string");
1041             JSHandle<EcmaString> replacement(thread, replaceTag->GetTaggedObject());
1042             // Let replStr be GetSubstitution(matched, string, pos, captures, replaceValue)
1043             replHandle.Update(GetSubstitution(thread, searchString, thisString, pos,
1044                                               capturesList, undefined, replacement));
1045         }
1046         JSHandle<EcmaString> realReplaceStr = JSTaggedValue::ToString(thread, replHandle);
1047         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1048         // Let tailPos be pos + the number of code units in matched.
1049         // Let newString be the String formed by concatenating the first pos code units of string,
1050         // replStr, and the trailing substring of string starting at index tailPos.
1051         // If pos is 0, the first element of the concatenation will be the
1052         // empty String.
1053         // Return newString.
1054         JSHandle<EcmaString> prefixString(thread,
1055                                           EcmaStringAccessor::FastSubString(ecmaVm, thisString, endOfLastMatch,
1056                                                                             pos - endOfLastMatch));
1057         stringPrefixString = EcmaStringAccessor(prefixString).ToU16String();
1058         if (EcmaStringAccessor(prefixString).IsUtf16()) {
1059             canBeCompress = false;
1060         }
1061         stringRealReplaceStr = EcmaStringAccessor(realReplaceStr).ToU16String();
1062         if (EcmaStringAccessor(realReplaceStr).IsUtf16()) {
1063             canBeCompress = false;
1064         }
1065         stringBuilder = stringBuilder + stringPrefixString + stringRealReplaceStr;
1066         endOfLastMatch = pos + searchLength;
1067         pos = EcmaStringAccessor::IndexOf(ecmaVm, thisString, searchString, pos + advanceBy);
1068     }
1069 
1070     if (endOfLastMatch < static_cast<int32_t>(EcmaStringAccessor(thisString).GetLength())) {
1071         auto thisLen = EcmaStringAccessor(thisString).GetLength();
1072         JSHandle<EcmaString> suffixString(thread,
1073             EcmaStringAccessor::FastSubString(ecmaVm, thisString, endOfLastMatch, thisLen - endOfLastMatch));
1074         stringSuffixString = EcmaStringAccessor(suffixString).ToU16String();
1075         if (EcmaStringAccessor(suffixString).IsUtf16()) {
1076             canBeCompress = false;
1077         }
1078         stringBuilder = stringBuilder + stringSuffixString;
1079     }
1080 
1081     auto *char16tData = const_cast<char16_t *>(stringBuilder.c_str());
1082     auto *uint16tData = reinterpret_cast<uint16_t *>(char16tData);
1083     return canBeCompress ?
1084         factory->NewFromUtf16LiteralCompress(uint16tData, stringBuilder.length()).GetTaggedValue() :
1085         factory->NewFromUtf16LiteralNotCompress(uint16tData, stringBuilder.length()).GetTaggedValue();
1086 }
1087 
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)1088 JSTaggedValue BuiltinsString::GetSubstitution(JSThread *thread, const JSHandle<EcmaString> &matched,
1089                                               const JSHandle<EcmaString> &srcString, int position,
1090                                               const JSHandle<TaggedArray> &captureList,
1091                                               const JSHandle<JSTaggedValue> &namedCaptures,
1092                                               const JSHandle<EcmaString> &replacement)
1093 {
1094     BUILTINS_API_TRACE(thread, String, GetSubstitution);
1095     auto ecmaVm = thread->GetEcmaVM();
1096     ObjectFactory *factory = ecmaVm->GetFactory();
1097     JSHandle<EcmaString> dollarString = JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledDollarString());
1098     JSHandle<EcmaString> replacementFlat(thread, EcmaStringAccessor::Flatten(ecmaVm, replacement));
1099     int32_t replaceLength = static_cast<int32_t>(EcmaStringAccessor(replacementFlat).GetLength());
1100     int32_t tailPos = position + static_cast<int32_t>(EcmaStringAccessor(matched).GetLength());
1101 
1102     int32_t nextDollarIndex = EcmaStringAccessor::IndexOf(ecmaVm, replacementFlat, dollarString);
1103     if (nextDollarIndex < 0) {
1104         return replacementFlat.GetTaggedValue();
1105     }
1106     std::u16string stringBuilder;
1107     bool canBeCompress = true;
1108     if (nextDollarIndex > 0) {
1109         stringBuilder = EcmaStringAccessor(replacementFlat).ToU16String(nextDollarIndex);
1110         if (EcmaStringAccessor(replacementFlat).IsUtf16()) {
1111             canBeCompress = false;
1112         }
1113     }
1114 
1115     while (true) {
1116         int peekIndex = nextDollarIndex + 1;
1117         if (peekIndex >= replaceLength) {
1118             stringBuilder += '$';
1119             auto *char16tData = const_cast<char16_t *>(stringBuilder.c_str());
1120             auto *uint16tData = reinterpret_cast<uint16_t *>(char16tData);
1121             return canBeCompress ?
1122                    factory->NewFromUtf16LiteralCompress(uint16tData, stringBuilder.length()).GetTaggedValue() :
1123                    factory->NewFromUtf16LiteralNotCompress(uint16tData, stringBuilder.length()).GetTaggedValue();
1124         }
1125         int continueFromIndex = -1;
1126         uint16_t peek = EcmaStringAccessor(replacementFlat).Get(peekIndex);
1127         switch (peek) {
1128             case '$':  // $$
1129                 stringBuilder += '$';
1130                 continueFromIndex = peekIndex + 1;
1131                 break;
1132             case '&':  // $& - match
1133                 stringBuilder += EcmaStringAccessor(matched).ToU16String();
1134                 if (EcmaStringAccessor(matched).IsUtf16()) {
1135                     canBeCompress = false;
1136                 }
1137                 continueFromIndex = peekIndex + 1;
1138                 break;
1139             case '`':  // $` - prefix
1140                 if (position > 0) {
1141                     EcmaString *prefix = EcmaStringAccessor::FastSubString(ecmaVm, srcString, 0, position);
1142                     stringBuilder += EcmaStringAccessor(prefix).ToU16String();
1143                     if (EcmaStringAccessor(prefix).IsUtf16()) {
1144                         canBeCompress = false;
1145                     }
1146                 }
1147                 continueFromIndex = peekIndex + 1;
1148                 break;
1149             case '\'': {
1150                 // $' - suffix
1151                 int32_t srcLength = static_cast<int32_t>(EcmaStringAccessor(srcString).GetLength());
1152                 if (tailPos < srcLength) {
1153                     EcmaString *sufffix = EcmaStringAccessor::FastSubString(
1154                         ecmaVm, srcString, tailPos, srcLength - tailPos);
1155                     stringBuilder += EcmaStringAccessor(sufffix).ToU16String();
1156                     if (EcmaStringAccessor(sufffix).IsUtf16()) {
1157                         canBeCompress = false;
1158                     }
1159                 }
1160                 continueFromIndex = peekIndex + 1;
1161                 break;
1162             }
1163             case '0':
1164             case '1':
1165             case '2':
1166             case '3':
1167             case '4':
1168             case '5':
1169             case '6':
1170             case '7':
1171             case '8':
1172             case '9': {
1173                 uint32_t capturesLength = captureList->GetLength();
1174                 // Valid indices are $1 .. $9, $01 .. $09 and $10 .. $99
1175                 uint32_t scaledIndex = peek - '0';
1176                 int32_t advance = 1;
1177                 if (peekIndex + 1 < replaceLength) {
1178                     uint16_t nextPeek = EcmaStringAccessor(replacementFlat).Get(peekIndex + 1);
1179                     if (nextPeek >= '0' && nextPeek <= '9') {
1180                         constexpr uint32_t TEN_BASE = 10;
1181                         uint32_t newScaledIndex = scaledIndex * TEN_BASE + (nextPeek - '0');
1182                         if (newScaledIndex <= capturesLength) {
1183                             scaledIndex = newScaledIndex;
1184                             advance = 2;  // 2: 2 means from index needs to add two.
1185                         }
1186                     }
1187                 }
1188 
1189                 if (scaledIndex == 0 || scaledIndex > capturesLength) {
1190                     stringBuilder += '$';
1191                     continueFromIndex = peekIndex;
1192                     break;
1193                 }
1194 
1195                 JSTaggedValue capturesVal(captureList->Get(scaledIndex - 1));
1196                 if (!capturesVal.IsUndefined()) {
1197                     EcmaString *captureString = EcmaString::Cast(capturesVal.GetTaggedObject());
1198                     stringBuilder += EcmaStringAccessor(captureString).ToU16String();
1199                     if (EcmaStringAccessor(captureString).IsUtf16()) {
1200                         canBeCompress = false;
1201                     }
1202                 }
1203                 continueFromIndex = peekIndex + advance;
1204                 break;
1205             }
1206             case '<': {
1207                 if (namedCaptures->IsUndefined()) {
1208                     stringBuilder += '$';
1209                     continueFromIndex = peekIndex;
1210                     break;
1211                 }
1212                 JSHandle<EcmaString> greaterSymString = factory->NewFromASCII(">");
1213                 int32_t pos = EcmaStringAccessor::IndexOf(ecmaVm, replacementFlat, greaterSymString, peekIndex);
1214                 if (pos == -1) {
1215                     stringBuilder += '$';
1216                     continueFromIndex = peekIndex;
1217                     break;
1218                 }
1219                 JSHandle<EcmaString> groupName(thread,
1220                                                EcmaStringAccessor::FastSubString(ecmaVm, replacementFlat,
1221                                                                                  peekIndex + 1, pos - peekIndex - 1));
1222                 JSHandle<JSTaggedValue> names(groupName);
1223                 JSHandle<JSTaggedValue> capture = JSObject::GetProperty(thread, namedCaptures, names).GetValue();
1224                 if (capture->IsUndefined()) {
1225                     continueFromIndex = pos + 1;
1226                     break;
1227                 }
1228                 JSHandle<EcmaString> captureName(capture);
1229                 stringBuilder += EcmaStringAccessor(captureName).ToU16String();
1230                 if (EcmaStringAccessor(captureName).IsUtf16()) {
1231                     canBeCompress = false;
1232                 }
1233                 continueFromIndex = pos + 1;
1234                 break;
1235             }
1236             default:
1237                 stringBuilder += '$';
1238                 continueFromIndex = peekIndex;
1239                 break;
1240         }
1241         // Go the the next $ in the replacement.
1242         nextDollarIndex = EcmaStringAccessor::IndexOf(ecmaVm, replacementFlat, dollarString, continueFromIndex);
1243         if (nextDollarIndex < 0) {
1244             if (continueFromIndex < replaceLength) {
1245                 EcmaString *nextAppend = EcmaStringAccessor::FastSubString(ecmaVm, replacementFlat, continueFromIndex,
1246                                                                            replaceLength - continueFromIndex);
1247                 stringBuilder += EcmaStringAccessor(nextAppend).ToU16String();
1248                 if (EcmaStringAccessor(nextAppend).IsUtf16()) {
1249                     canBeCompress = false;
1250                 }
1251             }
1252             auto *char16tData = const_cast<char16_t *>(stringBuilder.c_str());
1253             auto *uint16tData = reinterpret_cast<uint16_t *>(char16tData);
1254             return canBeCompress ?
1255                    factory->NewFromUtf16LiteralCompress(uint16tData, stringBuilder.length()).GetTaggedValue() :
1256                    factory->NewFromUtf16LiteralNotCompress(uint16tData, stringBuilder.length()).GetTaggedValue();
1257         }
1258         // Append substring between the previous and the next $ character.
1259         if (nextDollarIndex > continueFromIndex) {
1260             EcmaString *nextAppend = EcmaStringAccessor::FastSubString(
1261                 ecmaVm, replacementFlat, continueFromIndex, nextDollarIndex - continueFromIndex);
1262             stringBuilder += EcmaStringAccessor(nextAppend).ToU16String();
1263             if (EcmaStringAccessor(nextAppend).IsUtf16()) {
1264                 canBeCompress = false;
1265             }
1266         }
1267     }
1268     LOG_ECMA(FATAL) << "this branch is unreachable";
1269     UNREACHABLE();
1270 }
1271 
1272 // 21.1.3.15
Search(EcmaRuntimeCallInfo * argv)1273 JSTaggedValue BuiltinsString::Search(EcmaRuntimeCallInfo *argv)
1274 {
1275     ASSERT(argv);
1276     BUILTINS_API_TRACE(argv->GetThread(), String, Search);
1277     JSThread *thread = argv->GetThread();
1278     [[maybe_unused]] EcmaHandleScope handleScope(thread);
1279     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1280     JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1281     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1282     JSHandle<JSTaggedValue> regexp = BuiltinsString::GetCallArg(argv, 0);
1283     JSHandle<JSTaggedValue> searchTag = thread->GetEcmaVM()->GetGlobalEnv()->GetSearchSymbol();
1284     JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
1285     if (!regexp->IsUndefined() && !regexp->IsNull()) {
1286         if (regexp->IsECMAObject()) {
1287             JSHandle<JSTaggedValue> searcher = JSObject::GetMethod(thread, regexp, searchTag);
1288             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1289             if (!searcher->IsUndefined()) {
1290                 ASSERT(searcher->IsJSFunction());
1291                 EcmaRuntimeCallInfo *info =
1292                     EcmaInterpreter::NewRuntimeCallInfo(thread, searcher, regexp, undefined, 1);
1293                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1294                 info->SetCallArg(thisTag.GetTaggedValue());
1295                 return JSFunction::Call(info);
1296             }
1297         }
1298     }
1299     JSHandle<EcmaString> thisVal = JSTaggedValue::ToString(thread, thisTag);
1300     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1301     JSHandle<JSTaggedValue> rx(thread, BuiltinsRegExp::RegExpCreate(thread, regexp, undefined));
1302     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1303     EcmaRuntimeCallInfo *info =
1304         EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, rx, undefined, 1);
1305     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1306     info->SetCallArg(thisVal.GetTaggedValue());
1307     return JSFunction::Invoke(info, searchTag);
1308 }
1309 
1310 // 21.1.3.16
Slice(EcmaRuntimeCallInfo * argv)1311 JSTaggedValue BuiltinsString::Slice(EcmaRuntimeCallInfo *argv)
1312 {
1313     ASSERT(argv);
1314     BUILTINS_API_TRACE(argv->GetThread(), String, Slice);
1315     JSThread *thread = argv->GetThread();
1316     [[maybe_unused]] EcmaHandleScope handleScope(thread);
1317 
1318     JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1319     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1320     JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
1321     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1322     int32_t thisLen = static_cast<int32_t>(EcmaStringAccessor(thisHandle).GetLength());
1323     JSHandle<JSTaggedValue> startTag = BuiltinsString::GetCallArg(argv, 0);
1324     JSTaggedNumber startVal = JSTaggedValue::ToInteger(thread, startTag);
1325     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1326     int32_t start = ConvertDoubleToInt(startVal.GetNumber());
1327     int32_t end = 0;
1328     JSHandle<JSTaggedValue> endTag = BuiltinsString::GetCallArg(argv, 1);
1329     if (endTag->IsUndefined()) {
1330         end = thisLen;
1331     } else {
1332         JSTaggedNumber endVal = JSTaggedValue::ToInteger(thread, endTag);
1333         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1334         end = ConvertDoubleToInt(endVal.GetNumber());
1335     }
1336     int32_t from = 0;
1337     int32_t to = 0;
1338     if (start < 0) {
1339         from = std::max(start + thisLen, 0);
1340     } else {
1341         from = std::min(start, thisLen);
1342     }
1343     if (end < 0) {
1344         to = std::max(end + thisLen, 0);
1345     } else {
1346         to = std::min(end, thisLen);
1347     }
1348     int32_t len = std::max(to - from, 0);
1349     return JSTaggedValue(EcmaStringAccessor::FastSubString(thread->GetEcmaVM(), thisHandle, from, len));
1350 }
1351 
1352 // 21.1.3.17
Split(EcmaRuntimeCallInfo * argv)1353 JSTaggedValue BuiltinsString::Split(EcmaRuntimeCallInfo *argv)
1354 {
1355     ASSERT(argv);
1356     BUILTINS_API_TRACE(argv->GetThread(), String, Split);
1357     JSThread *thread = argv->GetThread();
1358     [[maybe_unused]] EcmaHandleScope handleScope(thread);
1359     auto ecmaVm = thread->GetEcmaVM();
1360     JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
1361 
1362     // Let O be RequireObjectCoercible(this value).
1363     JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv));
1364     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1365     JSHandle<JSObject> thisObj(thisTag);
1366     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1367     JSHandle<JSTaggedValue> seperatorTag = BuiltinsString::GetCallArg(argv, 0);
1368     JSHandle<JSTaggedValue> limitTag = BuiltinsString::GetCallArg(argv, 1);
1369     // If separator is neither undefined nor null, then
1370     if (seperatorTag->IsECMAObject()) {
1371         JSHandle<JSTaggedValue> splitKey = env->GetSplitSymbol();
1372         // Let splitter be GetMethod(separator, @@split).
1373         JSHandle<JSTaggedValue> splitter = JSObject::GetMethod(thread, seperatorTag, splitKey);
1374         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1375         if (!splitter->IsUndefined()) {
1376             // Return Call(splitter, separator, «‍O, limit»).
1377             const uint32_t argsLength = 2;
1378             JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
1379             EcmaRuntimeCallInfo *info =
1380                 EcmaInterpreter::NewRuntimeCallInfo(thread, splitter, seperatorTag, undefined, argsLength);
1381             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1382             info->SetCallArg(thisTag.GetTaggedValue(), limitTag.GetTaggedValue());
1383             return JSFunction::Call(info);
1384         }
1385     }
1386     // Let S be ToString(O).
1387     JSHandle<EcmaString> thisString = JSTaggedValue::ToString(thread, thisTag);
1388     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1389     // Let A be ArrayCreate(0).
1390     JSHandle<JSObject> resultArray(JSArray::ArrayCreate(thread, JSTaggedNumber(0)));
1391     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1392     uint32_t arrayLength = 0;
1393     // If limit is undefined, let lim = 2^53–1; else let lim = ToLength(limit).
1394     uint32_t lim = 0;
1395     if (limitTag->IsUndefined()) {
1396         lim = UINT32_MAX - 1;
1397     } else {
1398         lim = JSTaggedValue::ToInteger(thread, limitTag).ToUint32();
1399     }
1400     // ReturnIfAbrupt(lim).
1401     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1402     // If lim = 0, return A.
1403     if (lim == 0) {
1404         return resultArray.GetTaggedValue();
1405     }
1406     // Let s be the number of elements in S.
1407     int32_t thisLength = static_cast<int32_t>(EcmaStringAccessor(thisString).GetLength());
1408     JSHandle<EcmaString> seperatorString = JSTaggedValue::ToString(thread, seperatorTag);
1409     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1410     if (seperatorTag->IsUndefined()) {
1411         // Perform CreateDataProperty(A, "0", S).
1412         JSObject::CreateDataProperty(thread, resultArray, 0, JSHandle<JSTaggedValue>(thisString));
1413         ASSERT_PRINT(!thread->HasPendingException(), "CreateDataProperty(A, \"0\", S) can't throw exception");
1414         return resultArray.GetTaggedValue();
1415     }
1416     // If S.length = 0, then
1417     if (thisLength == 0) {
1418         if (EcmaStringAccessor::IndexOf(ecmaVm, thisString, seperatorString) != -1) {
1419             return resultArray.GetTaggedValue();
1420         }
1421         JSObject::CreateDataProperty(thread, resultArray, 0, JSHandle<JSTaggedValue>(thisString));
1422         ASSERT_PRINT(!thread->HasPendingException(), "CreateDataProperty(A, \"0\", S) can't throw exception");
1423         return resultArray.GetTaggedValue();
1424     }
1425 
1426     int32_t seperatorLength = static_cast<int32_t>(EcmaStringAccessor(seperatorString).GetLength());
1427     if (seperatorLength == 0) {
1428         for (int32_t i = 0; i < thisLength; ++i) {
1429             EcmaString *elementString = EcmaStringAccessor::FastSubString(ecmaVm, thisString, i, 1);
1430             JSHandle<JSTaggedValue> elementTag(thread, elementString);
1431             JSObject::CreateDataProperty(thread, resultArray, arrayLength, elementTag);
1432             ASSERT_PRINT(!thread->HasPendingException(), "CreateDataProperty can't throw exception");
1433             ++arrayLength;
1434             if (arrayLength == lim) {
1435                 return resultArray.GetTaggedValue();
1436             }
1437         }
1438         return resultArray.GetTaggedValue();
1439     }
1440     int32_t index = 0;
1441     int32_t pos = EcmaStringAccessor::IndexOf(ecmaVm, thisString, seperatorString);
1442     while (pos != -1) {
1443         EcmaString *elementString = EcmaStringAccessor::FastSubString(ecmaVm, thisString, index, pos - index);
1444         JSHandle<JSTaggedValue> elementTag(thread, elementString);
1445         JSObject::CreateDataProperty(thread, resultArray, arrayLength, elementTag);
1446         ASSERT_PRINT(!thread->HasPendingException(), "CreateDataProperty can't throw exception");
1447         ++arrayLength;
1448         if (arrayLength == lim) {
1449             return resultArray.GetTaggedValue();
1450         }
1451         index = pos + seperatorLength;
1452         pos = EcmaStringAccessor::IndexOf(ecmaVm, thisString, seperatorString, index);
1453     }
1454     EcmaString *elementString = EcmaStringAccessor::FastSubString(ecmaVm, thisString, index, thisLength - index);
1455     JSHandle<JSTaggedValue> elementTag(thread, elementString);
1456     JSObject::CreateDataProperty(thread, resultArray, arrayLength, elementTag);
1457     ASSERT_PRINT(!thread->HasPendingException(), "CreateDataProperty can't throw exception");
1458     return resultArray.GetTaggedValue();
1459 }
1460 
1461 // 21.1.3.18
StartsWith(EcmaRuntimeCallInfo * argv)1462 JSTaggedValue BuiltinsString::StartsWith(EcmaRuntimeCallInfo *argv)
1463 {
1464     ASSERT(argv);
1465     BUILTINS_API_TRACE(argv->GetThread(), String, StartsWith);
1466     JSThread *thread = argv->GetThread();
1467     [[maybe_unused]] EcmaHandleScope handleScope(thread);
1468     JSHandle<JSTaggedValue> searchTag = BuiltinsString::GetCallArg(argv, 0);
1469 
1470     JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1471     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1472     JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
1473     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1474     bool isRegexp = JSObject::IsRegExp(thread, searchTag);
1475     if (isRegexp) {
1476         THROW_TYPE_ERROR_AND_RETURN(thread, "is regexp", JSTaggedValue::Exception());
1477     }
1478 
1479     JSHandle<EcmaString> searchHandle = JSTaggedValue::ToString(thread, searchTag);
1480     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1481     uint32_t thisLen = EcmaStringAccessor(thisHandle).GetLength();
1482     uint32_t searchLen = EcmaStringAccessor(searchHandle).GetLength();
1483     int32_t pos = 0;
1484     JSHandle<JSTaggedValue> posTag = BuiltinsString::GetCallArg(argv, 1);
1485     if (posTag->IsUndefined()) {
1486         pos = 0;
1487     } else if (posTag->IsInt()) {
1488         pos = posTag->GetInt();
1489     } else {
1490         JSTaggedNumber posVal = JSTaggedValue::ToInteger(thread, posTag);
1491         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1492         pos = posVal.ToInt32();
1493     }
1494     pos = std::min(std::max(pos, 0), static_cast<int32_t>(thisLen));
1495     if (static_cast<uint32_t>(pos) + searchLen > thisLen) {
1496         return BuiltinsString::GetTaggedBoolean(false);
1497     }
1498     int32_t res = EcmaStringAccessor::IndexOf(thread->GetEcmaVM(), thisHandle, searchHandle, pos);
1499     if (res == pos) {
1500         return BuiltinsString::GetTaggedBoolean(true);
1501     }
1502     return BuiltinsString::GetTaggedBoolean(false);
1503 }
1504 
1505 // 21.1.3.19
Substring(EcmaRuntimeCallInfo * argv)1506 JSTaggedValue BuiltinsString::Substring(EcmaRuntimeCallInfo *argv)
1507 {
1508     ASSERT(argv);
1509     BUILTINS_API_TRACE(argv->GetThread(), String, Substring);
1510     JSThread *thread = argv->GetThread();
1511     [[maybe_unused]] EcmaHandleScope handleScope(thread);
1512 
1513     JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1514     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1515     JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
1516     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1517     int32_t thisLen = static_cast<int32_t>(EcmaStringAccessor(thisHandle).GetLength());
1518     JSHandle<JSTaggedValue> startTag = BuiltinsString::GetCallArg(argv, 0);
1519     JSTaggedNumber startVal = JSTaggedValue::ToInteger(thread, startTag);
1520     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1521     int32_t start = ConvertDoubleToInt(startVal.GetNumber());
1522     int32_t end = 0;
1523     JSHandle<JSTaggedValue> endTag = BuiltinsString::GetCallArg(argv, 1);
1524     if (endTag->IsUndefined()) {
1525         end = thisLen;
1526     } else {
1527         JSTaggedNumber endVal = JSTaggedValue::ToInteger(thread, endTag);
1528         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1529         end = ConvertDoubleToInt(endVal.GetNumber());
1530     }
1531     start = std::min(std::max(start, 0), thisLen);
1532     end = std::min(std::max(end, 0), thisLen);
1533     int32_t from = std::min(start, end);
1534     int32_t to = std::max(start, end);
1535     int32_t len = to - from;
1536     return JSTaggedValue(EcmaStringAccessor::FastSubString(thread->GetEcmaVM(), thisHandle, from, len));
1537 }
1538 
1539 // 21.1.3.20
ToLocaleLowerCase(EcmaRuntimeCallInfo * argv)1540 JSTaggedValue BuiltinsString::ToLocaleLowerCase(EcmaRuntimeCallInfo *argv)
1541 {
1542     ASSERT(argv);
1543     BUILTINS_API_TRACE(argv->GetThread(), String, ToLocaleLowerCase);
1544     JSThread *thread = argv->GetThread();
1545     EcmaVM *ecmaVm = thread->GetEcmaVM();
1546     [[maybe_unused]] EcmaHandleScope handleScope(thread);
1547 
1548     // Let O be RequireObjectCoercible(this value).
1549     JSHandle<JSTaggedValue> obj(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1550     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1551     // Let S be ? ToString(O).
1552     JSHandle<EcmaString> string = JSTaggedValue::ToString(thread, obj);
1553     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1554 
1555     // Let requestedLocales be ? CanonicalizeLocaleList(locales).
1556     JSHandle<JSTaggedValue> locales = GetCallArg(argv, 0);
1557     // Fast path
1558     if (locales->IsUndefined() && EcmaStringAccessor(string).IsUtf8()) {
1559         EcmaString *result = EcmaStringAccessor::TryToLower(ecmaVm, string);
1560         return JSTaggedValue(result);
1561     }
1562     JSHandle<TaggedArray> requestedLocales = intl::LocaleHelper::CanonicalizeLocaleList(thread, locales);
1563     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1564 
1565     // If requestedLocales is not an empty List, then Let requestedLocale be requestedLocales[0].
1566     // Else, Let requestedLocale be DefaultLocale().
1567     JSHandle<EcmaString> requestedLocale = intl::LocaleHelper::DefaultLocale(thread);
1568     if (requestedLocales->GetLength() != 0) {
1569         requestedLocale = JSHandle<EcmaString>(thread, requestedLocales->Get(0));
1570     }
1571 
1572     // Let noExtensionsLocale be the String value that is requestedLocale with all Unicode locale extension sequences
1573     // removed.
1574     intl::LocaleHelper::ParsedLocale noExtensionsLocale = intl::LocaleHelper::HandleLocale(requestedLocale);
1575     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1576 
1577     // Let availableLocales be a List with language tags that includes the languages for which the Unicode Character
1578     // Database contains language sensitive case mappings. Implementations may add additional language tags
1579     // if they support case mapping for additional locales.
1580     std::vector<std::string> availableLocales = intl::LocaleHelper::GetAvailableLocales(thread, nullptr, nullptr);
1581     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1582 
1583     // Let locale be BestAvailableLocale(availableLocales, noExtensionsLocale).
1584     std::string locale = intl::LocaleHelper::BestAvailableLocale(availableLocales, noExtensionsLocale.base);
1585     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1586 
1587     // If locale is undefined, let locale be "und".
1588     if (locale.empty()) {
1589         locale = "und";
1590     }
1591 
1592     // Let uString be a List containing in order the code points of S as defined in ES2020, 6.1.4,
1593     // starting at the first element of S.
1594     // Transform those elements in uString to the to the Unicode Default Case Conversion algorithm
1595     icu::Locale icuLocale = icu::Locale::createFromName(locale.c_str());
1596     EcmaString *result = EcmaStringAccessor::ToLocaleLower(ecmaVm, string, icuLocale);
1597     return JSTaggedValue(result);
1598 }
1599 
1600 // 21.1.3.21
ToLocaleUpperCase(EcmaRuntimeCallInfo * argv)1601 JSTaggedValue BuiltinsString::ToLocaleUpperCase(EcmaRuntimeCallInfo *argv)
1602 {
1603     ASSERT(argv);
1604     BUILTINS_API_TRACE(argv->GetThread(), String, ToLocaleLowerCase);
1605     JSThread *thread = argv->GetThread();
1606     EcmaVM *ecmaVm = thread->GetEcmaVM();
1607     [[maybe_unused]] EcmaHandleScope handleScope(thread);
1608 
1609     // Let O be RequireObjectCoercible(this value).
1610     JSHandle<JSTaggedValue> obj(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1611     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1612     // Let S be ? ToString(O).
1613     JSHandle<EcmaString> string = JSTaggedValue::ToString(thread, obj);
1614     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1615 
1616     // Let requestedLocales be ? CanonicalizeLocaleList(locales).
1617     JSHandle<JSTaggedValue> locales = GetCallArg(argv, 0);
1618     JSHandle<TaggedArray> requestedLocales = intl::LocaleHelper::CanonicalizeLocaleList(thread, locales);
1619     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1620 
1621     // If requestedLocales is not an empty List, then Let requestedLocale be requestedLocales[0].
1622     // Else, Let requestedLocale be DefaultLocale().
1623     JSHandle<EcmaString> requestedLocale = intl::LocaleHelper::DefaultLocale(thread);
1624     if (requestedLocales->GetLength() != 0) {
1625         requestedLocale = JSHandle<EcmaString>(thread, requestedLocales->Get(0));
1626     }
1627 
1628     // Let noExtensionsLocale be the String value that is requestedLocale with all Unicode locale extension sequences
1629     // removed.
1630     intl::LocaleHelper::ParsedLocale noExtensionsLocale = intl::LocaleHelper::HandleLocale(requestedLocale);
1631     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1632 
1633     // Let availableLocales be a List with language tags that includes the languages for which the Unicode Character
1634     // Database contains language sensitive case mappings. Implementations may add additional language tags
1635     // if they support case mapping for additional locales.
1636     std::vector<std::string> availableLocales = intl::LocaleHelper::GetAvailableLocales(thread, nullptr, nullptr);
1637     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1638 
1639     // Let locale be BestAvailableLocale(availableLocales, noExtensionsLocale).
1640     std::string locale = intl::LocaleHelper::BestAvailableLocale(availableLocales, noExtensionsLocale.base);
1641     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1642 
1643     // If locale is undefined, let locale be "und".
1644     if (locale.empty()) {
1645         locale = "und";
1646     }
1647 
1648     // Let uString be a List containing in order the code points of S as defined in ES2020, 6.1.4,
1649     // starting at the first element of S.
1650     // Transform those elements in uString to the to the Unicode Default Case Conversion algorithm
1651     icu::Locale icuLocale = icu::Locale::createFromName(locale.c_str());
1652     EcmaString *result = EcmaStringAccessor::ToLocaleUpper(ecmaVm, string, icuLocale);
1653     return JSTaggedValue(result);
1654 }
1655 
1656 // 21.1.3.22
ToLowerCase(EcmaRuntimeCallInfo * argv)1657 JSTaggedValue BuiltinsString::ToLowerCase(EcmaRuntimeCallInfo *argv)
1658 {
1659     ASSERT(argv);
1660     BUILTINS_API_TRACE(argv->GetThread(), String, ToLowerCase);
1661     JSThread *thread = argv->GetThread();
1662     [[maybe_unused]] EcmaHandleScope handleScope(thread);
1663     JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1664     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1665     JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
1666     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1667     EcmaString *result = EcmaStringAccessor::ToLower(thread->GetEcmaVM(), thisHandle);
1668     return JSTaggedValue(result);
1669 }
1670 
1671 // 21.1.3.23
ToString(EcmaRuntimeCallInfo * argv)1672 JSTaggedValue BuiltinsString::ToString(EcmaRuntimeCallInfo *argv)
1673 {
1674     ASSERT(argv);
1675     return ThisStringValue(argv->GetThread(), GetThis(argv).GetTaggedValue());
1676 }
1677 
1678 // 21.1.3.24
ToUpperCase(EcmaRuntimeCallInfo * argv)1679 JSTaggedValue BuiltinsString::ToUpperCase(EcmaRuntimeCallInfo *argv)
1680 {
1681     ASSERT(argv);
1682     BUILTINS_API_TRACE(argv->GetThread(), String, ToUpperCase);
1683     JSThread *thread = argv->GetThread();
1684     [[maybe_unused]] EcmaHandleScope handleScope(thread);
1685 
1686     JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1687     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1688     JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
1689     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1690     EcmaString *result = EcmaStringAccessor::ToUpper(thread->GetEcmaVM(), thisHandle);
1691     return JSTaggedValue(result);
1692 }
1693 
1694 // 21.1.3.25
Trim(EcmaRuntimeCallInfo * argv)1695 JSTaggedValue BuiltinsString::Trim(EcmaRuntimeCallInfo *argv)
1696 {
1697     ASSERT(argv);
1698     BUILTINS_API_TRACE(argv->GetThread(), String, Trim);
1699     JSThread *thread = argv->GetThread();
1700     [[maybe_unused]] EcmaHandleScope handleScope(thread);
1701     JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1702     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1703     JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
1704     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1705     EcmaString *res = EcmaStringAccessor::Trim(thread, thisHandle, EcmaString::TrimMode::TRIM);
1706     return JSTaggedValue(res);
1707 }
1708 
TrimStart(EcmaRuntimeCallInfo * argv)1709 JSTaggedValue BuiltinsString::TrimStart(EcmaRuntimeCallInfo *argv)
1710 {
1711     ASSERT(argv);
1712     BUILTINS_API_TRACE(argv->GetThread(), String, TrimStart);
1713     JSThread *thread = argv->GetThread();
1714     [[maybe_unused]] EcmaHandleScope handleScope(thread);
1715     JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv));
1716     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1717     JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
1718     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1719     EcmaString *res = EcmaStringAccessor::Trim(thread, thisHandle, EcmaString::TrimMode::TRIM_START);
1720     return JSTaggedValue(res);
1721 }
1722 
TrimEnd(EcmaRuntimeCallInfo * argv)1723 JSTaggedValue BuiltinsString::TrimEnd(EcmaRuntimeCallInfo *argv)
1724 {
1725     ASSERT(argv);
1726     BUILTINS_API_TRACE(argv->GetThread(), String, TrimEnd);
1727     JSThread *thread = argv->GetThread();
1728     [[maybe_unused]] EcmaHandleScope handleScope(thread);
1729     JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv));
1730     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1731     JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
1732     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1733     EcmaString *res = EcmaStringAccessor::Trim(thread, thisHandle, EcmaString::TrimMode::TRIM_END);
1734     return JSTaggedValue(res);
1735 }
1736 
TrimLeft(EcmaRuntimeCallInfo * argv)1737 JSTaggedValue BuiltinsString::TrimLeft(EcmaRuntimeCallInfo *argv)
1738 {
1739     ASSERT(argv);
1740     BUILTINS_API_TRACE(argv->GetThread(), String, TrimLeft);
1741     JSThread *thread = argv->GetThread();
1742     [[maybe_unused]] EcmaHandleScope handleScope(thread);
1743     JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv));
1744     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1745     JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
1746     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1747     EcmaString *res = EcmaStringAccessor::Trim(thread, thisHandle, EcmaString::TrimMode::TRIM_START);
1748     return JSTaggedValue(res);
1749 }
1750 
TrimRight(EcmaRuntimeCallInfo * argv)1751 JSTaggedValue BuiltinsString::TrimRight(EcmaRuntimeCallInfo *argv)
1752 {
1753     ASSERT(argv);
1754     BUILTINS_API_TRACE(argv->GetThread(), String, TrimRight);
1755     JSThread *thread = argv->GetThread();
1756     [[maybe_unused]] EcmaHandleScope handleScope(thread);
1757     JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv));
1758     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1759     JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
1760     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1761     EcmaString *res = EcmaStringAccessor::Trim(thread, thisHandle, EcmaString::TrimMode::TRIM_END);
1762     return JSTaggedValue(res);
1763 }
1764 
1765 // 21.1.3.26
ValueOf(EcmaRuntimeCallInfo * argv)1766 JSTaggedValue BuiltinsString::ValueOf(EcmaRuntimeCallInfo *argv)
1767 {
1768     ASSERT(argv);
1769     return ThisStringValue(argv->GetThread(), GetThis(argv).GetTaggedValue());
1770 }
1771 
1772 // 21.1.3.27
GetStringIterator(EcmaRuntimeCallInfo * argv)1773 JSTaggedValue BuiltinsString::GetStringIterator(EcmaRuntimeCallInfo *argv)
1774 {
1775     ASSERT(argv);
1776     BUILTINS_API_TRACE(argv->GetThread(), String, GetStringIterator);
1777     JSThread *thread = argv->GetThread();
1778     [[maybe_unused]] EcmaHandleScope handleScope(thread);
1779     // 1. Let O be RequireObjectCoercible(this value).
1780     JSHandle<JSTaggedValue> current(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1781     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1782     // Let S be ToString(O).
1783 
1784     JSHandle<EcmaString> string = JSTaggedValue::ToString(thread, current);
1785     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(argv->GetThread());
1786     // Return CreateStringIterator(S).
1787     return JSStringIterator::CreateStringIterator(thread, string).GetTaggedValue();
1788 }
1789 
1790 //  B.2.3.1
SubStr(EcmaRuntimeCallInfo * argv)1791 JSTaggedValue BuiltinsString::SubStr(EcmaRuntimeCallInfo *argv)
1792 {
1793     ASSERT(argv);
1794     BUILTINS_API_TRACE(argv->GetThread(), String, SubStr);
1795     JSThread *thread = argv->GetThread();
1796 
1797     [[maybe_unused]] EcmaHandleScope handleScope(thread);
1798 
1799     // 1. Let O be RequireObjectCoercible(this value).
1800     // 2. Let S be ToString(O).
1801 
1802     JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1803     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1804     JSHandle<EcmaString> thisString = JSTaggedValue::ToString(thread, thisTag);
1805 
1806     // 3. ReturnIfAbrupt(S).
1807     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1808     JSHandle<JSTaggedValue> intStart = GetCallArg(argv, 0);
1809     // 4. Let intStart be ToInteger(start).
1810     JSTaggedNumber numStart = JSTaggedValue::ToInteger(thread, intStart);
1811     // 5. ReturnIfAbrupt(intStart).
1812     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1813     int32_t start = numStart.ToInt32();
1814     JSHandle<JSTaggedValue> lengthTag = GetCallArg(argv, 1);
1815     // 6. If length is undefined, let end be +; otherwise let end be ToInteger(length).
1816     int32_t end = 0;
1817     if (lengthTag->IsUndefined()) {
1818         end = INT_MAX;
1819     } else {
1820         JSTaggedNumber lengthNumber = JSTaggedValue::ToInteger(thread, lengthTag);
1821         // 7. ReturnIfAbrupt(end).
1822         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1823         end = lengthNumber.ToInt32();
1824     }
1825     // 8. Let size be the number of code units in S.
1826     int32_t size = static_cast<int32_t>(EcmaStringAccessor(thisString).GetLength());
1827     // 9. If intStart < 0, let intStart be max(size + intStart,0).
1828     if (start < 0) {
1829         start = std::max(size + start, 0);
1830     }
1831     // 10. Let resultLength be min(max(end,0), size – intStart).
1832     int32_t resultLength = std::min(std::max(end, 0), size - start);
1833     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1834     // 11. If resultLength  0, return the empty String "".
1835     if (resultLength <= 0) {
1836         return factory->GetEmptyString().GetTaggedValue();
1837     }
1838     return JSTaggedValue(EcmaStringAccessor::FastSubString(thread->GetEcmaVM(), thisString, start, resultLength));
1839 }
1840 
1841 // 22.1.3.1
At(EcmaRuntimeCallInfo * argv)1842 JSTaggedValue BuiltinsString::At(EcmaRuntimeCallInfo *argv)
1843 {
1844     ASSERT(argv);
1845     BUILTINS_API_TRACE(argv->GetThread(), String, Substring);
1846     JSThread *thread = argv->GetThread();
1847     [[maybe_unused]] EcmaHandleScope handleScope(thread);
1848 
1849     // 1. Let O be RequireObjectCoercible(this value).
1850     // 2. Let S be ToString(O).
1851     JSHandle<JSTaggedValue> thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)));
1852     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1853     JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
1854     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1855 
1856     // 3. Let len be the length of S.
1857     int32_t thisLen = static_cast<int32_t>(EcmaStringAccessor(thisHandle).GetLength());
1858 
1859     // 4. Let relativeIndex be ? ToIntegerOrInfinity(index).
1860     JSHandle<JSTaggedValue> indexTag = BuiltinsString::GetCallArg(argv, 0);
1861     JSTaggedNumber indexVal = JSTaggedValue::ToInteger(thread, indexTag);
1862     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1863     int32_t relativeIndex = ConvertDoubleToInt(indexVal.GetNumber());
1864 
1865     // 5. If relativeIndex ≥ 0, then Let k be relativeIndex. 6. Else, Let k be len + relativeIndex.
1866     int32_t k = 0;
1867     if (relativeIndex >= 0) {
1868         k = relativeIndex;
1869     } else {
1870         k = thisLen + relativeIndex;
1871     }
1872     // 7. If k < 0 or k ≥ len, return undefined.
1873     if (k < 0 || k >= thisLen) {
1874         return JSTaggedValue::Undefined();
1875     }
1876     // 8. Return the substring of S from k to k + 1.
1877     return JSTaggedValue(EcmaStringAccessor::FastSubString(thread->GetEcmaVM(), thisHandle, k, 1));
1878 }
1879 
GetLength(EcmaRuntimeCallInfo * argv)1880 JSTaggedValue BuiltinsString::GetLength(EcmaRuntimeCallInfo *argv)
1881 {
1882     ASSERT(argv);
1883     JSThread *thread = argv->GetThread();
1884     BUILTINS_API_TRACE(thread, String, GetLength);
1885     [[maybe_unused]] EcmaHandleScope handleScope(thread);
1886     JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
1887 
1888     JSHandle<EcmaString> thisString = JSTaggedValue::ToString(thread, thisHandle);
1889     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1890     return GetTaggedInt(EcmaStringAccessor(thisString).GetLength());
1891 }
1892 
1893 // 21.1.3
ThisStringValue(JSThread * thread,JSTaggedValue value)1894 JSTaggedValue BuiltinsString::ThisStringValue(JSThread *thread, JSTaggedValue value)
1895 {
1896     BUILTINS_API_TRACE(thread, String, ThisStringValue);
1897     if (value.IsString()) {
1898         return value;
1899     }
1900     if (value.IsECMAObject()) {
1901         auto jshclass = value.GetTaggedObject()->GetClass();
1902         if (jshclass->GetObjectType() == JSType::JS_PRIMITIVE_REF) {
1903             JSTaggedValue primitive = JSPrimitiveRef::Cast(value.GetTaggedObject())->GetValue();
1904             if (primitive.IsString()) {
1905                 return primitive;
1906             }
1907         }
1908     }
1909     THROW_TYPE_ERROR_AND_RETURN(thread, "can not convert to String", JSTaggedValue::Exception());
1910 }
1911 
Pad(EcmaRuntimeCallInfo * argv,bool isStart)1912 JSTaggedValue BuiltinsString::Pad(EcmaRuntimeCallInfo *argv, bool isStart)
1913 {
1914     JSThread *thread = argv->GetThread();
1915     BUILTINS_API_TRACE(thread, String, Pad);
1916     [[maybe_unused]] EcmaHandleScope handleScope(thread);
1917 
1918     JSHandle<JSTaggedValue> thisTag = JSTaggedValue::RequireObjectCoercible(thread, BuiltinsString::GetThis(argv));
1919     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1920     JSHandle<EcmaString> thisHandle = JSTaggedValue::ToString(thread, thisTag);
1921     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1922     JSHandle<JSTaggedValue> lengthTag = GetCallArg(argv, 0);
1923     int32_t intMaxLength = JSTaggedValue::ToInt32(thread, lengthTag);
1924     int32_t stringLength = static_cast<int32_t>(EcmaStringAccessor(thisHandle).GetLength());
1925     if (intMaxLength <= stringLength) {
1926         return thisHandle.GetTaggedValue();
1927     }
1928     JSHandle<JSTaggedValue> fillString = GetCallArg(argv, 1);
1929     std::u16string stringBuilder;
1930     if (fillString->IsUndefined()) {
1931         stringBuilder = u" ";
1932     } else {
1933         JSHandle<EcmaString> filler = JSTaggedValue::ToString(thread, fillString);
1934         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1935         stringBuilder = EcmaStringAccessor(filler).ToU16String();
1936     }
1937     if (stringBuilder.size() == 0) {
1938         return thisHandle.GetTaggedValue();
1939     }
1940     std::u16string u16strSearch = EcmaStringAccessor(thisHandle).ToU16String();
1941     int32_t fillLen = intMaxLength - stringLength;
1942     int32_t len = static_cast<int32_t>(stringBuilder.length());
1943     std::u16string fiString;
1944     for (int32_t i = 0; i < fillLen; ++i) {
1945         fiString += stringBuilder[i % len];
1946     }
1947     std::u16string resultString;
1948     if (isStart) {
1949         resultString = fiString + u16strSearch;
1950     } else {
1951         resultString = u16strSearch + fiString;
1952     }
1953     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1954     return factory->NewFromUtf16Literal(reinterpret_cast<const uint16_t *>(resultString.c_str()),
1955                                         resultString.size()).GetTaggedValue();
1956 }
1957 
ConvertDoubleToInt(double d)1958 int32_t BuiltinsString::ConvertDoubleToInt(double d)
1959 {
1960     if (std::isnan(d) || d == -base::POSITIVE_INFINITY) {
1961         return 0;
1962     }
1963     if (d >= static_cast<double>(INT_MAX)) {
1964         return INT_MAX;
1965     }
1966     if (d <= static_cast<double>(INT_MIN)) {
1967         return INT_MIN;
1968     }
1969     return base::NumberHelper::DoubleToInt(d, base::INT32_BITS);
1970 }
1971 }  // namespace panda::ecmascript::builtins
1972