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