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