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