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