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