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 JSHandle<TaggedArray> &data, uint32_t length)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 
119 private:
120     std::vector<std::string> data_{};
121     uint32_t length_{0};
122     uint32_t curIdx_{0};
123     icu::Locale locale_{};
124 };
125 
126 struct ResolvedLocale {
127     std::string locale {};
128     icu::Locale localeData {};
129     std::map<std::string, std::string> extensions {};
130 };
131 
132 struct MatcherResult {
133     std::string locale;
134     std::string extension;
135 };
136 
137 struct OptionData {
138     std::string name;
139     std::string key;
140     std::vector<std::string> possibleValues;
141     bool isBoolValue = false;
142 };
143 
144 struct TagElements {
145     JSHandle<JSTaggedValue> language;
146     JSHandle<JSTaggedValue> script;
147     JSHandle<JSTaggedValue> region;
148 };
149 
150 class JSLocale : public JSObject {
151 public:
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>
GetOptionOfString(JSThread *thread, const JSHandle<JSObject> &options, const JSHandle<JSTaggedValue> &property, const std::vector<T> &enumValues, const std::vector<std::string> &strValues, T fallback)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 
IsLanguageSubtag(const std::string &value)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 
IsScriptSubtag(const std::string &value)299     static bool IsScriptSubtag(const std::string &value)
300     {
301         return IsAlpha(value, INTL_INDEX_FOUR, INTL_INDEX_FOUR);
302     }
303 
IsRegionSubtag(const std::string &value)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 
IsVariantSubtag(const std::string &value)309     static bool IsVariantSubtag(const std::string &value)
310     {
311         return IsThirdDigitAlphanum(value) || IsAlphanum(value, INTL_INDEX_FIVE, INTL_INDEX_EIGHT);
312     }
313 
IsThirdDigitAlphanum(const std::string &value)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 
IsExtensionSingleton(const std::string &value)320     static bool IsExtensionSingleton(const std::string &value)
321     {
322         return IsAlphanum(value, INTL_INDEX_ONE, INTL_INDEX_ONE);
323     }
324 
IsNormativeCalendar(const std::string &value)325     static bool IsNormativeCalendar(const std::string &value)
326     {
327         return IsWellAlphaNumList(value);
328     }
329 
IsNormativeNumberingSystem(const std::string &value)330     static bool IsNormativeNumberingSystem(const std::string &value)
331     {
332         return IsWellAlphaNumList(value);
333     }
334 
IsWellNumberingSystem(const std::string &value)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 
IsWellCollation(const icu::Locale &locale, const std::string &value)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 
IsWellCalendar(const icu::Locale &locale, const std::string &value)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>
IsWellExtension(const icu::Locale &locale, const char *key, const std::string &value)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 
AsciiAlphaToLower(uint32_t c)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 
IsAsciiAlpha(char ch)397     static bool IsAsciiAlpha(char ch)
398     {
399         return InRange(ch, 'A', 'Z') || InRange(ch, 'a', 'z');
400     }
401 
LocaleIndependentAsciiToUpper(char ch)402     static char LocaleIndependentAsciiToUpper(char ch)
403     {
404         return (InRange(ch, 'a', 'z')) ? static_cast<char>((ch - 'a' + 'A')) : ch;
405     }
406 
LocaleIndependentAsciiToLower(char ch)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>
InRange(T value, U start, U end)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 
IsWellAlphaNumList(const std::string &value)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 
ValidateOtherTags(const icu::Locale &locale, const char *packageName, const char *key, bool &res)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 
CheckLocales(const icu::Locale &locale, const char *key, const char *packageName, bool &res)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>
SetNumberFormatDigitOptions(JSThread *thread, const JSHandle<T> &intlObj, const JSHandle<JSTaggedValue> &options, int mnfdDefault, int mxfdDefault, NotationOption notation)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 
IsPrivateSubTag(std::string result, size_t len)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 
629 private:
630     static icu::Locale BuildICULocale(const std::string &bcp47Locale);
631 
IsCheckRange(const std::string &str, size_t min, size_t max, bool(rangeCheckFunc)(char))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 
IsAlpha(const std::string &str, size_t min, size_t max)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 
IsDigit(const std::string &str, size_t min, size_t max)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 
IsAlphanum(const std::string &str, size_t min, size_t max)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 
IsAToZ(char ch)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