/* * Copyright (c) 2021-2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ecmascript/js_displaynames.h" #include "ecmascript/intl/locale_helper.h" #include "ecmascript/object_factory-inl.h" #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wshadow" #elif defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wshadow" #endif #include "unicode/localebuilder.h" #if defined(__clang__) #pragma clang diagnostic pop #elif defined(__GNUC__) #pragma GCC diagnostic pop #endif namespace panda::ecmascript { const std::vector JSDisplayNames::LOCALE_MATCHER_OPTION = { LocaleMatcherOption::LOOKUP, LocaleMatcherOption::BEST_FIT }; const std::vector JSDisplayNames::LOCALE_MATCHER_OPTION_NAME = {"lookup", "best fit"}; const std::vector JSDisplayNames::STY_OPTION = { StyOption::NARROW, StyOption::SHORT, StyOption::LONG }; const std::vector JSDisplayNames::STY_OPTION_NAME = {"narrow", "short", "long"}; const std::vector JSDisplayNames::TYPED_NS_OPTION = { TypednsOption::LANGUAGE, TypednsOption::REGION, TypednsOption::SCRIPT, TypednsOption::CURRENCY, TypednsOption::CALENDAR, TypednsOption::DATETIMEFIELD }; const std::vector JSDisplayNames::TYPED_NS_OPTION_NAME = { "language", "region", "script", "currency", "calendar", "dateTimeField" }; const std::vector JSDisplayNames::FALLBACK_OPTION = { FallbackOption::CODE, FallbackOption::NONE }; const std::vector JSDisplayNames::FALLBACK_OPTION_OPTION_NAME = { "code", "none" }; const std::vector JSDisplayNames::LANGUAGE_DISPLAY_OPTION = { LanguageDisplayOption::DIALECT, LanguageDisplayOption::STANDARD }; const std::vector JSDisplayNames::LANGUAGE_DISPLAY_OPTION_NAME = { "dialect", "standard" }; icu::LocaleDisplayNames *JSDisplayNames::GetIcuLocaleDisplayNames() const { ASSERT(GetIcuLDN().IsJSNativePointer()); auto result = JSNativePointer::Cast(GetIcuLDN().GetTaggedObject())->GetExternalPointer(); return reinterpret_cast(result); } void JSDisplayNames::FreeIcuLocaleDisplayNames([[maybe_unused]] void *env, void *pointer, [[maybe_unused]] void* hint) { if (pointer == nullptr) { return; } auto icuLocaleDisplayNames = reinterpret_cast(pointer); delete icuLocaleDisplayNames; } void JSDisplayNames::SetIcuLocaleDisplayNames(JSThread *thread, const JSHandle &displayNames, icu::LocaleDisplayNames* iculocaledisplaynames, const NativePointerCallback &callback) { EcmaVM *ecmaVm = thread->GetEcmaVM(); ObjectFactory *factory = ecmaVm->GetFactory(); ASSERT(iculocaledisplaynames != nullptr); JSTaggedValue data = displayNames->GetIcuLDN(); if (data.IsJSNativePointer()) { JSNativePointer *native = JSNativePointer::Cast(data.GetTaggedObject()); native->ResetExternalPointer(thread, iculocaledisplaynames); return; } JSHandle pointer = factory->NewJSNativePointer(iculocaledisplaynames, callback); displayNames->SetIcuLDN(thread, pointer.GetTaggedValue()); } JSHandle JSDisplayNames::GetAvailableLocales(JSThread *thread) { const char *key = "calendar"; const char *path = nullptr; std::vector availableStringLocales = intl::LocaleHelper::GetAvailableLocales(thread, key, path); JSHandle availableLocales = JSLocale::ConstructLocaleList(thread, availableStringLocales); return availableLocales; } namespace { bool IsUnicodeScriptSubtag(const std::string& value) { UErrorCode status = U_ZERO_ERROR; icu::LocaleBuilder builder; builder.setScript(value).build(status); return U_SUCCESS(status); } bool IsUnicodeRegionSubtag(const std::string& value) { UErrorCode status = U_ZERO_ERROR; icu::LocaleBuilder builder; builder.setRegion(value).build(status); return U_SUCCESS(status); } } // InitializeDisplayNames ( displayNames, locales, options ) JSHandle JSDisplayNames::InitializeDisplayNames(JSThread *thread, const JSHandle &displayNames, const JSHandle &locales, const JSHandle &options) { [[maybe_unused]] EcmaHandleScope scope(thread); EcmaVM *ecmaVm = thread->GetEcmaVM(); ObjectFactory *factory = ecmaVm->GetFactory(); auto globalConst = thread->GlobalConstants(); // 3. Let requestedLocales be ? CanonicalizeLocaleList(locales). JSHandle requestedLocales = intl::LocaleHelper::CanonicalizeLocaleList(thread, locales); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDisplayNames, thread); // 4. If options is undefined, throw a TypeError exception. if (options->IsUndefined()) { THROW_TYPE_ERROR_AND_RETURN(thread, "options is undefined", displayNames); } // 5. Let options be ? GetOptionsObject(options). JSHandle optionsObject = JSTaggedValue::ToObject(thread, options); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDisplayNames, thread); // Note: No need to create a record. It's not observable. // 6. Let opt be a new Record. // 7. Let localeData be %DisplayNames%.[[LocaleData]]. // 8. Let matcher be ? GetOption(options, "localeMatcher", "string", « "lookup", "best fit" », "best fit"). JSHandle property = globalConst->GetHandledLocaleMatcherString(); auto matcher = JSLocale::GetOptionOfString( thread, optionsObject, property, JSDisplayNames::LOCALE_MATCHER_OPTION, JSDisplayNames::LOCALE_MATCHER_OPTION_NAME, LocaleMatcherOption::BEST_FIT); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDisplayNames, thread); // 10. Let r be ResolveLocale(%DisplayNames%.[[AvailableLocales]], requestedLocales, opt, // %DisplayNames%.[[RelevantExtensionKeys]]). JSHandle availableLocales; if (requestedLocales->GetLength() == 0) { availableLocales = factory->EmptyArray(); } else { availableLocales = JSDisplayNames::GetAvailableLocales(thread); } std::set relevantExtensionKeys {""}; ResolvedLocale r = JSLocale::ResolveLocale(thread, availableLocales, requestedLocales, matcher, relevantExtensionKeys); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDisplayNames, thread); icu::Locale icuLocale = r.localeData; // 11. Let style be ? GetOption(options, "style", "string", « "narrow", "short", "long" », "long"). property = globalConst->GetHandledStyleString(); auto StyOpt = JSLocale::GetOptionOfString(thread, optionsObject, property, JSDisplayNames::STY_OPTION, JSDisplayNames::STY_OPTION_NAME, StyOption::LONG); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDisplayNames, thread); // 12. Set DisplayNames.[[Style]] to style. displayNames->SetStyle(StyOpt); // 13. Let type be ? GetOption(options, "type", "string", « "language", "region", "script", "currency" », // "undefined"). property = globalConst->GetHandledTypeString(); auto type = JSLocale::GetOptionOfString(thread, optionsObject, property, JSDisplayNames::TYPED_NS_OPTION, JSDisplayNames::TYPED_NS_OPTION_NAME, TypednsOption::UNDEFINED); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDisplayNames, thread); // 14. If type is undefined, throw a TypeError exception. if (type == TypednsOption::UNDEFINED) { THROW_TYPE_ERROR_AND_RETURN(thread, "type is undefined", displayNames); } // 15. Set displayNames.[[Type]] to type. displayNames->SetType(type); // 16. Let fallback be ? GetOption(options, "fallback", "string", « "code", "none" », "code"). property = globalConst->GetHandledFallbackString(); auto fallback = JSLocale::GetOptionOfString(thread, optionsObject, property, JSDisplayNames::FALLBACK_OPTION, JSDisplayNames::FALLBACK_OPTION_OPTION_NAME, FallbackOption::CODE); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDisplayNames, thread); // 17. Set displayNames.[[Fallback]] to fallback. displayNames->SetFallback(fallback); // Let languageDisplay be ? GetOption(options, "languageDisplay", string, « "dialect", "standard" », "dialect"). property = globalConst->GetHandledLanguageDisplayString(); auto langDisplay = JSLocale::GetOptionOfString( thread, optionsObject, property, JSDisplayNames::LANGUAGE_DISPLAY_OPTION, JSDisplayNames::LANGUAGE_DISPLAY_OPTION_NAME, LanguageDisplayOption::DIALECT); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDisplayNames, thread); displayNames->SetLanguageDisplay(langDisplay); // 18. Set displayNames.[[Locale]] to the value of r.[[Locale]]. JSHandle localeStr = intl::LocaleHelper::ToLanguageTag(thread, icuLocale); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDisplayNames, thread); displayNames->SetLocale(thread, localeStr.GetTaggedValue()); // 19. Let dataLocale be r.[[dataLocale]]. // 20. Let dataLocaleData be localeData.[[]]. // 21. Let types be dataLocaleData.[[types]]. // 22. Assert: types is a Record (see 12.3.3). // 23. Let typeFields be types.[[]]. // 24. Assert: typeFields is a Record (see 12.3.3). // 25. Let styleFields be typeFields.[[