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
37namespace panda::ecmascript {
38
39const std::vector<LocaleMatcherOption> JSDisplayNames::LOCALE_MATCHER_OPTION = {
40    LocaleMatcherOption::LOOKUP, LocaleMatcherOption::BEST_FIT
41};
42const std::vector<std::string> JSDisplayNames::LOCALE_MATCHER_OPTION_NAME = {"lookup", "best fit"};
43
44const std::vector<StyOption> JSDisplayNames::STY_OPTION = {
45    StyOption::NARROW, StyOption::SHORT, StyOption::LONG
46};
47const std::vector<std::string> JSDisplayNames::STY_OPTION_NAME = {"narrow", "short", "long"};
48
49const std::vector<TypednsOption> JSDisplayNames::TYPED_NS_OPTION = {
50    TypednsOption::LANGUAGE, TypednsOption::REGION,
51    TypednsOption::SCRIPT, TypednsOption::CURRENCY,
52    TypednsOption::CALENDAR, TypednsOption::DATETIMEFIELD
53};
54const std::vector<std::string> JSDisplayNames::TYPED_NS_OPTION_NAME = {
55    "language", "region", "script", "currency",
56    "calendar", "dateTimeField"
57};
58
59const std::vector<FallbackOption> JSDisplayNames::FALLBACK_OPTION = {
60    FallbackOption::CODE, FallbackOption::NONE
61};
62const std::vector<std::string> JSDisplayNames::FALLBACK_OPTION_OPTION_NAME = {
63    "code", "none"
64};
65
66const std::vector<LanguageDisplayOption> JSDisplayNames::LANGUAGE_DISPLAY_OPTION = {
67    LanguageDisplayOption::DIALECT, LanguageDisplayOption::STANDARD
68};
69const std::vector<std::string> JSDisplayNames::LANGUAGE_DISPLAY_OPTION_NAME = {
70    "dialect", "standard"
71};
72
73icu::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
80void 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
89void 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
107JSHandle<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
116namespace {
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
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 )
135JSHandle<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 )
269JSHandle<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
383UDateTimePatternField 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
424JSHandle<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
445JSHandle<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
475JSHandle<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
493JSHandle<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
511void 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