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 #ifndef ECMASCRIPT_JSLOCALE_H 17 #define ECMASCRIPT_JSLOCALE_H 18 19 #include "ecmascript/ecma_macros.h" 20 #include "ecmascript/js_array.h" 21 #include "ecmascript/js_object.h" 22 #include "ecmascript/js_thread.h" 23 #include "ecmascript/mem/c_containers.h" 24 25 #include "ohos/init_data.h" 26 #include "unicode/basictz.h" 27 #include "unicode/brkiter.h" 28 #include "unicode/calendar.h" 29 #include "unicode/coll.h" 30 #include "unicode/datefmt.h" 31 #include "unicode/decimfmt.h" 32 #include "unicode/dtitvfmt.h" 33 #include "unicode/dtptngen.h" 34 #include "unicode/fieldpos.h" 35 #include "unicode/formattedvalue.h" 36 #include "unicode/gregocal.h" 37 #include "unicode/locid.h" 38 #include "unicode/normalizer2.h" 39 #include "unicode/numberformatter.h" 40 #include "unicode/numfmt.h" 41 #include "unicode/numsys.h" 42 #include "unicode/smpdtfmt.h" 43 #include "unicode/timezone.h" 44 #include "unicode/udat.h" 45 #include "unicode/unistr.h" 46 #include "unicode/ures.h" 47 #include "unicode/ustring.h" 48 #include "unicode/uvernum.h" 49 #include "unicode/uversion.h" 50 51 namespace panda::ecmascript { 52 53 enum class OptionType : uint8_t { STRING = 0x01, BOOLEAN }; 54 enum class LocaleMatcherOption : uint8_t { LOOKUP = 0x01, BEST_FIT, EXCEPTION }; 55 enum class FormatMatcherOption : uint8_t { BASIC = 0x01, BEST_FIT, EXCEPTION }; 56 enum class LocaleType : uint8_t { 57 LITERAL = 0x01, 58 NUMBER, 59 PLUS_SIGN, 60 MINUS_SIGN, 61 PERCENT_SIGN, 62 UNIT_PREFIX, 63 UNIT_SUFFIX, 64 CURRENCY_CODE, 65 CURRENCY_PREFIX, 66 CURRENCY_SUFFIX, 67 }; 68 69 enum class TypeOption : uint8_t { CARDINAL = 0x01, ORDINAL, EXCEPTION }; 70 enum class RoundingType : uint8_t { FRACTIONDIGITS = 0x01, SIGNIFICANTDIGITS, COMPACTROUNDING, EXCEPTION }; 71 enum class NotationOption : uint8_t { STANDARD = 0x01, SCIENTIFIC, ENGINEERING, COMPACT, EXCEPTION }; 72 73 constexpr uint32_t MAX_DIGITS = 21; 74 constexpr uint32_t MAX_FRACTION_DIGITS = 20; 75 constexpr uint8_t INTL_INDEX_ZERO = 0; 76 constexpr uint8_t INTL_INDEX_ONE = 1; 77 constexpr uint8_t INTL_INDEX_TWO = 2; 78 constexpr uint8_t INTL_INDEX_THREE = 3; 79 constexpr uint8_t INTL_INDEX_FOUR = 4; 80 constexpr uint8_t INTL_INDEX_FIVE = 5; 81 constexpr uint8_t INTL_INDEX_EIGHT = 8; 82 83 class JSIntlIterator : public icu::Locale::Iterator { 84 public: JSIntlIterator(const JSHandle<TaggedArray> &data, uint32_t length)85 JSIntlIterator(const JSHandle<TaggedArray> &data, uint32_t length) : length_(length), curIdx_(0) 86 { 87 for (uint32_t idx = 0; idx < length; idx++) { 88 auto itor = data->Get(idx); 89 std::string str = EcmaStringAccessor(itor).ToStdString(); 90 data_.emplace_back(str); 91 } 92 } 93 94 ~JSIntlIterator() override = default; 95 DEFAULT_COPY_SEMANTIC(JSIntlIterator); 96 DEFAULT_MOVE_SEMANTIC(JSIntlIterator); 97 98 UBool hasNext() const override 99 { 100 return static_cast<UBool>(curIdx_ < length_); 101 } 102 103 const icu::Locale &next() override 104 { 105 ASSERT(curIdx_ < length_); 106 UErrorCode status = U_ZERO_ERROR; 107 locale_ = icu::Locale::forLanguageTag(data_[curIdx_].c_str(), status); 108 ASSERT(U_SUCCESS(status)); 109 curIdx_++; 110 return locale_; 111 } 112 113 inline const std::string &operator[](size_t index) const noexcept 114 { 115 ASSERT(index < length_); 116 return data_[index]; 117 } 118 119 private: 120 std::vector<std::string> data_{}; 121 uint32_t length_{0}; 122 uint32_t curIdx_{0}; 123 icu::Locale locale_{}; 124 }; 125 126 struct ResolvedLocale { 127 std::string locale {}; 128 icu::Locale localeData {}; 129 std::map<std::string, std::string> extensions {}; 130 }; 131 132 struct MatcherResult { 133 std::string locale; 134 std::string extension; 135 }; 136 137 struct OptionData { 138 std::string name; 139 std::string key; 140 std::vector<std::string> possibleValues; 141 bool isBoolValue = false; 142 }; 143 144 struct TagElements { 145 JSHandle<JSTaggedValue> language; 146 JSHandle<JSTaggedValue> script; 147 JSHandle<JSTaggedValue> region; 148 }; 149 150 class JSLocale : public JSObject { 151 public: 152 static const std::set<std::string> WELL_NUMBER_SYSTEM; 153 static const std::set<std::string> WELL_COLLATION; 154 155 static const std::string LATN_STRING; 156 157 static const std::vector<LocaleMatcherOption> LOCALE_MATCHER_OPTION; 158 static const std::vector<std::string> LOCALE_MATCHER_OPTION_NAME; 159 160 static const std::map<std::string, std::set<std::string>> LOCALE_MAP; 161 162 static const std::vector<std::string> HOUR_CYCLE; 163 static const std::vector<std::string> CASE_FIRST; 164 static JSLocale *Cast(TaggedObject *object) 165 { 166 ASSERT(JSTaggedValue(object).IsJSLocale()); 167 return static_cast<JSLocale *>(object); 168 } 169 170 static constexpr size_t ICU_FIELD_OFFSET = JSObject::SIZE; 171 // icu::Locale internal slot. 172 ACCESSORS(IcuField, ICU_FIELD_OFFSET, SIZE) 173 174 DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, ICU_FIELD_OFFSET, SIZE) 175 DECL_DUMP() 176 177 icu::Locale *GetIcuLocale() const 178 { 179 ASSERT(GetIcuField().IsJSNativePointer()); 180 auto result = JSNativePointer::Cast(GetIcuField().GetTaggedObject())->GetExternalPointer(); 181 return reinterpret_cast<icu::Locale *>(result); 182 } 183 184 static void FreeIcuLocale([[maybe_unused]] void *env, void *pointer, void *data) 185 { 186 if (pointer == nullptr) { 187 return; 188 } 189 auto icuLocale = reinterpret_cast<icu::Locale *>(pointer); 190 icuLocale->~Locale(); 191 if (data != nullptr) { 192 reinterpret_cast<EcmaVM *>(data)->GetNativeAreaAllocator()->FreeBuffer(pointer); 193 } 194 } 195 196 // 6.2.4 DefaultLocale () 197 static JSHandle<EcmaString> DefaultLocale(JSThread *thread); 198 199 // 6.4.1 IsValidTimeZoneName ( timeZone ) 200 static bool IsValidTimeZoneName(const icu::TimeZone &tz); 201 202 // 9.2.3 LookupMatcher ( availableLocales, requestedLocales ) 203 static JSHandle<EcmaString> LookupMatcher(JSThread *thread, const JSHandle<TaggedArray> &availableLocales, 204 const JSHandle<TaggedArray> &requestedLocales); 205 206 // 9.2.4 BestFitMatcher ( availableLocales, requestedLocales ) 207 static JSHandle<EcmaString> BestFitMatcher(JSThread *thread, const JSHandle<TaggedArray> &availableLocales, 208 const JSHandle<TaggedArray> &requestedLocales); 209 210 // 9.2.5 UnicodeExtensionValue ( extension, key ) 211 static std::string UnicodeExtensionValue(const std::string extension, const std::string key); 212 213 // 9.2.7 ResolveLocale ( availableLocales, requestedLocales, options, relevantExtensionKeys, localeData ) 214 static ResolvedLocale ResolveLocale(JSThread *thread, const JSHandle<TaggedArray> &availableLocales, 215 const JSHandle<TaggedArray> &requestedLocales, 216 [[maybe_unused]] LocaleMatcherOption matcher, 217 const std::set<std::string> &relevantExtensionKeys); 218 219 // 9.2.8 LookupSupportedLocales ( availableLocales, requestedLocales ) 220 static JSHandle<TaggedArray> LookupSupportedLocales(JSThread *thread, const JSHandle<TaggedArray> &availableLocales, 221 const JSHandle<TaggedArray> &requestedLocales); 222 223 // 9.2.9 BestFitSupportedLocales ( availableLocales, requestedLocales ) 224 static JSHandle<TaggedArray> BestFitSupportedLocales(JSThread *thread, 225 const JSHandle<TaggedArray> &availableLocales, 226 const JSHandle<TaggedArray> &requestedLocales); 227 228 // 9.2.10 SupportedLocales ( availableLocales, requestedLocales, options ) 229 static JSHandle<JSArray> SupportedLocales(JSThread *thread, const JSHandle<TaggedArray> &availableLocales, 230 const JSHandle<TaggedArray> &requestedLocales, 231 const JSHandle<JSTaggedValue> &options); 232 233 // 9.2.11 GetOption ( options, property, type, values, fallback ) 234 template<typename T> GetOptionOfString(JSThread *thread, const JSHandle<JSObject> &options, const JSHandle<JSTaggedValue> &property, const std::vector<T> &enumValues, const std::vector<std::string> &strValues, T fallback)235 static T GetOptionOfString(JSThread *thread, const JSHandle<JSObject> &options, 236 const JSHandle<JSTaggedValue> &property, const std::vector<T> &enumValues, 237 const std::vector<std::string> &strValues, T fallback) 238 { 239 // 1. Let value be ? Get(options, property). 240 OperationResult operationResult = JSObject::GetProperty(thread, options, property); 241 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, T::EXCEPTION); 242 JSHandle<JSTaggedValue> value = operationResult.GetValue(); 243 244 if (value->IsUndefined()) { 245 return fallback; 246 } 247 248 // 2. If value is not undefined, then 249 // d. If values is not undefined, then 250 // i. If values does not contain an element equal to value, throw a RangeError exception. 251 JSHandle<EcmaString> valueEStr = JSTaggedValue::ToString(thread, value); 252 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, T::EXCEPTION); 253 std::string valueStr = std::string(ConvertToString(*valueEStr, StringConvertedUsage::LOGICOPERATION)); 254 int existIdx = -1; 255 if (!enumValues.empty()) { 256 size_t strValuesSize = strValues.size(); 257 for (size_t i = 0; i < strValuesSize; i++) { 258 if (strValues[i] == valueStr) { 259 existIdx = static_cast<int>(i); 260 } 261 } 262 if (existIdx == -1) { 263 THROW_RANGE_ERROR_AND_RETURN(thread, "getStringOption failed", T::EXCEPTION); 264 } 265 } 266 if (existIdx == -1) { 267 LOG_ECMA(FATAL) << "this branch is unreachable"; 268 UNREACHABLE(); 269 } 270 // e.Return value. 271 return enumValues[existIdx]; 272 } 273 274 static bool GetOptionOfBool(JSThread *thread, const JSHandle<JSObject> &options, 275 const JSHandle<JSTaggedValue> &property, bool fallback, bool *res); 276 277 static JSHandle<JSTaggedValue> GetOption(JSThread *thread, const JSHandle<JSObject> &options, 278 const JSHandle<JSTaggedValue> &property, OptionType type, 279 const JSHandle<JSTaggedValue> &values, 280 const JSHandle<JSTaggedValue> &fallback); 281 282 static bool GetOptionOfString(JSThread *thread, const JSHandle<JSObject> &options, 283 const JSHandle<JSTaggedValue> &property, const std::vector<std::string> &values, 284 std::string *optionValue); 285 286 // 9.2.12 DefaultNumberOption ( value, minimum, maximum, fallback ) 287 static int DefaultNumberOption(JSThread *thread, const JSHandle<JSTaggedValue> &value, int minimum, int maximum, 288 int fallback); 289 290 // 9.2.13 GetNumberOption ( options, property, minimum, maximum, fallback ) 291 static int GetNumberOption(JSThread *thread, const JSHandle<JSObject> &options, 292 const JSHandle<JSTaggedValue> &property, int minimum, int maximum, int fallback); 293 IsLanguageSubtag(const std::string &value)294 static bool IsLanguageSubtag(const std::string &value) 295 { 296 return IsAlpha(value, INTL_INDEX_TWO, INTL_INDEX_THREE) || IsAlpha(value, INTL_INDEX_FIVE, INTL_INDEX_EIGHT); 297 } 298 IsScriptSubtag(const std::string &value)299 static bool IsScriptSubtag(const std::string &value) 300 { 301 return IsAlpha(value, INTL_INDEX_FOUR, INTL_INDEX_FOUR); 302 } 303 IsRegionSubtag(const std::string &value)304 static bool IsRegionSubtag(const std::string &value) 305 { 306 return IsAlpha(value, INTL_INDEX_TWO, INTL_INDEX_TWO) || IsDigit(value, INTL_INDEX_THREE, INTL_INDEX_THREE); 307 } 308 IsVariantSubtag(const std::string &value)309 static bool IsVariantSubtag(const std::string &value) 310 { 311 return IsThirdDigitAlphanum(value) || IsAlphanum(value, INTL_INDEX_FIVE, INTL_INDEX_EIGHT); 312 } 313 IsThirdDigitAlphanum(const std::string &value)314 static bool IsThirdDigitAlphanum(const std::string &value) 315 { 316 return InRange(value[0], '0', '9') && value.length() == INTL_INDEX_FOUR && 317 IsAlphanum(value.substr(INTL_INDEX_ONE), INTL_INDEX_THREE, INTL_INDEX_THREE); 318 } 319 IsExtensionSingleton(const std::string &value)320 static bool IsExtensionSingleton(const std::string &value) 321 { 322 return IsAlphanum(value, INTL_INDEX_ONE, INTL_INDEX_ONE); 323 } 324 IsNormativeCalendar(const std::string &value)325 static bool IsNormativeCalendar(const std::string &value) 326 { 327 return IsWellAlphaNumList(value); 328 } 329 IsNormativeNumberingSystem(const std::string &value)330 static bool IsNormativeNumberingSystem(const std::string &value) 331 { 332 return IsWellAlphaNumList(value); 333 } 334 IsWellNumberingSystem(const std::string &value)335 static bool IsWellNumberingSystem(const std::string &value) 336 { 337 if (JSLocale::WELL_NUMBER_SYSTEM.find(value) != JSLocale::WELL_NUMBER_SYSTEM.end()) { 338 return false; 339 } 340 UErrorCode status = U_ZERO_ERROR; 341 icu::NumberingSystem *numberingSystem = icu::NumberingSystem::createInstanceByName(value.c_str(), status); 342 bool result = U_SUCCESS(status) != 0 && numberingSystem != nullptr; 343 delete numberingSystem; 344 numberingSystem = nullptr; 345 return result; 346 } 347 IsWellCollation(const icu::Locale &locale, const std::string &value)348 static bool IsWellCollation(const icu::Locale &locale, const std::string &value) 349 { 350 if (JSLocale::WELL_COLLATION.find(value) != JSLocale::WELL_COLLATION.end()) { 351 return false; 352 } 353 return IsWellExtension<icu::Collator>(locale, "collation", value); 354 } 355 IsWellCalendar(const icu::Locale &locale, const std::string &value)356 static bool IsWellCalendar(const icu::Locale &locale, const std::string &value) 357 { 358 return IsWellExtension<icu::Calendar>(locale, "calendar", value); 359 } 360 361 template<typename T> IsWellExtension(const icu::Locale &locale, const char *key, const std::string &value)362 static bool IsWellExtension(const icu::Locale &locale, const char *key, const std::string &value) 363 { 364 UErrorCode status = U_ZERO_ERROR; 365 const char *outdatedType = uloc_toLegacyType(key, value.c_str()); 366 if (outdatedType == nullptr) { 367 return false; 368 } 369 icu::StringEnumeration *sequence = T::getKeywordValuesForLocale(key, icu::Locale(locale.getBaseName()), 370 false, status); 371 if (U_FAILURE(status)) { 372 delete sequence; 373 sequence = nullptr; 374 return false; 375 } 376 int32_t size = 0; 377 const char *element = sequence->next(&size, status); 378 while (U_SUCCESS(status) && element != nullptr) { 379 if (strcmp(outdatedType, element) == 0) { 380 delete sequence; 381 sequence = nullptr; 382 return true; 383 } 384 element = sequence->next(&size, status); 385 } 386 delete sequence; 387 sequence = nullptr; 388 return false; 389 } 390 AsciiAlphaToLower(uint32_t c)391 static inline constexpr int AsciiAlphaToLower(uint32_t c) 392 { 393 constexpr uint32_t FLAG = 0x20; 394 return static_cast<int>(c | FLAG); 395 } 396 IsAsciiAlpha(char ch)397 static bool IsAsciiAlpha(char ch) 398 { 399 return InRange(ch, 'A', 'Z') || InRange(ch, 'a', 'z'); 400 } 401 LocaleIndependentAsciiToUpper(char ch)402 static char LocaleIndependentAsciiToUpper(char ch) 403 { 404 return (InRange(ch, 'a', 'z')) ? static_cast<char>((ch - 'a' + 'A')) : ch; 405 } 406 LocaleIndependentAsciiToLower(char ch)407 static char LocaleIndependentAsciiToLower(char ch) 408 { 409 return (InRange(ch, 'A', 'Z')) ? static_cast<char>((ch - 'A' + 'a')) : ch; 410 } 411 412 template<typename T, typename U> InRange(T value, U start, U end)413 static bool InRange(T value, U start, U end) 414 { 415 ASSERT(start <= end); 416 ASSERT(sizeof(T) >= sizeof(U)); 417 return (value >= static_cast<T>(start)) && (value <= static_cast<T>(end)); 418 } 419 IsWellAlphaNumList(const std::string &value)420 static bool IsWellAlphaNumList(const std::string &value) 421 { 422 if (value.length() < INTL_INDEX_THREE) { 423 return false; 424 } 425 char lastChar = value[value.length() - 1]; 426 if (lastChar == '-') { 427 return false; 428 } 429 std::vector<std::string> items; 430 std::istringstream input(value); 431 std::string temp; 432 while (getline(input, temp, '-')) { 433 items.push_back(temp); 434 } 435 for (auto &item : items) { 436 if (!IsAlphanum(item, INTL_INDEX_THREE, INTL_INDEX_EIGHT)) { 437 return false; 438 } 439 } 440 return true; 441 } 442 ValidateOtherTags(const icu::Locale &locale, const char *packageName, const char *key, bool &res)443 static bool ValidateOtherTags(const icu::Locale &locale, const char *packageName, const char *key, bool &res) 444 { 445 const char *localeCountry = locale.getCountry(); 446 const char *localeScript = locale.getScript(); 447 std::string removeCountry; 448 if (localeCountry[0] != '\0' && localeScript[0] != '\0') { 449 removeCountry = locale.getLanguage(); 450 removeCountry.append("-"); 451 removeCountry.append(localeScript); 452 return CheckLocales(removeCountry.c_str(), key, packageName, res); 453 } 454 if (localeCountry[0] != '\0' || localeScript[0] != '\0') { 455 std::string language = locale.getLanguage(); 456 return CheckLocales(language.c_str(), key, packageName, res); 457 } 458 return res; 459 } 460 CheckLocales(const icu::Locale &locale, const char *key, const char *packageName, bool &res)461 static bool CheckLocales(const icu::Locale &locale, const char *key, const char *packageName, bool &res) 462 { 463 res = false; 464 UErrorCode status = U_ZERO_ERROR; 465 const char *formalLocale = locale.getName(); 466 UResourceBundle *localeRes = ures_open(packageName, formalLocale, &status); 467 if (localeRes != nullptr && status == U_ZERO_ERROR) { 468 if (key == nullptr) { 469 res = true; 470 } else { 471 UResourceBundle *keyRes = ures_getByKey(localeRes, key, nullptr, &status); 472 if (keyRes != nullptr && status == U_ZERO_ERROR) { 473 res = true; 474 } 475 ures_close(keyRes); 476 } 477 } 478 ures_close(localeRes); 479 if (res) { 480 return res; 481 } else { 482 ValidateOtherTags(locale, packageName, key, res); 483 } 484 return res; 485 } 486 487 static std::vector<std::string> GetAvailableStringLocales(JSThread *thread, 488 const JSHandle<TaggedArray> &availableLocales); 489 490 static JSHandle<JSObject> PutElement(JSThread *thread, int index, const JSHandle<JSArray> &array, 491 const JSHandle<JSTaggedValue> &fieldTypeString, 492 const JSHandle<JSTaggedValue> &value); 493 494 static std::string GetNumberingSystem(const icu::Locale &icuLocale); 495 496 static bool IsWellFormedCurrencyCode(const std::string ¤cy); 497 498 static bool IsWellFormedCalendarCode(const std::string& calendar); 499 500 static JSHandle<JSTaggedValue> GetNumberFieldType(JSThread *thread, JSTaggedValue x, int32_t fieldId); 501 502 static bool ApplyOptionsToTag(JSThread *thread, const JSHandle<EcmaString> &tag, const JSHandle<JSObject> &options, 503 TagElements &tagElements); 504 505 static JSHandle<JSLocale> InitializeLocale(JSThread *thread, const JSHandle<JSLocale> &locale, 506 const JSHandle<EcmaString> &localeString, 507 const JSHandle<JSObject> &options); 508 509 static JSTaggedValue NormalizeKeywordValue(JSThread *thread, const JSHandle<JSLocale> &locale, 510 const std::string &key); 511 512 static JSHandle<EcmaString> ToString(JSThread *thread, const JSHandle<JSLocale> &locale); 513 514 // 12.1.1 SetNumberFormatDigitOptions ( intlObj, options, mnfdDefault, mxfdDefault, notation ) 515 template<typename T> SetNumberFormatDigitOptions(JSThread *thread, const JSHandle<T> &intlObj, const JSHandle<JSTaggedValue> &options, int mnfdDefault, int mxfdDefault, NotationOption notation)516 static void SetNumberFormatDigitOptions(JSThread *thread, const JSHandle<T> &intlObj, 517 const JSHandle<JSTaggedValue> &options, int mnfdDefault, int mxfdDefault, 518 NotationOption notation) 519 { 520 // 1. Assert: Type(intlObj) is Object. 521 // 2. Assert: Type(options) is Object. 522 // 3. Assert: Type(mnfdDefault) is Number. 523 // 4. Assert: Type(mxfdDefault) is Number. 524 ASSERT(options->IsHeapObject()); 525 auto globalConst = thread->GlobalConstants(); 526 // Set intlObj.[[MinimumFractionDigits]] to 0. 527 intlObj->SetMinimumFractionDigits(thread, JSTaggedValue(0)); 528 // Set intlObj.[[MaximumFractionDigits]] to 0. 529 intlObj->SetMaximumFractionDigits(thread, JSTaggedValue(0)); 530 // Set intlObj.[[MinimumSignificantDigits]] to 0. 531 intlObj->SetMinimumSignificantDigits(thread, JSTaggedValue(0)); 532 // Set intlObj.[[MaximumSignificantDigits]] to 0. 533 intlObj->SetMaximumSignificantDigits(thread, JSTaggedValue(0)); 534 535 // 5. Let mnid be ? GetNumberOption(options, "minimumIntegerDigits,", 1, 21, 1). 536 JSHandle<JSTaggedValue> mnidKey = globalConst->GetHandledMinimumIntegerDigitsString(); 537 int mnid = GetNumberOption(thread, JSHandle<JSObject>::Cast(options), mnidKey, 1, MAX_DIGITS, 1); 538 // 6. Let mnfd be ? Get(options, "minimumFractionDigits"). 539 JSHandle<JSTaggedValue> mnfdKey = globalConst->GetHandledMinimumFractionDigitsString(); 540 JSHandle<JSTaggedValue> mnfd = JSTaggedValue::GetProperty(thread, options, mnfdKey).GetValue(); 541 intlObj->SetMinimumIntegerDigits(thread, JSTaggedValue(mnid)); 542 RETURN_IF_ABRUPT_COMPLETION(thread); 543 // 7. Let mxfd be ? Get(options, "maximumFractionDigits"). 544 JSHandle<JSTaggedValue> mxfdKey = globalConst->GetHandledMaximumFractionDigitsString(); 545 JSHandle<JSTaggedValue> mxfd = JSTaggedValue::GetProperty(thread, options, mxfdKey).GetValue(); 546 RETURN_IF_ABRUPT_COMPLETION(thread); 547 // 8. Let mnsd be ? Get(options, "minimumSignificantDigits"). 548 JSHandle<JSTaggedValue> mnsdKey = globalConst->GetHandledMinimumSignificantDigitsString(); 549 JSHandle<JSTaggedValue> mnsd = JSTaggedValue::GetProperty(thread, options, mnsdKey).GetValue(); 550 RETURN_IF_ABRUPT_COMPLETION(thread); 551 // 9. Let mxsd be ? Get(options, "maximumSignificantDigits"). 552 JSHandle<JSTaggedValue> mxsdKey = globalConst->GetHandledMaximumSignificantDigitsString(); 553 JSHandle<JSTaggedValue> mxsd = JSTaggedValue::GetProperty(thread, options, mxsdKey).GetValue(); 554 RETURN_IF_ABRUPT_COMPLETION(thread); 555 556 // 10. Set intlObj.[[MinimumIntegerDigits]] to mnid. 557 // 11. If mnsd is not undefined or mxsd is not undefined, then 558 if (!mnsd->IsUndefined() || !mxsd->IsUndefined()) { 559 // a. Set intlObj.[[RoundingType]] to significantDigits. 560 intlObj->SetRoundingType(RoundingType::SIGNIFICANTDIGITS); 561 // b. Let mnsd be ? DefaultNumberOption(mnsd, 1, 21, 1). 562 mnsd = JSHandle<JSTaggedValue>( 563 thread, JSTaggedValue(JSLocale::DefaultNumberOption(thread, mnsd, 1, MAX_DIGITS, 1))); 564 RETURN_IF_ABRUPT_COMPLETION(thread); 565 // c. Let mxsd be ? DefaultNumberOption(mxsd, mnsd, 21, 21). 566 mxsd = JSHandle<JSTaggedValue>(thread, 567 JSTaggedValue(JSLocale::DefaultNumberOption(thread, mxsd, mnsd->GetInt(), MAX_DIGITS, MAX_DIGITS))); 568 RETURN_IF_ABRUPT_COMPLETION(thread); 569 // d. Set intlObj.[[MinimumSignificantDigits]] to mnsd. 570 intlObj->SetMinimumSignificantDigits(thread, mnsd); 571 // e. Set intlObj.[[MaximumSignificantDigits]] to mxsd. 572 intlObj->SetMaximumSignificantDigits(thread, mxsd); 573 } else { 574 if (!mnfd->IsUndefined() || !mxfd->IsUndefined()) { 575 // 12. Else if mnfd is not undefined or mxfd is not undefined, then 576 // a. Set intlObj.[[RoundingType]] to fractionDigits. 577 intlObj->SetRoundingType(RoundingType::FRACTIONDIGITS); 578 if (!mxfd->IsUndefined()) { 579 JSTaggedValue mxfdValue = 580 JSTaggedValue(JSLocale::DefaultNumberOption(thread, mxfd, 0, MAX_FRACTION_DIGITS, mxfdDefault)); 581 RETURN_IF_ABRUPT_COMPLETION(thread); 582 mxfd = JSHandle<JSTaggedValue>(thread, mxfdValue); 583 mnfdDefault = std::min(mnfdDefault, mxfd->GetInt()); 584 } 585 // b. Let mnfd be ? DefaultNumberOption(mnfd, 0, 20, mnfdDefault). 586 mnfd = JSHandle<JSTaggedValue>( 587 thread, JSTaggedValue(DefaultNumberOption(thread, mnfd, 0, MAX_FRACTION_DIGITS, mnfdDefault))); 588 // c. Let mxfdActualDefault be max( mnfd, mxfdDefault ). 589 int mxfdActualDefault = std::max(mnfd->GetInt(), mxfdDefault); 590 // d. Let mxfd be ? DefaultNumberOption(mxfd, mnfd, 20, mxfdActualDefault). 591 mxfd = JSHandle<JSTaggedValue>( 592 thread, JSTaggedValue(JSLocale::DefaultNumberOption(thread, mxfd, mnfd->GetInt(), 593 MAX_FRACTION_DIGITS, mxfdActualDefault))); 594 RETURN_IF_ABRUPT_COMPLETION(thread); 595 // e. Set intlObj.[[MinimumFractionDigits]] to mnfd. 596 intlObj->SetMinimumFractionDigits(thread, mnfd); 597 // f. Set intlObj.[[MaximumFractionDigits]] to mxfd. 598 intlObj->SetMaximumFractionDigits(thread, mxfd); 599 } else if (notation == NotationOption::COMPACT) { 600 // 13. Else if notation is "compact", then 601 // a. Set intlObj.[[RoundingType]] to compactRounding. 602 intlObj->SetRoundingType(RoundingType::COMPACTROUNDING); 603 } else { 604 // 14. else, 605 // a.Set intlObj.[[RoundingType]] to fractionDigits. 606 intlObj->SetRoundingType(RoundingType::FRACTIONDIGITS); 607 // b.Set intlObj.[[MinimumFractionDigits]] to mnfdDefault. 608 intlObj->SetMinimumFractionDigits(thread, JSTaggedValue(mnfdDefault)); 609 // c.Set intlObj.[[MaximumFractionDigits]] to mxfdDefault. 610 intlObj->SetMaximumFractionDigits(thread, JSTaggedValue(mxfdDefault)); 611 } 612 } 613 } 614 615 static JSHandle<TaggedArray> ConstructLocaleList(JSThread *thread, 616 const std::vector<std::string> &icuAvailableLocales); 617 618 static bool CheckLocales(const icu::Locale &locale, const char *path, const char *key); 619 IsPrivateSubTag(std::string result, size_t len)620 static bool IsPrivateSubTag(std::string result, size_t len) 621 { 622 if ((len > INTL_INDEX_ONE) && (result[INTL_INDEX_ONE] == '-')) { 623 ASSERT(result[INTL_INDEX_ZERO] == 'x' || result[INTL_INDEX_ZERO] == 'i'); 624 return true; 625 } 626 return false; 627 } 628 629 private: 630 static icu::Locale BuildICULocale(const std::string &bcp47Locale); 631 IsCheckRange(const std::string &str, size_t min, size_t max, bool(rangeCheckFunc)(char))632 static bool IsCheckRange(const std::string &str, size_t min, size_t max, bool(rangeCheckFunc)(char)) 633 { 634 if (!InRange(str.length(), min, max)) { 635 return false; 636 } 637 for (char i : str) { 638 if (!rangeCheckFunc(i)) { 639 return false; 640 } 641 } 642 return true; 643 } 644 IsAlpha(const std::string &str, size_t min, size_t max)645 static bool IsAlpha(const std::string &str, size_t min, size_t max) 646 { 647 if (!InRange(str.length(), min, max)) { 648 return false; 649 } 650 for (char c : str) { 651 if (!IsAsciiAlpha(c)) { 652 return false; 653 } 654 } 655 return true; 656 } 657 IsDigit(const std::string &str, size_t min, size_t max)658 static bool IsDigit(const std::string &str, size_t min, size_t max) 659 { 660 if (!InRange(str.length(), min, max)) { 661 return false; 662 } 663 for (char i : str) { 664 if (!InRange(i, '0', '9')) { 665 return false; 666 } 667 } 668 return true; 669 } 670 IsAlphanum(const std::string &str, size_t min, size_t max)671 static bool IsAlphanum(const std::string &str, size_t min, size_t max) 672 { 673 if (!InRange(str.length(), min, max)) { 674 return false; 675 } 676 for (char i : str) { 677 if (!IsAsciiAlpha(i) && !InRange(i, '0', '9')) { 678 return false; 679 } 680 } 681 return true; 682 } 683 IsAToZ(char ch)684 static bool IsAToZ(char ch) 685 { 686 int lowerCh = JSLocale::AsciiAlphaToLower(ch); 687 return JSLocale::InRange(lowerCh, 'a', 'z'); 688 } 689 }; 690 } // namespace panda::ecmascript 691 #endif // ECMASCRIPT_JSLOCALE_H 692