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