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