• 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 #include "ecmascript/js_locale.h"
17 
18 #include "ecmascript/intl/locale_helper.h"
19 #include "ecmascript/base/string_helper.h"
20 #include "ecmascript/ecma_macros.h"
21 #include "ecmascript/ecma_vm.h"
22 #include "ecmascript/global_env.h"
23 #include "ecmascript/object_factory.h"
24 
25 #if defined(__clang__)
26 #pragma clang diagnostic push
27 #pragma clang diagnostic ignored "-Wshadow"
28 #elif defined(__GNUC__)
29 #pragma GCC diagnostic push
30 #pragma GCC diagnostic ignored "-Wshadow"
31 #endif
32 #include "unicode/localebuilder.h"
33 #if defined(__clang__)
34 #pragma clang diagnostic pop
35 #elif defined(__GNUC__)
36 #pragma GCC diagnostic pop
37 #endif
38 
39 #include "unicode/localematcher.h"
40 
41 namespace panda::ecmascript {
42 const std::string LATN_STRING = "latn";
43 // 6.4.1 IsValidTimeZoneName ( timeZone )
IsValidTimeZoneName(const icu::TimeZone & tz)44 bool JSLocale::IsValidTimeZoneName(const icu::TimeZone &tz)
45 {
46     UErrorCode status = U_ZERO_ERROR;
47     icu::UnicodeString id;
48     tz.getID(id);
49     icu::UnicodeString canonical;
50     icu::TimeZone::getCanonicalID(id, canonical, status);
51     UBool canonicalFlag = (canonical != icu::UnicodeString("Etc/Unknown", -1, US_INV));
52     return (U_SUCCESS(status) != 0) && (canonicalFlag != 0);
53 }
54 
55 // 9.2.3 LookupMatcher ( availableLocales, requestedLocales )
LookupMatcher(JSThread * thread,const JSHandle<TaggedArray> & availableLocales,const JSHandle<TaggedArray> & requestedLocales)56 JSHandle<EcmaString> JSLocale::LookupMatcher(JSThread *thread, const JSHandle<TaggedArray> &availableLocales,
57                                              const JSHandle<TaggedArray> &requestedLocales)
58 {
59     MatcherResult result = {std::string(), std::string()};
60     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
61     // 1. Let result be a new Record.
62     // 2. For each element locale of requestedLocales in List order, do
63     std::vector<std::string> availableStringLocales = GetAvailableStringLocales(thread, availableLocales);
64     uint32_t length = requestedLocales->GetLength();
65     JSMutableHandle<EcmaString> locale(thread, JSTaggedValue::Undefined());
66     for (uint32_t i = 0; i < length; ++i) {
67         locale.Update(requestedLocales->Get(thread, i));
68         // 2. a. Let noExtensionsLocale be the String value that is locale
69         //       with all Unicode locale extension sequences removed.
70         intl::LocaleHelper::ParsedLocale parsedResult = intl::LocaleHelper::HandleLocale(locale);
71         // 2. b. Let availableLocale be BestAvailableLocale(availableLocales, noExtensionsLocale).
72         std::string availableLocale =
73             intl::LocaleHelper::BestAvailableLocale(availableStringLocales, parsedResult.base);
74         // 2. c. If availableLocale is not undefined, append locale to the end of subset.
75         if (!availableLocale.empty()) {
76             result = {std::string(), std::string()};
77             // 2. c. i. Set result.[[locale]] to availableLocale.
78             result.locale = availableLocale;
79             // 2. c. ii. If locale and noExtensionsLocale are not the same String value, then
80             // 2. c. ii. 1. Let extension be the String value consisting of  the first substring of locale that is a
81             //              Unicode locale extension sequence.
82             if (!parsedResult.extension.empty()) {
83                 result.extension = parsedResult.extension;
84             }
85             // 2. c. ii. 2. Set result.[[extension]] to extension.
86             std::string res = result.locale + result.extension;
87             // 2. c. iii. Return result.
88             return factory->NewFromStdString(res);
89         }
90     }
91 
92     // 3. Let defLocale be DefaultLocale();
93     // 4. Set result.[[locale]] to defLocale.
94     // 5. Return result.
95     std::string defLocale = intl::LocaleHelper::ConvertToStdString(intl::LocaleHelper::DefaultLocale(thread));
96     result.locale = defLocale;
97     return factory->NewFromStdString(result.locale);
98 }
99 
BuildLocaleMatcher(JSThread * thread,uint32_t * availableLength,UErrorCode * status,const JSHandle<TaggedArray> & availableLocales)100 icu::LocaleMatcher BuildLocaleMatcher(JSThread *thread, uint32_t *availableLength, UErrorCode *status,
101                                       const JSHandle<TaggedArray> &availableLocales)
102 {
103     std::string locale = intl::LocaleHelper::ConvertToStdString(intl::LocaleHelper::DefaultLocale(thread));
104     icu::Locale defaultLocale = icu::Locale::forLanguageTag(locale, *status);
105     ASSERT_PRINT(U_SUCCESS(*status), "icu::Locale::forLanguageTag failed");
106     icu::LocaleMatcher::Builder builder;
107     builder.setDefaultLocale(&defaultLocale);
108     uint32_t length = availableLocales->GetLength();
109 
110     JSMutableHandle<EcmaString> item(thread, JSTaggedValue::Undefined());
111     for (*availableLength = 0; *availableLength < length; ++(*availableLength)) {
112         item.Update(availableLocales->Get(thread, *availableLength));
113         std::string itemStr = intl::LocaleHelper::ConvertToStdString(item);
114         icu::Locale localeForLanguageTag = icu::Locale::forLanguageTag(itemStr, *status);
115         if (U_SUCCESS(*status) != 0) {
116             builder.addSupportedLocale(localeForLanguageTag);
117         } else {
118             break;
119         }
120     }
121     *status = U_ZERO_ERROR;
122     return builder.build(*status);
123 }
124 
125 // 9.2.4 BestFitMatcher ( availableLocales, requestedLocales )
BestFitMatcher(JSThread * thread,const JSHandle<TaggedArray> & availableLocales,const JSHandle<TaggedArray> & requestedLocales)126 JSHandle<EcmaString> JSLocale::BestFitMatcher(JSThread *thread, const JSHandle<TaggedArray> &availableLocales,
127                                               const JSHandle<TaggedArray> &requestedLocales)
128 {
129     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
130     UErrorCode status = U_ZERO_ERROR;
131     uint32_t availableLength = availableLocales->GetLength();
132     icu::LocaleMatcher matcher = BuildLocaleMatcher(thread, &availableLength, &status, availableLocales);
133     ASSERT(U_SUCCESS(status));
134 
135     uint32_t requestedLocalesLength = requestedLocales->GetLength();
136     JSIntlIterator iter(requestedLocales, requestedLocalesLength);
137     auto bestFit = matcher.getBestMatch(iter, status)->toLanguageTag<std::string>(status);
138 
139     if (U_FAILURE(status) != 0) {
140         return intl::LocaleHelper::DefaultLocale(thread);
141     }
142 
143     for (uint32_t i = 0; i < requestedLocalesLength; ++i) {
144         if (iter[i] == bestFit) {
145             return JSHandle<EcmaString>(thread, requestedLocales->Get(thread, i));
146         }
147     }
148     return factory->NewFromStdString(bestFit);
149 }
150 
151 // 9.2.8 LookupSupportedLocales ( availableLocales, requestedLocales )
LookupSupportedLocales(JSThread * thread,const JSHandle<TaggedArray> & availableLocales,const JSHandle<TaggedArray> & requestedLocales)152 JSHandle<TaggedArray> JSLocale::LookupSupportedLocales(JSThread *thread, const JSHandle<TaggedArray> &availableLocales,
153                                                        const JSHandle<TaggedArray> &requestedLocales)
154 {
155     uint32_t index = 0;
156     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
157     uint32_t length = requestedLocales->GetLength();
158     // 1. Let subset be a new empty List.
159     JSHandle<TaggedArray> subset = factory->NewTaggedArray(length);
160     JSMutableHandle<EcmaString> item(thread, JSTaggedValue::Undefined());
161     std::vector<std::string> availableStringLocales = GetAvailableStringLocales(thread, availableLocales);
162     // 2. For each element locale of requestedLocales in List order, do
163     //    a. Let noExtensionsLocale be the String value that is locale with all Unicode locale extension sequences
164     //       removed.
165     //    b. Let availableLocale be BestAvailableLocale(availableLocales, noExtensionsLocale).
166     //    c. If availableLocale is not undefined, append locale to the end of subset.
167     for (uint32_t i = 0; i < length; ++i) {
168         item.Update(requestedLocales->Get(thread, i));
169         intl::LocaleHelper::ParsedLocale foundationResult = intl::LocaleHelper::HandleLocale(item);
170         std::string availableLocale =
171             intl::LocaleHelper::BestAvailableLocale(availableStringLocales, foundationResult.base);
172         if (!availableLocale.empty()) {
173             subset->Set(thread, index++, item.GetTaggedValue());
174         }
175     }
176     // 3. Return subset.
177     return TaggedArray::SetCapacity(thread, subset, index);
178 }
179 
180 // 9.2.9 BestFitSupportedLocales ( availableLocales, requestedLocales )
BestFitSupportedLocales(JSThread * thread,const JSHandle<TaggedArray> & availableLocales,const JSHandle<TaggedArray> & requestedLocales)181 JSHandle<TaggedArray> JSLocale::BestFitSupportedLocales(JSThread *thread, const JSHandle<TaggedArray> &availableLocales,
182                                                         const JSHandle<TaggedArray> &requestedLocales)
183 {
184     UErrorCode status = U_ZERO_ERROR;
185     uint32_t requestLength = requestedLocales->GetLength();
186     uint32_t availableLength = availableLocales->GetLength();
187     icu::LocaleMatcher matcher = BuildLocaleMatcher(thread, &availableLength, &status, availableLocales);
188     ASSERT(U_SUCCESS(status));
189 
190     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
191     JSHandle<EcmaString> defaultLocale = intl::LocaleHelper::DefaultLocale(thread);
192     JSHandle<TaggedArray> result = factory->NewTaggedArray(requestLength);
193 
194     uint32_t index = 0;
195     JSMutableHandle<EcmaString> locale(thread, JSTaggedValue::Undefined());
196     for (uint32_t i = 0; i < requestLength; ++i) {
197         locale.Update(requestedLocales->Get(thread, i));
198         if (EcmaStringAccessor::StringsAreEqual(thread->GetEcmaVM(), locale, defaultLocale)) {
199             result->Set(thread, index++, locale.GetTaggedValue());
200         } else {
201             status = U_ZERO_ERROR;
202             std::string localeStr = intl::LocaleHelper::ConvertToStdString(locale);
203             icu::Locale desired = icu::Locale::forLanguageTag(localeStr, status);
204             auto bestFit = matcher.getBestMatch(desired, status)->toLanguageTag<std::string>(status);
205             if ((U_SUCCESS(status) != 0) &&
206                 EcmaStringAccessor::StringsAreEqual(thread->GetEcmaVM(), locale, factory->NewFromStdString(bestFit))) {
207                 result->Set(thread, index++, locale.GetTaggedValue());
208             }
209         }
210     }
211     result = TaggedArray::SetCapacity(thread, result, index);
212     return result;
213 }
214 
215 // 9.2.10 SupportedLocales ( availableLocales, requestedLocales, options )
SupportedLocales(JSThread * thread,const JSHandle<TaggedArray> & availableLocales,const JSHandle<TaggedArray> & requestedLocales,const JSHandle<JSTaggedValue> & options)216 JSHandle<JSArray> JSLocale::SupportedLocales(JSThread *thread, const JSHandle<TaggedArray> &availableLocales,
217                                              const JSHandle<TaggedArray> &requestedLocales,
218                                              const JSHandle<JSTaggedValue> &options)
219 {
220     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
221     // 1. If options is not undefined, then
222     //    a. Let options be ? ToObject(options).
223     //    b. Let matcher be ? GetOption(options, "localeMatcher", "string", « "lookup", "best fit" », "best fit").
224     // 2. Else, let matcher be "best fit".
225     if (!options->IsUndefined()) {
226         JSHandle<JSObject> obj = JSTaggedValue::ToObject(thread, options);
227         RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread);
228 
229         [[maybe_unused]] LocaleMatcherOption matcher = GetOptionOfString<LocaleMatcherOption>(thread,
230             obj, globalConst->GetHandledLocaleMatcherString(),
231             {LocaleMatcherOption::LOOKUP, LocaleMatcherOption::BEST_FIT}, {"lookup", "best fit"},
232             LocaleMatcherOption::BEST_FIT);
233         RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread);
234     }
235 
236     // 3. If matcher is "best fit", then
237     //    a. Let supportedLocales be BestFitSupportedLocales(availableLocales, requestedLocales).
238     // 4. Else,
239     //    a. Let supportedLocales be LookupSupportedLocales(availableLocales, requestedLocales).
240     JSMutableHandle<TaggedArray> supportedLocales(thread, JSTaggedValue::Undefined());
241     supportedLocales.Update(LookupSupportedLocales(thread, availableLocales, requestedLocales).GetTaggedValue());
242 
243     JSHandle<JSArray> subset = JSArray::CreateArrayFromList(thread, supportedLocales);
244     // 5. Return CreateArrayFromList(supportedLocales).
245     return subset;
246 }
247 
248 // 9.2.11 GetOption ( options, property, type, values, fallback )
GetOption(JSThread * thread,const JSHandle<JSObject> & options,const JSHandle<JSTaggedValue> & property,OptionType type,const JSHandle<JSTaggedValue> & values,const JSHandle<JSTaggedValue> & fallback)249 JSHandle<JSTaggedValue> JSLocale::GetOption(JSThread *thread, const JSHandle<JSObject> &options,
250                                             const JSHandle<JSTaggedValue> &property, OptionType type,
251                                             const JSHandle<JSTaggedValue> &values,
252                                             const JSHandle<JSTaggedValue> &fallback)
253 {
254     // 1. Let value be ? Get(options, property).
255     JSHandle<JSTaggedValue> value = JSObject::GetProperty(thread, options, property).GetValue();
256     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
257 
258     // 2. If value is not undefined, then
259     if (!value->IsUndefined()) {
260         // a. Assert: type is "boolean" or "string".
261         ASSERT_PRINT(type == OptionType::BOOLEAN || type == OptionType::STRING, "type is not boolean or string");
262 
263         // b. If type is "boolean", then
264         //    i. Let value be ToBoolean(value).
265         if (type == OptionType::BOOLEAN) {
266             value = JSHandle<JSTaggedValue>(thread, JSTaggedValue(value->ToBoolean()));
267         }
268         // c. If type is "string", then
269         //    i. Let value be ? ToString(value).
270         if (type == OptionType::STRING) {
271             JSHandle<EcmaString> str = JSTaggedValue::ToString(thread, value);
272             RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
273             value = JSHandle<JSTaggedValue>(thread, str.GetTaggedValue());
274         }
275 
276         // d. If values is not undefined, then
277         //    i. If values does not contain an element equal to value, throw a RangeError exception.
278         if (!values->IsUndefined()) {
279             bool isExist = false;
280             JSHandle<TaggedArray> valuesArray = JSHandle<TaggedArray>::Cast(values);
281             uint32_t length = valuesArray->GetLength();
282             for (uint32_t i = 0; i < length; i++) {
283                 if (JSTaggedValue::SameValue(valuesArray->Get(thread, i), value.GetTaggedValue())) {
284                     isExist = true;
285                 }
286             }
287             if (!isExist) {
288                 JSHandle<JSTaggedValue> exception(thread, JSTaggedValue::Exception());
289                 THROW_RANGE_ERROR_AND_RETURN(thread, "values does not contain an element equal to value", exception);
290             }
291         }
292         // e. Return value.
293         return value;
294     }
295     // 3. Else, return fallback.
296     return fallback;
297 }
298 
GetOptionOfString(JSThread * thread,const JSHandle<JSObject> & options,const JSHandle<JSTaggedValue> & property,const std::vector<std::string> & values,std::string * optionValue)299 bool JSLocale::GetOptionOfString(JSThread *thread, const JSHandle<JSObject> &options,
300                                  const JSHandle<JSTaggedValue> &property, const std::vector<std::string> &values,
301                                  std::string *optionValue)
302 {
303     // 1. Let value be ? Get(options, property).
304     OperationResult operationResult = JSObject::GetProperty(thread, options, property);
305     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
306     JSHandle<JSTaggedValue> value = operationResult.GetValue();
307     // 2. If value is not undefined, then
308     if (value->IsUndefined()) {
309         return false;
310     }
311     //    c. If type is "string" "string", then
312     //       i. Let value be ? ToString(value).
313     JSHandle<EcmaString> valueEStr = JSTaggedValue::ToString(thread, value);
314     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
315     if (EcmaStringAccessor(valueEStr).IsUtf16()) {
316         THROW_RANGE_ERROR_AND_RETURN(thread, "Value out of range for locale options property", false);
317     }
318     *optionValue = intl::LocaleHelper::ConvertToStdString(valueEStr);
319     if (values.empty()) {
320         return true;
321     }
322     // d. If values is not undefined, then
323     //    i. If values does not contain an element equal to value, throw a RangeError exception.
324     for (const auto &item : values) {
325         if (item == *optionValue) {
326             return true;
327         }
328     }
329     THROW_RANGE_ERROR_AND_RETURN(thread, "Value out of range for locale options property", false);
330 }
331 
332 // 9.2.12 DefaultNumberOption ( value, minimum, maximum, fallback )
DefaultNumberOption(JSThread * thread,const JSHandle<JSTaggedValue> & value,int minimum,int maximum,int fallback)333 int JSLocale::DefaultNumberOption(JSThread *thread, const JSHandle<JSTaggedValue> &value, int minimum, int maximum,
334                                   int fallback)
335 {
336     // 1. If value is not undefined, then
337     if (!value->IsUndefined()) {
338         // a. Let value be ? ToNumber(value).
339         JSTaggedNumber number = JSTaggedValue::ToNumber(thread, value);
340         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, fallback);
341         // b. If value is NaN or less than minimum or greater than maximum, throw a RangeError exception.
342         double num = JSTaggedValue(number).GetNumber();
343         if (std::isnan(num) || num < minimum || num > maximum) {
344             THROW_RANGE_ERROR_AND_RETURN(thread, "", fallback);
345         }
346         // c. Return floor(value).
347         return std::floor(num);
348     }
349     // 2. Else, return fallback.
350     return fallback;
351 }
352 
353 // 9.2.13 GetNumberOption ( options, property, minimum, maximum, fallback )
GetNumberOption(JSThread * thread,const JSHandle<JSObject> & options,const JSHandle<JSTaggedValue> & property,int min,int max,int fallback)354 int JSLocale::GetNumberOption(JSThread *thread, const JSHandle<JSObject> &options,
355                               const JSHandle<JSTaggedValue> &property, int min, int max, int fallback)
356 {
357     // 1. Let value be ? Get(options, property).
358     JSHandle<JSTaggedValue> value = JSObject::GetProperty(thread, options, property).GetValue();
359     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, fallback);
360 
361     // 2. Return ? DefaultNumberOption(value, minimum, maximum, fallback).
362     int result = DefaultNumberOption(thread, value, min, max, fallback);
363     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, fallback);
364     return result;
365 }
366 
367 // 9.2.5 UnicodeExtensionValue ( extension, key )
UnicodeExtensionValue(const std::string extension,const std::string key)368 std::string JSLocale::UnicodeExtensionValue(const std::string extension, const std::string key)
369 {
370     // 1. Assert: The number of elements in key is 2.
371     // 2. Let size be the number of elements in extension.
372     ASSERT(key.size() == INTL_INDEX_TWO);
373     size_t size = extension.size();
374     // 3. Let searchValue be the concatenation of "-" , key, and "-".
375     std::string searchValue = "-" + key + "-";
376     // 4. Let pos be Call(%StringProto_indexOf%, extension, « searchValue »).
377     size_t pos = extension.find(searchValue);
378     // 5. If pos ≠ -1, then
379     if (pos != std::string::npos) {
380         // a. Let start be pos + 4.
381         size_t start = pos + INTL_INDEX_FOUR;
382         // b. Let end be start.
383         size_t end = start;
384         // c. Let k be start.
385         size_t k = start;
386         // d. Let done be false.
387         bool done = false;
388         // e. Repeat, while done is false
389         while (!done) {
390             // i. Let e be Call(%StringProto_indexOf%, extension, « "-", k »).
391             size_t e = extension.find("-", k);
392             size_t len;
393             // ii. If e = -1, let len be size - k; else let len be e - k.
394             if (e == std::string::npos) {
395                 len = size - k;
396             } else {
397                 len = e - k;
398             }
399             // iii. If len = 2, then
400             //     1. Let done be true.
401             if (len == INTL_INDEX_TWO) {
402                 done = true;
403             // iv. Else if e = -1, then
404             //    1. Let end be size.
405             //    2. Let done be true.
406             } else if (e == std::string::npos) {
407                 end = size;
408                 done = true;
409             // v. Else,
410             //   1. Let end be e.
411             //   2. Let k be e + 1.
412             } else {
413                 end = e;
414                 k = e + INTL_INDEX_ONE;
415             }
416         }
417         // f. Return the String value equal to the substring of extension consisting of the code units at indices.
418         // start (inclusive) through end (exclusive).
419         std::string result = extension.substr(start, end - start);
420         return result;
421     }
422     // 6. Let searchValue be the concatenation of "-" and key.
423     searchValue = "-" + key;
424     // 7. Let pos be Call(%StringProto_indexOf%, extension, « searchValue »).
425     pos = extension.find(searchValue);
426     // 8. If pos ≠ -1 and pos + 3 = size, then
427     //    a. Return the empty String.
428     if (pos != std::string::npos && pos + INTL_INDEX_THREE == size) {
429         return "";
430     }
431     // 9. Return undefined.
432     return "undefined";
433 }
434 
ResolveLocale(JSThread * thread,const JSHandle<TaggedArray> & availableLocales,const JSHandle<TaggedArray> & requestedLocales,LocaleMatcherOption matcher,const std::set<std::string> & relevantExtensionKeys)435 ResolvedLocale JSLocale::ResolveLocale(JSThread *thread, const JSHandle<TaggedArray> &availableLocales,
436                                        const JSHandle<TaggedArray> &requestedLocales,
437                                        [[maybe_unused]] LocaleMatcherOption matcher,
438                                        const std::set<std::string> &relevantExtensionKeys)
439 {
440     std::map<std::string, std::set<std::string>> localeMap = {
441         {"hc", {"h11", "h12", "h23", "h24"}},
442         {"lb", {"strict", "normal", "loose"}},
443         {"kn", {"true", "false"}},
444         {"kf", {"upper", "lower", "false"}}
445     };
446 
447     // 1. Let matcher be options.[[localeMatcher]].
448     // 2. If matcher is "lookup" "lookup", then
449     //    a. Let r be LookupMatcher(availableLocales, requestedLocales).
450     // 3. Else,
451     //    a. Let r be BestFitMatcher(availableLocales, requestedLocales).
452     JSMutableHandle<EcmaString> locale(thread, JSTaggedValue::Undefined());
453     if (availableLocales->GetLength() == 0 && requestedLocales->GetLength() == 0) {
454         locale.Update(intl::LocaleHelper::DefaultLocale(thread).GetTaggedValue());
455     } else {
456         locale.Update(LookupMatcher(thread, availableLocales, requestedLocales).GetTaggedValue());
457     }
458 
459     // 4. Let foundLocale be r.[[locale]].
460     // 5. Let result be a new Record.
461     // 6. Set result.[[dataLocale]] to foundLocale.
462     // 7. Let supportedExtension be "-u".
463     std::string foundLocale = intl::LocaleHelper::ConvertToStdString(locale);
464     icu::Locale foundLocaleData = BuildICULocale(foundLocale);
465     ResolvedLocale result;
466     result.localeData = foundLocaleData;
467     JSHandle<EcmaString> tag = intl::LocaleHelper::ToLanguageTag(thread, foundLocaleData);
468     result.locale = intl::LocaleHelper::ConvertToStdString(tag);
469     std::string supportedExtension = "-u";
470     icu::LocaleBuilder localeBuilder;
471     localeBuilder.setLocale(foundLocaleData).clearExtensions();
472     // 8. For each element key of relevantExtensionKeys in List order, do
473     for (auto &key : relevantExtensionKeys) {
474         auto doubleMatch = foundLocale.find(key);
475         if (doubleMatch == std::string::npos) {
476             continue;
477         }
478         UErrorCode status = U_ZERO_ERROR;
479         std::set<std::string> keyLocaleData;
480         std::unique_ptr<icu::StringEnumeration> wellFormKey(foundLocaleData.createKeywords(status));
481         if (U_FAILURE(status) != 0) {
482             return result;
483         }
484         if (!wellFormKey) {
485             return result;
486         }
487         std::string value;
488 
489         // c. Let keyLocaleData be foundLocaleData.[[<key>]].
490         // e. Let value be keyLocaleData[0].
491         if ((key != "ca") && (key != "co") && (key != "nu")) {
492             keyLocaleData = localeMap[key];
493             value = *keyLocaleData.begin();
494         }
495 
496         // g. Let supportedExtensionAddition be "".
497         // h. If r has an [[extension]] field, then
498         std::string supportedExtensionAddition;
499         size_t found = foundLocale.find("-u-");
500         if (found != std::string::npos) {
501             std::string extension = foundLocale.substr(found + INTL_INDEX_ONE);
502 
503             // i. Let requestedValue be UnicodeExtensionValue(r.[[extension]], key).
504             std::string requestedValue = UnicodeExtensionValue(extension, key);
505             if (key == "kn" && requestedValue.empty()) {
506                 requestedValue = "true";
507             }
508 
509             // ii. If requestedValue is not undefined, then
510             if (requestedValue != "undefined") {
511                 // 1. If requestedValue is not the empty String, then
512                 if (!requestedValue.empty()) {
513                     // a. If keyLocaleData contains requestedValue, then
514                     //    i. Let value be requestedValue.
515                     //    ii. Let supportedExtensionAddition be the concatenation of "-", key, "-", and value.
516                     if (key == "ca" || key == "co") {
517                         if (key == "co") {
518                             bool isValidValue = IsWellCollation(foundLocaleData, requestedValue);
519                             if (!isValidValue) {
520                                 continue;
521                             }
522                             value = requestedValue;
523                             supportedExtensionAddition = "-" + key + "-" + value;
524                             localeBuilder.setUnicodeLocaleKeyword(key, requestedValue);
525                         } else {
526                             bool isValidValue = IsWellCalendar(foundLocaleData, requestedValue);
527                             if (!isValidValue) {
528                                 continue;
529                             }
530                             value = requestedValue;
531                             supportedExtensionAddition = "-" + key + "-" + value;
532                             localeBuilder.setUnicodeLocaleKeyword(key, requestedValue);
533                         }
534                     } else if (key == "nu") {
535                         bool isValidValue = IsWellNumberingSystem(requestedValue);
536                         if (!isValidValue) {
537                             continue;
538                         }
539                         value = requestedValue;
540                         supportedExtensionAddition = "-" + key + "-" + value;
541                         localeBuilder.setUnicodeLocaleKeyword(key, requestedValue);
542                     } else if (keyLocaleData.find(requestedValue) != keyLocaleData.end()) {
543                         value = requestedValue;
544                         supportedExtensionAddition = "-" + key + "-" + value;
545                         localeBuilder.setUnicodeLocaleKeyword(key, requestedValue);
546                     }
547                 }
548             }
549         }
550         result.extensions.emplace(key, value);
551         supportedExtension += supportedExtensionAddition;
552     }
553     size_t found = foundLocale.find("-u-");
554     if (found != std::string::npos) {
555         foundLocale = foundLocale.substr(0, found);
556     }
557 
558     // 9. If the number of elements in supportedExtension is greater than 2, then
559     if (supportedExtension.size() > 2) {
560         // a. Let privateIndex be Call(%StringProto_indexOf%, foundLocale, « "-x-" »).
561         size_t privateIndex = foundLocale.find("-x-");
562         // b. If privateIndex = -1, then
563         //    i. Let foundLocale be the concatenation of foundLocale and supportedExtension.
564         if (privateIndex == std::string::npos) {
565             foundLocale = foundLocale + supportedExtension;
566         } else {
567             std::string preExtension = foundLocale.substr(0, privateIndex);
568             std::string postExtension = foundLocale.substr(privateIndex);
569             foundLocale = preExtension + supportedExtension + postExtension;
570         }
571 
572         tag = intl::LocaleHelper::ToLanguageTag(thread, foundLocaleData);
573         if (!intl::LocaleHelper::IsStructurallyValidLanguageTag(tag)) {
574             result.extensions.erase(result.extensions.begin(), result.extensions.end());
575             result.locale = foundLocale;
576         }
577         tag = intl::LocaleHelper::CanonicalizeUnicodeLocaleId(thread, tag);
578         foundLocale = intl::LocaleHelper::ConvertToStdString(tag);
579     }
580 
581     // 10. Set result.[[locale]] to foundLocale.
582     result.locale = foundLocale;
583     UErrorCode status = U_ZERO_ERROR;
584     foundLocaleData = localeBuilder.build(status);
585     result.localeData = foundLocaleData;
586 
587     // 11. Return result.
588     return result;
589 }
590 
BuildICULocale(const std::string & bcp47Locale)591 icu::Locale JSLocale::BuildICULocale(const std::string &bcp47Locale)
592 {
593     UErrorCode status = U_ZERO_ERROR;
594     icu::Locale icuLocale = icu::Locale::forLanguageTag(bcp47Locale, status);
595     ASSERT_PRINT(U_SUCCESS(status), "forLanguageTag failed");
596     ASSERT_PRINT(!icuLocale.isBogus(), "icuLocale is bogus");
597     return icuLocale;
598 }
599 
ConstructLocaleList(JSThread * thread,const std::vector<std::string> & icuAvailableLocales)600 JSHandle<TaggedArray> JSLocale::ConstructLocaleList(JSThread *thread,
601                                                     const std::vector<std::string> &icuAvailableLocales)
602 {
603     EcmaVM *ecmaVm = thread->GetEcmaVM();
604     ObjectFactory *factory = ecmaVm->GetFactory();
605     JSHandle<TaggedArray> locales = factory->NewTaggedArray(icuAvailableLocales.size());
606     int32_t index = 0;
607     for (const std::string &locale : icuAvailableLocales) {
608         JSHandle<EcmaString> localeStr = factory->NewFromStdString(locale);
609         locales->Set(thread, index++, localeStr);
610     }
611     return locales;
612 }
613 
GetNumberingSystem(const icu::Locale & icuLocale)614 std::string JSLocale::GetNumberingSystem(const icu::Locale &icuLocale)
615 {
616     UErrorCode status = U_ZERO_ERROR;
617     std::unique_ptr<icu::NumberingSystem> numberingSystem(icu::NumberingSystem::createInstance(icuLocale, status));
618     if (U_SUCCESS(status) != 0) {
619         return numberingSystem->getName();
620     }
621     return LATN_STRING;
622 }
623 
IsWellFormedCurrencyCode(const std::string & currency)624 bool JSLocale::IsWellFormedCurrencyCode(const std::string &currency)
625 {
626     if (currency.length() != INTL_INDEX_THREE) {
627         return false;
628     }
629     return (IsAToZ(currency[INTL_INDEX_ZERO]) && IsAToZ(currency[INTL_INDEX_ONE]) && IsAToZ(currency[INTL_INDEX_TWO]));
630 }
631 
PutElement(JSThread * thread,int index,const JSHandle<JSArray> & array,const JSHandle<JSTaggedValue> & fieldTypeString,const JSHandle<JSTaggedValue> & value)632 JSHandle<JSObject> JSLocale::PutElement(JSThread *thread, int index, const JSHandle<JSArray> &array,
633                                         const JSHandle<JSTaggedValue> &fieldTypeString,
634                                         const JSHandle<JSTaggedValue> &value)
635 {
636     auto ecmaVm = thread->GetEcmaVM();
637     ObjectFactory *factory = ecmaVm->GetFactory();
638 
639     // Let record be ! ObjectCreate(%ObjectPrototype%).
640     JSHandle<JSObject> record = factory->NewEmptyJSObject();
641 
642     auto globalConst = thread->GlobalConstants();
643     // obj.type = field_type_string
644     JSObject::CreateDataPropertyOrThrow(thread, record, globalConst->GetHandledTypeString(), fieldTypeString);
645     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread);
646     // obj.value = value
647     JSObject::CreateDataPropertyOrThrow(thread, record, globalConst->GetHandledValueString(), value);
648     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread);
649 
650     JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>::Cast(array), index,
651                                JSHandle<JSTaggedValue>::Cast(record), true);
652     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread);
653     return record;
654 }
655 
656 // 9.2.11 GetOption ( options, property, type, values, fallback )
GetOptionOfBool(JSThread * thread,const JSHandle<JSObject> & options,const JSHandle<JSTaggedValue> & property,bool fallback,bool * res)657 bool JSLocale::GetOptionOfBool(JSThread *thread, const JSHandle<JSObject> &options,
658                                const JSHandle<JSTaggedValue> &property, bool fallback, bool *res)
659 {
660     // 1. Let value be ? Get(options, property).
661     OperationResult operationResult = JSObject::GetProperty(thread, options, property);
662     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
663     JSHandle<JSTaggedValue> value = operationResult.GetValue();
664     *res = fallback;
665     // 2. If value is not undefined, then
666     if (!value->IsUndefined()) {
667         // b. Let value be ToBoolean(value).
668         *res = value->ToBoolean();
669         return true;
670     }
671     // 3. not found
672     return false;
673 }
674 
GetNumberFieldType(JSThread * thread,JSTaggedValue x,int32_t fieldId)675 JSHandle<JSTaggedValue> JSLocale::GetNumberFieldType(JSThread *thread, JSTaggedValue x, int32_t fieldId)
676 {
677     ASSERT(x.IsNumber());
678     double number = 0;
679     auto globalConst = thread->GlobalConstants();
680     if (static_cast<UNumberFormatFields>(fieldId) == UNUM_INTEGER_FIELD) {
681         number = x.GetNumber();
682         if (std::isfinite(number)) {
683             return globalConst->GetHandledIntegerString();
684         }
685         if (std::isnan(number)) {
686             return globalConst->GetHandledNanString();
687         }
688         return globalConst->GetHandledInfinityString();
689     } else if (static_cast<UNumberFormatFields>(fieldId) == UNUM_FRACTION_FIELD) {
690         return globalConst->GetHandledFractionString();
691     } else if (static_cast<UNumberFormatFields>(fieldId) == UNUM_DECIMAL_SEPARATOR_FIELD) {
692         return globalConst->GetHandledDecimalString();
693     } else if (static_cast<UNumberFormatFields>(fieldId) == UNUM_GROUPING_SEPARATOR_FIELD) {
694         return globalConst->GetHandledGroupString();
695     } else if (static_cast<UNumberFormatFields>(fieldId) == UNUM_CURRENCY_FIELD) {
696         return globalConst->GetHandledCurrencyString();
697     } else if (static_cast<UNumberFormatFields>(fieldId) == UNUM_PERCENT_FIELD) {
698         return globalConst->GetHandledPercentSignString();
699     } else if (static_cast<UNumberFormatFields>(fieldId) == UNUM_SIGN_FIELD) {
700         number = x.GetNumber();
701         return std::signbit(number) ? globalConst->GetHandledMinusSignString()
702                                     : globalConst->GetHandledPlusSignString();
703     } else if (static_cast<UNumberFormatFields>(fieldId) == UNUM_EXPONENT_SYMBOL_FIELD) {
704         return globalConst->GetHandledExponentSeparatorString();
705     } else if (static_cast<UNumberFormatFields>(fieldId) == UNUM_EXPONENT_SIGN_FIELD) {
706         return globalConst->GetHandledExponentMinusSignString();
707     } else if (static_cast<UNumberFormatFields>(fieldId) == UNUM_EXPONENT_FIELD) {
708         return globalConst->GetHandledExponentIntegerString();
709     } else if (static_cast<UNumberFormatFields>(fieldId) == UNUM_COMPACT_FIELD) {
710         return globalConst->GetHandledCompactString();
711     } else if (static_cast<UNumberFormatFields>(fieldId) == UNUM_MEASURE_UNIT_FIELD) {
712         return globalConst->GetHandledUnitString();
713     } else {
714         LOG_ECMA(FATAL) << "this branch is unreachable";
715         UNREACHABLE();
716     }
717 }
718 
719 // 10.1.1 ApplyOptionsToTag( tag, options )
ApplyOptionsToTag(JSThread * thread,const JSHandle<EcmaString> & tag,const JSHandle<JSObject> & options,TagElements & tagElements)720 bool JSLocale::ApplyOptionsToTag(JSThread *thread, const JSHandle<EcmaString> &tag, const JSHandle<JSObject> &options,
721                                  TagElements &tagElements)
722 {
723     EcmaVM *ecmaVm = thread->GetEcmaVM();
724     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
725     ObjectFactory *factory = ecmaVm->GetFactory();
726     if (*tag == *(factory->GetEmptyString())) {
727         return false;
728     }
729     // 2. If intl::LocaleHelper::IsStructurallyValidLanguageTag(tag) is false, throw a RangeError exception.
730     if (!intl::LocaleHelper::IsStructurallyValidLanguageTag(tag)) {
731         return false;
732     }
733 
734     tagElements.language =
735         GetOption(thread, options, globalConst->GetHandledLanguageString(), OptionType::STRING,
736                   globalConst->GetHandledUndefined(), globalConst->GetHandledUndefined());
737     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
738 
739     // 4. If language is not undefined, then
740     //    a. If language does not match the unicode_language_subtag production, throw a RangeError exception.
741     if (!tagElements.language->IsUndefined()) {
742         std::string languageStr =
743             intl::LocaleHelper::ConvertToStdString(JSHandle<EcmaString>::Cast(tagElements.language));
744         if (languageStr[INTL_INDEX_ZERO] == '\0' ||
745             IsAlpha(languageStr, INTL_INDEX_FOUR, INTL_INDEX_FOUR)) {
746             return false;
747         }
748     }
749 
750     // 5. Let script be ? GetOption(options, "script", "string", undefined, undefined).
751     tagElements.script =
752         GetOption(thread, options, globalConst->GetHandledScriptString(), OptionType::STRING,
753                   globalConst->GetHandledUndefined(), globalConst->GetHandledUndefined());
754     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
755 
756     // 6. If script is not undefined, then
757     //    a. If script does not match the unicode_script_subtag production, throw a RangeError exception.
758     if (!tagElements.script->IsUndefined()) {
759         std::string scriptStr =
760             intl::LocaleHelper::ConvertToStdString((JSHandle<EcmaString>::Cast(tagElements.script)));
761         if (scriptStr[INTL_INDEX_ZERO] == '\0') {
762             return false;
763         }
764     }
765 
766     // 7. Let region be ? GetOption(options, "region", "string", undefined, undefined).
767     // 8. If region is not undefined, then
768     //    a. If region does not match the unicode_region_subtag production, throw a RangeError exception.
769     tagElements.region =
770         GetOption(thread, options, globalConst->GetHandledRegionString(), OptionType::STRING,
771                   globalConst->GetHandledUndefined(), globalConst->GetHandledUndefined());
772     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
773 
774     if (!tagElements.region->IsUndefined()) {
775         std::string regionStr = intl::LocaleHelper::ConvertToStdString(JSHandle<EcmaString>::Cast(tagElements.region));
776         if (regionStr[INTL_INDEX_ZERO] == '\0') {
777             return false;
778         }
779     }
780     return true;
781 }
782 
BuildOptionsTags(const JSHandle<EcmaString> & tag,icu::LocaleBuilder * builder,JSHandle<JSTaggedValue> language,JSHandle<JSTaggedValue> script,JSHandle<JSTaggedValue> region)783 bool BuildOptionsTags(const JSHandle<EcmaString> &tag, icu::LocaleBuilder *builder, JSHandle<JSTaggedValue> language,
784                       JSHandle<JSTaggedValue> script, JSHandle<JSTaggedValue> region)
785 {
786     std::string tagStr = intl::LocaleHelper::ConvertToStdString(tag);
787     int32_t len = static_cast<int32_t>(tagStr.length());
788     ASSERT(len > 0);
789     builder->setLanguageTag({ tagStr.c_str(), len });
790     UErrorCode status = U_ZERO_ERROR;
791     icu::Locale locale = builder->build(status);
792     locale.canonicalize(status);
793     if (U_FAILURE(status) != 0) {
794         return false;
795     }
796     builder->setLocale(locale);
797 
798     if (!language->IsUndefined()) {
799         std::string languageStr = intl::LocaleHelper::ConvertToStdString(JSHandle<EcmaString>::Cast(language));
800         builder->setLanguage(languageStr);
801         builder->build(status);
802         if ((U_FAILURE(status) != 0)) {
803             return false;
804         }
805     }
806 
807     if (!script->IsUndefined()) {
808         std::string scriptStr = intl::LocaleHelper::ConvertToStdString((JSHandle<EcmaString>::Cast(script)));
809         builder->setScript(scriptStr);
810         builder->build(status);
811         if ((U_FAILURE(status) != 0)) {
812             return false;
813         }
814     }
815 
816     if (!region->IsUndefined()) {
817         std::string regionStr = intl::LocaleHelper::ConvertToStdString(JSHandle<EcmaString>::Cast(region));
818         builder->setRegion(regionStr);
819         builder->build(status);
820         if ((U_FAILURE(status) != 0)) {
821             return false;
822         }
823     }
824     return true;
825 }
826 
InsertOptions(JSThread * thread,const JSHandle<JSObject> & options,icu::LocaleBuilder * builder)827 bool InsertOptions(JSThread *thread, const JSHandle<JSObject> &options, icu::LocaleBuilder *builder)
828 {
829     const std::vector<std::string> hourCycleValues = {"h11", "h12", "h23", "h24"};
830     const std::vector<std::string> caseFirstValues = {"upper", "lower", "false"};
831     const std::vector<std::string> emptyValues = {};
832     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
833     std::string strResult;
834     bool findca =
835         JSLocale::GetOptionOfString(thread, options, globalConst->GetHandledCalendarString(), emptyValues, &strResult);
836     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
837     if (findca) {
838         if (!uloc_toLegacyType(uloc_toLegacyKey("ca"), strResult.c_str())) {
839             return false;
840         }
841         builder->setUnicodeLocaleKeyword("ca", strResult.c_str());
842     }
843 
844     bool findco =
845         JSLocale::GetOptionOfString(thread, options, globalConst->GetHandledCollationString(), emptyValues, &strResult);
846     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
847     if (findco) {
848         if (!uloc_toLegacyType(uloc_toLegacyKey("co"), strResult.c_str())) {
849             return false;
850         }
851         builder->setUnicodeLocaleKeyword("co", strResult.c_str());
852     }
853 
854     bool findhc = JSLocale::GetOptionOfString(thread, options, globalConst->GetHandledHourCycleString(),
855                                               hourCycleValues, &strResult);
856     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
857     if (findhc) {
858         if (!uloc_toLegacyType(uloc_toLegacyKey("hc"), strResult.c_str())) {
859             return false;
860         }
861         builder->setUnicodeLocaleKeyword("hc", strResult.c_str());
862     }
863 
864     bool findkf = JSLocale::GetOptionOfString(thread, options, globalConst->GetHandledCaseFirstString(),
865                                               caseFirstValues, &strResult);
866     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
867     if (findkf) {
868         if (!uloc_toLegacyType(uloc_toLegacyKey("kf"), strResult.c_str())) {
869             return false;
870         }
871         builder->setUnicodeLocaleKeyword("kf", strResult.c_str());
872     }
873 
874     bool boolResult = false;
875     bool findkn =
876         JSLocale::GetOptionOfBool(thread, options, globalConst->GetHandledNumericString(), false, &boolResult);
877     if (findkn) {
878         strResult = boolResult ? "true" : "false";
879         if (!uloc_toLegacyType(uloc_toLegacyKey("kn"), strResult.c_str())) {
880             return false;
881         }
882         builder->setUnicodeLocaleKeyword("kn", strResult.c_str());
883     }
884 
885     bool findnu =
886         JSLocale::GetOptionOfString(thread, options, globalConst->GetHandledNumberingSystemString(), emptyValues,
887                                     &strResult);
888     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
889     if (findnu) {
890         if (!uloc_toLegacyType(uloc_toLegacyKey("nu"), strResult.c_str())) {
891             return false;
892         }
893         builder->setUnicodeLocaleKeyword("nu", strResult.c_str());
894     }
895     return true;
896 }
897 
InitializeLocale(JSThread * thread,const JSHandle<JSLocale> & locale,const JSHandle<EcmaString> & localeString,const JSHandle<JSObject> & options)898 JSHandle<JSLocale> JSLocale::InitializeLocale(JSThread *thread, const JSHandle<JSLocale> &locale,
899                                               const JSHandle<EcmaString> &localeString,
900                                               const JSHandle<JSObject> &options)
901 {
902     icu::LocaleBuilder builder;
903     TagElements tagElements;
904     if (!ApplyOptionsToTag(thread, localeString, options, tagElements)) {
905         THROW_RANGE_ERROR_AND_RETURN(thread, "apply option to tag failed", locale);
906     }
907 
908     bool res = BuildOptionsTags(localeString, &builder, tagElements.language, tagElements.script, tagElements.region);
909     if (!res) {
910         THROW_RANGE_ERROR_AND_RETURN(thread, "apply option to tag failed", locale);
911     }
912     bool insertResult = InsertOptions(thread, options, &builder);
913     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, locale);
914     UErrorCode status = U_ZERO_ERROR;
915     icu::Locale icuLocale = builder.build(status);
916     icuLocale.canonicalize(status);
917 
918     if (!insertResult || (U_FAILURE(status) != 0)) {
919         THROW_RANGE_ERROR_AND_RETURN(thread, "insert or build failed", locale);
920     }
921     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
922     factory->NewJSIntlIcuData(locale, icuLocale, JSLocale::FreeIcuLocale);
923     return locale;
924 }
925 
ConvertValue(const UErrorCode & status,std::string & value,const std::string & key)926 int ConvertValue(const UErrorCode &status, std::string &value, const std::string &key)
927 {
928     if (status == U_ILLEGAL_ARGUMENT_ERROR || value.empty()) {
929         return 1;
930     }
931 
932     if (value == "yes") {
933         value = "true";
934     }
935 
936     if (key == "kf" && value == "true") {
937         return 2;  // 2: in this case normalizedKeyword is empty string
938     }
939     return 0;
940 }
941 
NormalizeKeywordValue(JSThread * thread,const JSHandle<JSLocale> & locale,const std::string & key)942 JSHandle<EcmaString> JSLocale::NormalizeKeywordValue(JSThread *thread, const JSHandle<JSLocale> &locale,
943                                                      const std::string &key)
944 {
945     icu::Locale *icuLocale = locale->GetIcuLocale();
946     UErrorCode status = U_ZERO_ERROR;
947     auto value = icuLocale->getUnicodeKeywordValue<std::string>(key, status);
948 
949     EcmaVM *ecmaVm = thread->GetEcmaVM();
950     ObjectFactory *factory = ecmaVm->GetFactory();
951 
952     int result = ConvertValue(status, value, key);
953     if (result == 1) {
954         return JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledUndefinedString());
955     }
956     if (result == 2) {  // 2: in this case normalizedKeyword is empty string
957         return factory->GetEmptyString();
958     }
959     return factory->NewFromStdString(value);
960 }
961 
ToString(JSThread * thread,const JSHandle<JSLocale> & locale)962 JSHandle<EcmaString> JSLocale::ToString(JSThread *thread, const JSHandle<JSLocale> &locale)
963 {
964     icu::Locale *icuLocale = locale->GetIcuLocale();
965     if (icuLocale == nullptr) {
966         ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
967         return factory->GetEmptyString();
968     }
969     JSHandle<EcmaString> result = intl::LocaleHelper::ToLanguageTag(thread, *icuLocale);
970     return result;
971 }
972 
GetAvailableStringLocales(JSThread * thread,const JSHandle<TaggedArray> & availableLocales)973 std::vector<std::string> JSLocale::GetAvailableStringLocales(JSThread *thread,
974                                                              const JSHandle<TaggedArray> &availableLocales)
975 {
976     std::vector<std::string> availableStringLocales;
977     JSMutableHandle<EcmaString> availableItem(thread, JSTaggedValue::Undefined());
978     uint32_t availablecalesLength = availableLocales->GetLength();
979     for (uint32_t i = 0; i < availablecalesLength; i++) {
980         availableItem.Update(availableLocales->Get(thread, i));
981         availableStringLocales.emplace_back(intl::LocaleHelper::ConvertToStdString(availableItem));
982     }
983     return availableStringLocales;
984 }
985 
986 }  // namespace panda::ecmascript
987