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