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