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 = locale.getLanguage(); 437 removeCountry.append("-"); 438 removeCountry.append(localeScript); 439 return CheckLocales(removeCountry.c_str(), key, packageName, res); 440 } 441 if (localeCountry[0] != '\0' || localeScript[0] != '\0') { 442 std::string language = locale.getLanguage(); 443 return CheckLocales(language.c_str(), key, packageName, res); 444 } 445 return res; 446 } 447 CheckLocales(const icu::Locale & locale,const char * key,const char * packageName,bool & res)448 static bool CheckLocales(const icu::Locale &locale, const char *key, const char *packageName, bool &res) 449 { 450 res = false; 451 UErrorCode status = U_ZERO_ERROR; 452 const char *formalLocale = locale.getName(); 453 UResourceBundle *localeRes = ures_open(packageName, formalLocale, &status); 454 if (localeRes != nullptr && status == U_ZERO_ERROR) { 455 if (key == nullptr) { 456 res = true; 457 } else { 458 UResourceBundle *keyRes = ures_getByKey(localeRes, key, nullptr, &status); 459 if (keyRes != nullptr && status == U_ZERO_ERROR) { 460 res = true; 461 } 462 ures_close(keyRes); 463 } 464 } 465 ures_close(localeRes); 466 if (res) { 467 return res; 468 } else { 469 ValidateOtherTags(locale, packageName, key, res); 470 } 471 return res; 472 } 473 474 static std::vector<std::string> GetAvailableStringLocales(JSThread *thread, 475 const JSHandle<TaggedArray> &availableLocales); 476 477 static JSHandle<JSObject> PutElement(JSThread *thread, int index, const JSHandle<JSArray> &array, 478 const JSHandle<JSTaggedValue> &fieldTypeString, 479 const JSHandle<JSTaggedValue> &value); 480 481 static std::string GetNumberingSystem(const icu::Locale &icuLocale); 482 483 static bool IsWellFormedCurrencyCode(const std::string ¤cy); 484 485 static JSHandle<JSTaggedValue> GetNumberFieldType(JSThread *thread, JSTaggedValue x, int32_t fieldId); 486 487 static bool ApplyOptionsToTag(JSThread *thread, const JSHandle<EcmaString> &tag, const JSHandle<JSObject> &options, 488 TagElements &tagElements); 489 490 static JSHandle<JSLocale> InitializeLocale(JSThread *thread, const JSHandle<JSLocale> &locale, 491 const JSHandle<EcmaString> &localeString, 492 const JSHandle<JSObject> &options); 493 494 static JSHandle<EcmaString> NormalizeKeywordValue(JSThread *thread, const JSHandle<JSLocale> &locale, 495 const std::string &key); 496 497 static JSHandle<EcmaString> ToString(JSThread *thread, const JSHandle<JSLocale> &locale); 498 499 // 12.1.1 SetNumberFormatDigitOptions ( intlObj, options, mnfdDefault, mxfdDefault, notation ) 500 template<typename T> SetNumberFormatDigitOptions(JSThread * thread,const JSHandle<T> & intlObj,const JSHandle<JSTaggedValue> & options,int mnfdDefault,int mxfdDefault,NotationOption notation)501 static void SetNumberFormatDigitOptions(JSThread *thread, const JSHandle<T> &intlObj, 502 const JSHandle<JSTaggedValue> &options, int mnfdDefault, int mxfdDefault, 503 NotationOption notation) 504 { 505 // 1. Assert: Type(intlObj) is Object. 506 // 2. Assert: Type(options) is Object. 507 // 3. Assert: Type(mnfdDefault) is Number. 508 // 4. Assert: Type(mxfdDefault) is Number. 509 ASSERT(options->IsHeapObject()); 510 auto globalConst = thread->GlobalConstants(); 511 // Set intlObj.[[MinimumFractionDigits]] to 0. 512 intlObj->SetMinimumFractionDigits(thread, JSTaggedValue(0)); 513 // Set intlObj.[[MaximumFractionDigits]] to 0. 514 intlObj->SetMaximumFractionDigits(thread, JSTaggedValue(0)); 515 // Set intlObj.[[MinimumSignificantDigits]] to 0. 516 intlObj->SetMinimumSignificantDigits(thread, JSTaggedValue(0)); 517 // Set intlObj.[[MaximumSignificantDigits]] to 0. 518 intlObj->SetMaximumSignificantDigits(thread, JSTaggedValue(0)); 519 520 // 5. Let mnid be ? GetNumberOption(options, "minimumIntegerDigits,", 1, 21, 1). 521 JSHandle<JSTaggedValue> mnidKey = globalConst->GetHandledMinimumIntegerDigitsString(); 522 int mnid = GetNumberOption(thread, JSHandle<JSObject>::Cast(options), mnidKey, 1, MAX_DIGITS, 1); 523 // 6. Let mnfd be ? Get(options, "minimumFractionDigits"). 524 JSHandle<JSTaggedValue> mnfdKey = globalConst->GetHandledMinimumFractionDigitsString(); 525 JSHandle<JSTaggedValue> mnfd = JSTaggedValue::GetProperty(thread, options, mnfdKey).GetValue(); 526 intlObj->SetMinimumIntegerDigits(thread, JSTaggedValue(mnid)); 527 RETURN_IF_ABRUPT_COMPLETION(thread); 528 // 7. Let mxfd be ? Get(options, "maximumFractionDigits"). 529 JSHandle<JSTaggedValue> mxfdKey = globalConst->GetHandledMaximumFractionDigitsString(); 530 JSHandle<JSTaggedValue> mxfd = JSTaggedValue::GetProperty(thread, options, mxfdKey).GetValue(); 531 RETURN_IF_ABRUPT_COMPLETION(thread); 532 // 8. Let mnsd be ? Get(options, "minimumSignificantDigits"). 533 JSHandle<JSTaggedValue> mnsdKey = globalConst->GetHandledMinimumSignificantDigitsString(); 534 JSHandle<JSTaggedValue> mnsd = JSTaggedValue::GetProperty(thread, options, mnsdKey).GetValue(); 535 RETURN_IF_ABRUPT_COMPLETION(thread); 536 // 9. Let mxsd be ? Get(options, "maximumSignificantDigits"). 537 JSHandle<JSTaggedValue> mxsdKey = globalConst->GetHandledMaximumSignificantDigitsString(); 538 JSHandle<JSTaggedValue> mxsd = JSTaggedValue::GetProperty(thread, options, mxsdKey).GetValue(); 539 RETURN_IF_ABRUPT_COMPLETION(thread); 540 541 // 10. Set intlObj.[[MinimumIntegerDigits]] to mnid. 542 // 11. If mnsd is not undefined or mxsd is not undefined, then 543 if (!mnsd->IsUndefined() || !mxsd->IsUndefined()) { 544 // a. Set intlObj.[[RoundingType]] to significantDigits. 545 intlObj->SetRoundingType(RoundingType::SIGNIFICANTDIGITS); 546 // b. Let mnsd be ? DefaultNumberOption(mnsd, 1, 21, 1). 547 mnsd = JSHandle<JSTaggedValue>( 548 thread, JSTaggedValue(JSLocale::DefaultNumberOption(thread, mnsd, 1, MAX_DIGITS, 1))); 549 // c. Let mxsd be ? DefaultNumberOption(mxsd, mnsd, 21, 21). 550 mxsd = JSHandle<JSTaggedValue>(thread, 551 JSTaggedValue(JSLocale::DefaultNumberOption(thread, mxsd, mnsd->GetInt(), MAX_DIGITS, MAX_DIGITS))); 552 // d. Set intlObj.[[MinimumSignificantDigits]] to mnsd. 553 intlObj->SetMinimumSignificantDigits(thread, mnsd); 554 // e. Set intlObj.[[MaximumSignificantDigits]] to mxsd. 555 intlObj->SetMaximumSignificantDigits(thread, mxsd); 556 } else { 557 if (!mnfd->IsUndefined() || !mxfd->IsUndefined()) { 558 // 12. Else if mnfd is not undefined or mxfd is not undefined, then 559 // a. Set intlObj.[[RoundingType]] to fractionDigits. 560 intlObj->SetRoundingType(RoundingType::FRACTIONDIGITS); 561 if (!mxfd->IsUndefined()) { 562 JSTaggedValue mxfdValue = 563 JSTaggedValue(JSLocale::DefaultNumberOption(thread, mxfd, 0, MAX_FRACTION_DIGITS, mxfdDefault)); 564 mxfd = JSHandle<JSTaggedValue>(thread, mxfdValue); 565 mnfdDefault = std::min(mnfdDefault, mxfd->GetInt()); 566 } 567 // b. Let mnfd be ? DefaultNumberOption(mnfd, 0, 20, mnfdDefault). 568 mnfd = JSHandle<JSTaggedValue>( 569 thread, JSTaggedValue(DefaultNumberOption(thread, mnfd, 0, MAX_FRACTION_DIGITS, mnfdDefault))); 570 // c. Let mxfdActualDefault be max( mnfd, mxfdDefault ). 571 int mxfdActualDefault = std::max(mnfd->GetInt(), mxfdDefault); 572 // d. Let mxfd be ? DefaultNumberOption(mxfd, mnfd, 20, mxfdActualDefault). 573 mxfd = JSHandle<JSTaggedValue>( 574 thread, JSTaggedValue(JSLocale::DefaultNumberOption(thread, mxfd, mnfd->GetInt(), 575 MAX_FRACTION_DIGITS, mxfdActualDefault))); 576 // e. Set intlObj.[[MinimumFractionDigits]] to mnfd. 577 intlObj->SetMinimumFractionDigits(thread, mnfd); 578 // f. Set intlObj.[[MaximumFractionDigits]] to mxfd. 579 intlObj->SetMaximumFractionDigits(thread, mxfd); 580 } else if (notation == NotationOption::COMPACT) { 581 // 13. Else if notation is "compact", then 582 // a. Set intlObj.[[RoundingType]] to compactRounding. 583 intlObj->SetRoundingType(RoundingType::COMPACTROUNDING); 584 } else { 585 // 14. else, 586 // a.Set intlObj.[[RoundingType]] to fractionDigits. 587 intlObj->SetRoundingType(RoundingType::FRACTIONDIGITS); 588 // b.Set intlObj.[[MinimumFractionDigits]] to mnfdDefault. 589 intlObj->SetMinimumFractionDigits(thread, JSTaggedValue(mnfdDefault)); 590 // c.Set intlObj.[[MaximumFractionDigits]] to mxfdDefault. 591 intlObj->SetMaximumFractionDigits(thread, JSTaggedValue(mxfdDefault)); 592 } 593 } 594 } 595 596 static JSHandle<TaggedArray> ConstructLocaleList(JSThread *thread, 597 const std::vector<std::string> &icuAvailableLocales); 598 599 static bool CheckLocales(const icu::Locale &locale, const char *path, const char *key); 600 IsPrivateSubTag(std::string result,size_t len)601 static bool IsPrivateSubTag(std::string result, size_t len) 602 { 603 if ((len > INTL_INDEX_ONE) && (result[INTL_INDEX_ONE] == '-')) { 604 ASSERT(result[INTL_INDEX_ZERO] == 'x' || result[INTL_INDEX_ZERO] == 'i'); 605 return true; 606 } 607 return false; 608 } 609 610 private: 611 static icu::Locale BuildICULocale(const std::string &bcp47Locale); 612 IsCheckRange(const std::string & str,size_t min,size_t max,bool (rangeCheckFunc)(char))613 static bool IsCheckRange(const std::string &str, size_t min, size_t max, bool(rangeCheckFunc)(char)) 614 { 615 if (!InRange(str.length(), min, max)) { 616 return false; 617 } 618 for (char i : str) { 619 if (!rangeCheckFunc(i)) { 620 return false; 621 } 622 } 623 return true; 624 } 625 IsAlpha(const std::string & str,size_t min,size_t max)626 static bool IsAlpha(const std::string &str, size_t min, size_t max) 627 { 628 if (!InRange(str.length(), min, max)) { 629 return false; 630 } 631 for (char c : str) { 632 if (!IsAsciiAlpha(c)) { 633 return false; 634 } 635 } 636 return true; 637 } 638 IsDigit(const std::string & str,size_t min,size_t max)639 static bool IsDigit(const std::string &str, size_t min, size_t max) 640 { 641 if (!InRange(str.length(), min, max)) { 642 return false; 643 } 644 for (char i : str) { 645 if (!InRange(i, '0', '9')) { 646 return false; 647 } 648 } 649 return true; 650 } 651 IsAlphanum(const std::string & str,size_t min,size_t max)652 static bool IsAlphanum(const std::string &str, size_t min, size_t max) 653 { 654 if (!InRange(str.length(), min, max)) { 655 return false; 656 } 657 for (char i : str) { 658 if (!IsAsciiAlpha(i) && !InRange(i, '0', '9')) { 659 return false; 660 } 661 } 662 return true; 663 } 664 IsAToZ(char ch)665 static bool IsAToZ(char ch) 666 { 667 int lowerCh = JSLocale::AsciiAlphaToLower(ch); 668 return JSLocale::InRange(lowerCh, 'a', 'z'); 669 } 670 }; 671 } // namespace panda::ecmascript 672 #endif // ECMASCRIPT_JSLOCALE_H 673