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