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