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