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