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