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