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 ¤cy)
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