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