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