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