1 /*
2  * Copyright (c) 2021-2022 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_displaynames.h"
17 
18 #include <cstring>
19 
20 #include "ecmascript/intl/locale_helper.h"
21 #include "ecmascript/object_factory-inl.h"
22 
23 #if defined(__clang__)
24 #pragma clang diagnostic push
25 #pragma clang diagnostic ignored "-Wshadow"
26 #elif defined(__GNUC__)
27 #pragma GCC diagnostic push
28 #pragma GCC diagnostic ignored "-Wshadow"
29 #endif
30 #include "unicode/localebuilder.h"
31 #if defined(__clang__)
32 #pragma clang diagnostic pop
33 #elif defined(__GNUC__)
34 #pragma GCC diagnostic pop
35 #endif
36 
37 namespace panda::ecmascript {
38 
39 const std::vector<LocaleMatcherOption> JSDisplayNames::LOCALE_MATCHER_OPTION = {
40     LocaleMatcherOption::LOOKUP, LocaleMatcherOption::BEST_FIT
41 };
42 const std::vector<std::string> JSDisplayNames::LOCALE_MATCHER_OPTION_NAME = {"lookup", "best fit"};
43 
44 const std::vector<StyOption> JSDisplayNames::STY_OPTION = {
45     StyOption::NARROW, StyOption::SHORT, StyOption::LONG
46 };
47 const std::vector<std::string> JSDisplayNames::STY_OPTION_NAME = {"narrow", "short", "long"};
48 
49 const std::vector<TypednsOption> JSDisplayNames::TYPED_NS_OPTION = {
50     TypednsOption::LANGUAGE, TypednsOption::REGION,
51     TypednsOption::SCRIPT, TypednsOption::CURRENCY,
52     TypednsOption::CALENDAR, TypednsOption::DATETIMEFIELD
53 };
54 const std::vector<std::string> JSDisplayNames::TYPED_NS_OPTION_NAME = {
55     "language", "region", "script", "currency",
56     "calendar", "dateTimeField"
57 };
58 
59 const std::vector<FallbackOption> JSDisplayNames::FALLBACK_OPTION = {
60     FallbackOption::CODE, FallbackOption::NONE
61 };
62 const std::vector<std::string> JSDisplayNames::FALLBACK_OPTION_OPTION_NAME = {
63     "code", "none"
64 };
65 
66 const std::vector<LanguageDisplayOption> JSDisplayNames::LANGUAGE_DISPLAY_OPTION = {
67     LanguageDisplayOption::DIALECT, LanguageDisplayOption::STANDARD
68 };
69 const std::vector<std::string> JSDisplayNames::LANGUAGE_DISPLAY_OPTION_NAME = {
70     "dialect", "standard"
71 };
72 
GetIcuLocaleDisplayNames() const73 icu::LocaleDisplayNames *JSDisplayNames::GetIcuLocaleDisplayNames() const
74 {
75     ASSERT(GetIcuLDN().IsJSNativePointer());
76     auto result = JSNativePointer::Cast(GetIcuLDN().GetTaggedObject())->GetExternalPointer();
77     return reinterpret_cast<icu::LocaleDisplayNames *>(result);
78 }
79 
FreeIcuLocaleDisplayNames([[maybe_unused]] void *env, void *pointer, [[maybe_unused]] void* hint)80 void JSDisplayNames::FreeIcuLocaleDisplayNames([[maybe_unused]] void *env, void *pointer, [[maybe_unused]] void* hint)
81 {
82     if (pointer == nullptr) {
83         return;
84     }
85     auto icuLocaleDisplayNames = reinterpret_cast<icu::LocaleDisplayNames *>(pointer);
86     delete icuLocaleDisplayNames;
87 }
88 
SetIcuLocaleDisplayNames(JSThread *thread, const JSHandle<JSDisplayNames> &displayNames, icu::LocaleDisplayNames* iculocaledisplaynames, const NativePointerCallback &callback)89 void JSDisplayNames::SetIcuLocaleDisplayNames(JSThread *thread, const JSHandle<JSDisplayNames> &displayNames,
90                                               icu::LocaleDisplayNames* iculocaledisplaynames,
91                                               const NativePointerCallback &callback)
92 {
93     EcmaVM *ecmaVm = thread->GetEcmaVM();
94     ObjectFactory *factory = ecmaVm->GetFactory();
95 
96     ASSERT(iculocaledisplaynames != nullptr);
97     JSTaggedValue data = displayNames->GetIcuLDN();
98     if (data.IsJSNativePointer()) {
99         JSNativePointer *native = JSNativePointer::Cast(data.GetTaggedObject());
100         native->ResetExternalPointer(thread, iculocaledisplaynames);
101         return;
102     }
103     JSHandle<JSNativePointer> pointer = factory->NewJSNativePointer(iculocaledisplaynames, callback);
104     displayNames->SetIcuLDN(thread, pointer.GetTaggedValue());
105 }
106 
GetAvailableLocales(JSThread *thread)107 JSHandle<TaggedArray> JSDisplayNames::GetAvailableLocales(JSThread *thread)
108 {
109     const char *key = "calendar";
110     const char *path = nullptr;
111     std::vector<std::string> availableStringLocales = intl::LocaleHelper::GetAvailableLocales(thread, key, path);
112     JSHandle<TaggedArray> availableLocales = JSLocale::ConstructLocaleList(thread, availableStringLocales);
113     return availableLocales;
114 }
115 
116 namespace {
IsUnicodeScriptSubtag(const std::string& value)117     bool IsUnicodeScriptSubtag(const std::string& value)
118     {
119         UErrorCode status = U_ZERO_ERROR;
120         icu::LocaleBuilder builder;
121         builder.setScript(value).build(status);
122         return U_SUCCESS(status);
123     }
124 
IsUnicodeRegionSubtag(const std::string& value)125     bool IsUnicodeRegionSubtag(const std::string& value)
126     {
127         UErrorCode status = U_ZERO_ERROR;
128         icu::LocaleBuilder builder;
129         builder.setRegion(value).build(status);
130         return U_SUCCESS(status);
131     }
132 }
133 
134 // InitializeDisplayNames ( displayNames, locales, options )
InitializeDisplayNames(JSThread *thread, const JSHandle<JSDisplayNames> &displayNames, const JSHandle<JSTaggedValue> &locales, const JSHandle<JSTaggedValue> &options)135 JSHandle<JSDisplayNames> JSDisplayNames::InitializeDisplayNames(JSThread *thread,
136                                                                 const JSHandle<JSDisplayNames> &displayNames,
137                                                                 const JSHandle<JSTaggedValue> &locales,
138                                                                 const JSHandle<JSTaggedValue> &options)
139 {
140     [[maybe_unused]] EcmaHandleScope scope(thread);
141     EcmaVM *ecmaVm = thread->GetEcmaVM();
142     ObjectFactory *factory = ecmaVm->GetFactory();
143     auto globalConst = thread->GlobalConstants();
144     // 3. Let requestedLocales be ? CanonicalizeLocaleList(locales).
145     JSHandle<TaggedArray> requestedLocales = intl::LocaleHelper::CanonicalizeLocaleList(thread, locales);
146     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDisplayNames, thread);
147 
148     // 4. If options is undefined, throw a TypeError exception.
149     if (options->IsUndefined()) {
150         THROW_TYPE_ERROR_AND_RETURN(thread, "options is undefined", displayNames);
151     }
152 
153     // 5. Let options be ? GetOptionsObject(options).
154     JSHandle<JSObject> optionsObject = JSTaggedValue::ToObject(thread, options);
155     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDisplayNames, thread);
156 
157     // Note: No need to create a record. It's not observable.
158     // 6. Let opt be a new Record.
159     // 7. Let localeData be %DisplayNames%.[[LocaleData]].
160     // 8. Let matcher be ? GetOption(options, "localeMatcher", "string", « "lookup", "best fit" », "best fit").
161     JSHandle<JSTaggedValue> property = globalConst->GetHandledLocaleMatcherString();
162     auto matcher = JSLocale::GetOptionOfString<LocaleMatcherOption>(
163         thread, optionsObject, property,
164         JSDisplayNames::LOCALE_MATCHER_OPTION, JSDisplayNames::LOCALE_MATCHER_OPTION_NAME,
165         LocaleMatcherOption::BEST_FIT);
166     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDisplayNames, thread);
167 
168     // 10. Let r be ResolveLocale(%DisplayNames%.[[AvailableLocales]], requestedLocales, opt,
169     // %DisplayNames%.[[RelevantExtensionKeys]]).
170     JSHandle<TaggedArray> availableLocales;
171     if (requestedLocales->GetLength() == 0) {
172         availableLocales = factory->EmptyArray();
173     } else {
174         availableLocales = JSDisplayNames::GetAvailableLocales(thread);
175     }
176     std::set<std::string> relevantExtensionKeys {""};
177     ResolvedLocale r =
178         JSLocale::ResolveLocale(thread, availableLocales, requestedLocales, matcher, relevantExtensionKeys);
179     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDisplayNames, thread);
180     icu::Locale icuLocale = r.localeData;
181 
182     // 11. Let style be ? GetOption(options, "style", "string", « "narrow", "short", "long" », "long").
183     property = globalConst->GetHandledStyleString();
184     auto StyOpt = JSLocale::GetOptionOfString<StyOption>(thread, optionsObject, property,
185         JSDisplayNames::STY_OPTION, JSDisplayNames::STY_OPTION_NAME,
186         StyOption::LONG);
187     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDisplayNames, thread);
188 
189     // 12. Set DisplayNames.[[Style]] to style.
190     displayNames->SetStyle(StyOpt);
191 
192     // 13. Let type be ? GetOption(options, "type", "string", « "language", "region", "script", "currency" »,
193     // "undefined").
194     property = globalConst->GetHandledTypeString();
195     auto type = JSLocale::GetOptionOfString<TypednsOption>(thread, optionsObject, property,
196         JSDisplayNames::TYPED_NS_OPTION, JSDisplayNames::TYPED_NS_OPTION_NAME,
197         TypednsOption::UNDEFINED);
198     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDisplayNames, thread);
199 
200     // 14. If type is undefined, throw a TypeError exception.
201     if (type == TypednsOption::UNDEFINED) {
202         THROW_TYPE_ERROR_AND_RETURN(thread, "type is undefined", displayNames);
203     }
204 
205     // 15. Set displayNames.[[Type]] to type.
206     displayNames->SetType(type);
207 
208     // 16. Let fallback be ? GetOption(options, "fallback", "string", « "code", "none" », "code").
209     property = globalConst->GetHandledFallbackString();
210     auto fallback = JSLocale::GetOptionOfString<FallbackOption>(thread, optionsObject, property,
211         JSDisplayNames::FALLBACK_OPTION, JSDisplayNames::FALLBACK_OPTION_OPTION_NAME,
212         FallbackOption::CODE);
213     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDisplayNames, thread);
214 
215     // 17. Set displayNames.[[Fallback]] to fallback.
216     displayNames->SetFallback(fallback);
217 
218     // Let languageDisplay be ? GetOption(options, "languageDisplay", string, « "dialect", "standard" », "dialect").
219     property = globalConst->GetHandledLanguageDisplayString();
220     auto langDisplay = JSLocale::GetOptionOfString<LanguageDisplayOption>(
221         thread, optionsObject, property,
222         JSDisplayNames::LANGUAGE_DISPLAY_OPTION,
223         JSDisplayNames::LANGUAGE_DISPLAY_OPTION_NAME, LanguageDisplayOption::DIALECT);
224     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDisplayNames, thread);
225     displayNames->SetLanguageDisplay(langDisplay);
226 
227     // 18. Set displayNames.[[Locale]] to the value of r.[[Locale]].
228     JSHandle<EcmaString> localeStr = intl::LocaleHelper::ToLanguageTag(thread, icuLocale);
229     RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDisplayNames, thread);
230     displayNames->SetLocale(thread, localeStr.GetTaggedValue());
231     // 19. Let dataLocale be r.[[dataLocale]].
232     // 20. Let dataLocaleData be localeData.[[<dataLocale>]].
233     // 21. Let types be dataLocaleData.[[types]].
234     // 22. Assert: types is a Record (see 12.3.3).
235     // 23. Let typeFields be types.[[<type>]].
236     // 24. Assert: typeFields is a Record (see 12.3.3).
237     // 25. Let styleFields be typeFields.[[<style>]].
238     // 26. Assert: styleFields is a Record (see 12.3.3).
239     // 27. Set displayNames.[[Fields]] to styleFields.
240     // 28. Return displayNames.
241 
242     // Trans StyOption to ICU Style
243     UDisplayContext uStyle;
244     switch (StyOpt) {
245         case StyOption::LONG:
246             uStyle = UDISPCTX_LENGTH_FULL;
247             break;
248         case StyOption::SHORT:
249             uStyle = UDISPCTX_LENGTH_SHORT;
250             break;
251         case StyOption::NARROW:
252             uStyle = UDISPCTX_LENGTH_SHORT;
253             break;
254         default:
255             LOG_ECMA(FATAL) << "this branch is unreachable";
256             UNREACHABLE();
257     }
258     UDisplayContext displayContext[] = {uStyle};
259     icu::LocaleDisplayNames *icudisplaynames(icu::LocaleDisplayNames::createInstance(icuLocale, displayContext, 1));
260     if (icudisplaynames == nullptr) {
261         delete icudisplaynames;
262         THROW_RANGE_ERROR_AND_RETURN(thread, "create icu::LocaleDisplayNames failed", displayNames);
263     }
264     SetIcuLocaleDisplayNames(thread, displayNames, icudisplaynames, JSDisplayNames::FreeIcuLocaleDisplayNames);
265     return displayNames;
266 }
267 
268 // CanonicalCodeForDisplayNames ( type, code )
CanonicalCodeForDisplayNames(JSThread *thread, const JSHandle<JSDisplayNames> &displayNames, const TypednsOption &typeOpt, const JSHandle<EcmaString> &code)269 JSHandle<EcmaString> JSDisplayNames::CanonicalCodeForDisplayNames(JSThread *thread,
270                                                                   const JSHandle<JSDisplayNames> &displayNames,
271                                                                   const TypednsOption &typeOpt,
272                                                                   const JSHandle<EcmaString> &code)
273 {
274     if (typeOpt == TypednsOption::LANGUAGE) {
275         // a. If code does not match the unicode_language_id production, throw a RangeError exception.
276         UErrorCode status = U_ZERO_ERROR;
277         std::string codeSt = intl::LocaleHelper::ConvertToStdString(code);
278         icu::Locale loc = icu::Locale(icu::Locale::forLanguageTag(codeSt, status).getBaseName());
279         std::string checked = loc.toLanguageTag<std::string>(status);
280         if (checked.size() == 0) {
281             THROW_TYPE_ERROR_AND_RETURN(thread, "not match the language id", code);
282         }
283         if (U_FAILURE(status)) {
284             THROW_TYPE_ERROR_AND_RETURN(thread, "not match the unicode_language_id", code);
285         }
286         // b. If IsStructurallyValidLanguageTag(code) is false, throw a RangeError exception.
287         // c. Set code to CanonicalizeUnicodeLocaleId(code).
288         // d. Return code.
289         if (!intl::LocaleHelper::IsStructurallyValidLanguageTag(code)) {
290             THROW_TYPE_ERROR_AND_RETURN(thread, "not a structurally valid", code);
291         }
292         JSHandle<EcmaString> codeStr = intl::LocaleHelper::CanonicalizeUnicodeLocaleId(thread, code);
293         RETURN_HANDLE_IF_ABRUPT_COMPLETION(EcmaString, thread);
294         icu::LocaleDisplayNames *icuLocaldisplaynames = displayNames->GetIcuLocaleDisplayNames();
295         icu::UnicodeString result;
296         std::string codeString = intl::LocaleHelper::ConvertToStdString(codeStr);
297         icuLocaldisplaynames->languageDisplayName(codeString.c_str(), result);
298         JSHandle<EcmaString> codeResult = intl::LocaleHelper::UStringToString(thread, result);
299         return codeResult;
300     } else if (typeOpt == TypednsOption::REGION) {
301         // a. If code does not match the unicode_region_subtag production, throw a RangeError exception.
302         std::string regionCode = intl::LocaleHelper::ConvertToStdString(code);
303         if (!IsUnicodeRegionSubtag(regionCode)) {
304             THROW_RANGE_ERROR_AND_RETURN(thread, "invalid region", code);
305         }
306         // b. Let code be the result of mapping code to upper case as described in 6.1.
307         // c. Return code.
308         icu::LocaleDisplayNames *icuLocaldisplaynames = displayNames->GetIcuLocaleDisplayNames();
309         icu::UnicodeString result;
310         icuLocaldisplaynames->regionDisplayName(regionCode.c_str(), result);
311         JSHandle<EcmaString> codeResult = intl::LocaleHelper::UStringToString(thread, result);
312         return codeResult;
313     } else if (typeOpt == TypednsOption::SCRIPT) {
314         std::string scriptCode = intl::LocaleHelper::ConvertToStdString(code);
315         if (!IsUnicodeScriptSubtag(scriptCode)) {
316             THROW_RANGE_ERROR_AND_RETURN(thread, "invalid script", code);
317         }
318         icu::LocaleDisplayNames *icuLocaldisplaynames = displayNames->GetIcuLocaleDisplayNames();
319         icu::UnicodeString result;
320         icuLocaldisplaynames->scriptDisplayName(scriptCode.c_str(), result);
321         JSHandle<EcmaString> codeResult = intl::LocaleHelper::UStringToString(thread, result);
322         return codeResult;
323     } else if (typeOpt == TypednsOption::CALENDAR) {
324         std::string calendarCode = intl::LocaleHelper::ConvertToStdString(code);
325         if (!JSLocale::IsWellFormedCalendarCode(calendarCode)) {
326             THROW_RANGE_ERROR_AND_RETURN(thread, "invalid calendar", code);
327         }
328 
329         icu::LocaleDisplayNames *icuLocaldisplaynames = displayNames->GetIcuLocaleDisplayNames();
330         icu::UnicodeString result;
331         std::string calendarStrCode = std::strcmp(calendarCode.c_str(), "gregory") == 0
332                                         ? "gregorian"
333                                         : std::strcmp(calendarCode.c_str(), "ethioaa") == 0
334                                             ? "ethiopic-amete-alem"
335                                             : calendarCode;
336         icuLocaldisplaynames->keyValueDisplayName("calendar", calendarStrCode.c_str(), result);
337         JSHandle<EcmaString> codeResult = intl::LocaleHelper::UStringToString(thread, result);
338         return codeResult;
339     } else if (typeOpt == TypednsOption::DATETIMEFIELD) {
340         StyOption style = displayNames->GetStyle();
341         UDateTimePGDisplayWidth width;
342         switch (style) {
343             case StyOption::LONG:
344                 width = UDATPG_WIDE;
345                 break;
346             case StyOption::SHORT:
347                 width = UDATPG_ABBREVIATED;
348                 break;
349             case StyOption::NARROW:
350                 width = UDATPG_NARROW;
351                 break;
352             default:
353                 break;
354         }
355         std::string datetimeCode = intl::LocaleHelper::ConvertToStdString(code);
356         UDateTimePatternField field = StringToUDateTimePatternField(datetimeCode.c_str());
357         if (field == UDATPG_FIELD_COUNT) {
358             THROW_RANGE_ERROR_AND_RETURN(thread, "invalid datetimefield", code);
359         }
360 
361         UErrorCode status = U_ZERO_ERROR;
362         icu::LocaleDisplayNames *icuLocaldisplaynames = displayNames->GetIcuLocaleDisplayNames();
363         icu::Locale locales = icuLocaldisplaynames->getLocale();
364         std::unique_ptr<icu::DateTimePatternGenerator> generator(
365             icu::DateTimePatternGenerator::createInstance(locales, status));
366         icu::UnicodeString result = generator->getFieldDisplayName(field, width);
367         return intl::LocaleHelper::UStringToString(thread, result);
368     }
369     // 4. 4. Assert: type is "currency".
370     // 5. If ! IsWellFormedCurrencyCode(code) is false, throw a RangeError exception.
371     ASSERT(typeOpt == TypednsOption::CURRENCY);
372     std::string cCode = intl::LocaleHelper::ConvertToStdString(code);
373     if (!JSLocale::IsWellFormedCurrencyCode(cCode)) {
374         THROW_RANGE_ERROR_AND_RETURN(thread, "not a wellformed currency code", code);
375     }
376     icu::LocaleDisplayNames *icuLocaldisplaynames = displayNames->GetIcuLocaleDisplayNames();
377     icu::UnicodeString result;
378     icuLocaldisplaynames->keyValueDisplayName("currency", cCode.c_str(), result);
379     JSHandle<EcmaString> codeResult = intl::LocaleHelper::UStringToString(thread, result);
380     return codeResult;
381 }
382 
StringToUDateTimePatternField(const char* code)383 UDateTimePatternField JSDisplayNames::StringToUDateTimePatternField(const char* code)
384 {
385     if (std::strcmp(code, "day") == 0) {
386         return UDATPG_DAY_FIELD;
387     }
388     if (std::strcmp(code, "dayPeriod") == 0) {
389         return UDATPG_DAYPERIOD_FIELD;
390     }
391     if (std::strcmp(code, "era") == 0) {
392         return UDATPG_ERA_FIELD;
393     }
394     if (std::strcmp(code, "hour") == 0) {
395         return UDATPG_HOUR_FIELD;
396     }
397     if (std::strcmp(code, "minute") == 0) {
398         return UDATPG_MINUTE_FIELD;
399     }
400     if (std::strcmp(code, "month") == 0) {
401         return UDATPG_MONTH_FIELD;
402     }
403     if (std::strcmp(code, "quarter") == 0) {
404         return UDATPG_QUARTER_FIELD;
405     }
406     if (std::strcmp(code, "second") == 0) {
407         return UDATPG_SECOND_FIELD;
408     }
409     if (std::strcmp(code, "timeZoneName") == 0) {
410         return UDATPG_ZONE_FIELD;
411     }
412     if (std::strcmp(code, "weekOfYear") == 0) {
413         return UDATPG_WEEK_OF_YEAR_FIELD;
414     }
415     if (std::strcmp(code, "weekday") == 0) {
416         return UDATPG_WEEKDAY_FIELD;
417     }
418     if (std::strcmp(code, "year") == 0) {
419         return UDATPG_YEAR_FIELD;
420     }
421     return UDATPG_FIELD_COUNT;
422 }
423 
StyOptionToEcmaString(JSThread *thread, StyOption style)424 JSHandle<JSTaggedValue> StyOptionToEcmaString(JSThread *thread, StyOption style)
425 {
426     JSMutableHandle<JSTaggedValue> result(thread, JSTaggedValue::Undefined());
427     auto globalConst = thread->GlobalConstants();
428     switch (style) {
429         case StyOption::LONG:
430             result.Update(globalConst->GetHandledLongString().GetTaggedValue());
431             break;
432         case StyOption::SHORT:
433             result.Update(globalConst->GetHandledShortString().GetTaggedValue());
434             break;
435         case StyOption::NARROW:
436             result.Update(globalConst->GetHandledNarrowString().GetTaggedValue());
437             break;
438         default:
439             LOG_ECMA(FATAL) << "this branch is unreachable";
440             UNREACHABLE();
441     }
442     return result;
443 }
444 
TypeOptionToEcmaString(JSThread *thread, TypednsOption type)445 JSHandle<JSTaggedValue> TypeOptionToEcmaString(JSThread *thread, TypednsOption type)
446 {
447     JSMutableHandle<JSTaggedValue> result(thread, JSTaggedValue::Undefined());
448     auto globalConst = thread->GlobalConstants();
449     switch (type) {
450         case TypednsOption::LANGUAGE:
451             result.Update(globalConst->GetHandledLanguageString().GetTaggedValue());
452             break;
453         case TypednsOption::CALENDAR:
454             result.Update(globalConst->GetHandledCalendarString().GetTaggedValue());
455             break;
456         case TypednsOption::CURRENCY:
457             result.Update(globalConst->GetHandledCurrencyString().GetTaggedValue());
458             break;
459         case TypednsOption::DATETIMEFIELD:
460             result.Update(globalConst->GetHandledDateTimeFieldString().GetTaggedValue());
461             break;
462         case TypednsOption::REGION:
463             result.Update(globalConst->GetHandledRegionString().GetTaggedValue());
464             break;
465         case TypednsOption::SCRIPT:
466             result.Update(globalConst->GetHandledScriptString().GetTaggedValue());
467             break;
468         default:
469             LOG_ECMA(FATAL) << "this branch is unreachable";
470             UNREACHABLE();
471     }
472     return result;
473 }
474 
FallbackOptionToEcmaString(JSThread *thread, FallbackOption fallback)475 JSHandle<JSTaggedValue> FallbackOptionToEcmaString(JSThread *thread, FallbackOption fallback)
476 {
477     JSMutableHandle<JSTaggedValue> result(thread, JSTaggedValue::Undefined());
478     auto globalConst = thread->GlobalConstants();
479     switch (fallback) {
480         case FallbackOption::CODE:
481             result.Update(globalConst->GetHandledCodeString().GetTaggedValue());
482             break;
483         case FallbackOption::NONE:
484             result.Update(globalConst->GetHandledNoneString().GetTaggedValue());
485             break;
486         default:
487             LOG_ECMA(FATAL) << "this branch is unreachable";
488             UNREACHABLE();
489     }
490     return result;
491 }
492 
LanguageDisplayOptionToEcmaString(JSThread *thread, LanguageDisplayOption langDisplay)493 JSHandle<JSTaggedValue> LanguageDisplayOptionToEcmaString(JSThread *thread, LanguageDisplayOption langDisplay)
494 {
495     JSMutableHandle<JSTaggedValue> result(thread, JSTaggedValue::Undefined());
496     auto globalConst = thread->GlobalConstants();
497     switch (langDisplay) {
498         case LanguageDisplayOption::DIALECT:
499             result.Update(globalConst->GetHandledDialectString().GetTaggedValue());
500             break;
501         case LanguageDisplayOption::STANDARD:
502             result.Update(globalConst->GetHandledStandardString().GetTaggedValue());
503             break;
504         default:
505             LOG_ECMA(FATAL) << "this branch is unreachable";
506             UNREACHABLE();
507     }
508     return result;
509 }
510 
ResolvedOptions(JSThread *thread, const JSHandle<JSDisplayNames> &displayNames, const JSHandle<JSObject> &options)511 void JSDisplayNames::ResolvedOptions(JSThread *thread, const JSHandle<JSDisplayNames> &displayNames,
512                                      const JSHandle<JSObject> &options)
513 {
514     auto globalConst = thread->GlobalConstants();
515 
516     // [[Locale]]
517     JSHandle<JSTaggedValue> propertyKey = globalConst->GetHandledLocaleString();
518     JSHandle<JSTaggedValue> locale(thread, displayNames->GetLocale());
519     JSObject::CreateDataPropertyOrThrow(thread, options, propertyKey, locale);
520     RETURN_IF_ABRUPT_COMPLETION(thread);
521 
522     // [[Style]]
523     StyOption style = displayNames->GetStyle();
524     propertyKey = globalConst->GetHandledStyleString();
525     JSHandle<JSTaggedValue> styleString = StyOptionToEcmaString(thread, style);
526     JSObject::CreateDataPropertyOrThrow(thread, options, propertyKey, styleString);
527     RETURN_IF_ABRUPT_COMPLETION(thread);
528 
529     // [[type]]
530     TypednsOption type = displayNames->GetType();
531     propertyKey = globalConst->GetHandledTypeString();
532     JSHandle<JSTaggedValue> typeString = TypeOptionToEcmaString(thread, type);
533     JSObject::CreateDataPropertyOrThrow(thread, options, propertyKey, typeString);
534     RETURN_IF_ABRUPT_COMPLETION(thread);
535 
536     // [[fallback]]
537     FallbackOption fallback = displayNames->GetFallback();
538     propertyKey = globalConst->GetHandledFallbackString();
539     JSHandle<JSTaggedValue> fallbackString = FallbackOptionToEcmaString(thread, fallback);
540     JSObject::CreateDataPropertyOrThrow(thread, options, propertyKey, fallbackString);
541     RETURN_IF_ABRUPT_COMPLETION(thread);
542 
543     // [[languageDisplay]]
544     // The default languageDisplay is 'dialect' if type is 'language'
545     if (type == TypednsOption::LANGUAGE) {
546         LanguageDisplayOption langDisplay = displayNames->GetLanguageDisplay();
547         propertyKey = globalConst->GetHandledLanguageDisplayString();
548         JSHandle<JSTaggedValue> langDisplayString = LanguageDisplayOptionToEcmaString(thread, langDisplay);
549         JSObject::CreateDataPropertyOrThrow(thread, options, propertyKey, langDisplayString);
550         RETURN_IF_ABRUPT_COMPLETION(thread);
551     }
552 }
553 }  // namespace panda::ecmascript
554