/* * 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 #include "ecmascript/global_env.h" #include "ecmascript/global_env_constants.h" #include "unicode/errorcode.h" #include "unicode/locdspnm.h" #include "unicode/locid.h" #include "unicode/udisplaycontext.h" #include "unicode/uloc.h" #include "unicode/unistr.h" #include "unicode/uscript.h" #include "unicode/ustring.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 { icu::LocaleDisplayNames *JSDisplayNames::GetIcuLocaleDisplayNames() const { ASSERT(GetIcuLDN().IsJSNativePointer()); auto result = JSNativePointer::Cast(GetIcuLDN().GetTaggedObject())->GetExternalPointer(); return reinterpret_cast(result); } void JSDisplayNames::FreeIcuLocaleDisplayNames(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 DeleteEntryPoint &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(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; JSHandle availableLocales = JSLocale::GetAvailableLocales(thread, key, path); 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 = JSLocale::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, {LocaleMatcherOption::LOOKUP, LocaleMatcherOption::BEST_FIT}, {"lookup", "best fit"}, 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, {StyOption::NARROW, StyOption::SHORT, StyOption::LONG}, {"narrow", "short", "long"}, 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, {TypednsOption::LANGUAGE, TypednsOption::REGION, TypednsOption::SCRIPT, TypednsOption::CURRENCY}, {"language", "region", "script", "currency"}, 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, {FallbackOption::CODE, FallbackOption::NONE}, {"code", "none"}, FallbackOption::CODE); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDisplayNames, thread); // 17. Set displayNames.[[Fallback]] to fallback. displayNames->SetFallback(fallback); // 18. Set displayNames.[[Locale]] to the value of r.[[Locale]]. JSHandle localeStr = JSLocale::ToLanguageTag(thread, icuLocale); 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.[[