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