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