/* * Copyright (c) 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_list_format.h" #include #include #include "ecmascript/ecma_macros.h" #include "ecmascript/global_env.h" #include "ecmascript/global_env_constants.h" #include "ecmascript/js_array.h" #include "ecmascript/js_locale.h" #include "ecmascript/js_iterator.h" #include "unicode/fieldpos.h" #include "unicode/fpositer.h" #include "unicode/formattedvalue.h" #include "unicode/stringpiece.h" #include "unicode/unistr.h" #include "unicode/utf8.h" #include "unicode/uloc.h" #include "unicode/ustring.h" namespace panda::ecmascript { icu::ListFormatter *JSListFormat::GetIcuListFormatter() const { ASSERT(GetIcuLF().IsJSNativePointer()); auto result = JSNativePointer::Cast(GetIcuLF().GetTaggedObject())->GetExternalPointer(); return reinterpret_cast(result); } void JSListFormat::FreeIcuListFormatter(void *pointer, [[maybe_unused]] void* hint) { if (pointer == nullptr) { return; } auto icuListFormat = reinterpret_cast(pointer); icuListFormat->~ListFormatter(); } void JSListFormat::SetIcuListFormatter(JSThread *thread, const JSHandle listFormat, icu::ListFormatter *icuListFormatter, const DeleteEntryPoint &callback) { EcmaVM *ecmaVm = thread->GetEcmaVM(); ObjectFactory *factory = ecmaVm->GetFactory(); ASSERT(icuListFormatter != nullptr); JSTaggedValue data = listFormat->GetIcuLF(); if (data.IsJSNativePointer()) { JSNativePointer *native = JSNativePointer::Cast(data.GetTaggedObject()); native->ResetExternalPointer(icuListFormatter); return; } JSHandle pointer = factory->NewJSNativePointer(icuListFormatter); pointer->SetDeleter(callback); listFormat->SetIcuLF(thread, pointer.GetTaggedValue()); } JSHandle JSListFormat::GetAvailableLocales(JSThread *thread) { JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); JSHandle listFormatLocales = env->GetListFormatLocales(); if (!listFormatLocales->IsUndefined()) { return JSHandle::Cast(listFormatLocales); } const char *key = "listPattern"; const char *path = nullptr; JSHandle availableLocales = JSLocale::GetAvailableLocales(thread, key, path); env->SetListFormatLocales(thread, availableLocales); return availableLocales; } // 13. InitializeListFormat ( listformat, locales, options ) JSHandle JSListFormat::InitializeListFormat(JSThread *thread, const JSHandle &listFormat, 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(JSListFormat, thread); // 4. Let options be ? GetOptionsObject(options). JSHandle optionsObject; if (options->IsUndefined()) { optionsObject = factory->CreateNullJSObject(); } else { optionsObject = JSTaggedValue::ToObject(thread, options); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSListFormat, thread); } // 5. Let opt be a new Record. // 6. 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(JSListFormat, thread); // 8. Let localeData be %ListFormat%.[[LocaleData]]. JSHandle availableLocales; if (requestedLocales->GetLength() == 0) { availableLocales = factory->EmptyArray(); } else { availableLocales = GetAvailableLocales(thread); } // 9. Let r be ResolveLocale(%ListFormat%.[[AvailableLocales]], requestedLocales, // opt, %ListFormat%.[[RelevantExtensionKeys]], localeData). std::set relevantExtensionKeys {""}; ResolvedLocale r = JSLocale::ResolveLocale(thread, availableLocales, requestedLocales, matcher, relevantExtensionKeys); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSListFormat, thread); // 10. Set listFormat.[[Locale]] to r.[[locale]]. icu::Locale icuLocale = r.localeData; JSHandle localeStr = JSLocale::ToLanguageTag(thread, icuLocale); listFormat->SetLocale(thread, localeStr.GetTaggedValue()); // 11. Let type be ? GetOption(options, "type", "string", « "conjunction", "disjunction", "unit" », "conjunction"). property = globalConst->GetHandledTypeString(); auto type = JSLocale::GetOptionOfString(thread, optionsObject, property, {ListTypeOption::CONJUNCTION, ListTypeOption::DISJUNCTION, ListTypeOption::UNIT}, {"conjunction", "disjunction", "unit"}, ListTypeOption::CONJUNCTION); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSListFormat, thread); // 12. Set listFormat.[[Type]] to type. listFormat->SetType(type); // 13. Let style be ? GetOption(options, "style", "string", « "long", "short", "narrow" », "long"). property = globalConst->GetHandledStyleString(); auto style = JSLocale::GetOptionOfString(thread, optionsObject, property, {ListStyleOption::LONG, ListStyleOption::SHORT, ListStyleOption::NARROW}, {"long", "short", "narrow"}, ListStyleOption::LONG); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSListFormat, thread); // 14. Set listFormat.[[Style]] to style. listFormat->SetStyle(style); // 15. Let dataLocale be r.[[dataLocale]]. // 16. Let dataLocaleData be localeData.[[]]. // 17. Let dataLocaleTypes be dataLocaleData.[[]]. // 18. Set listFormat.[[Templates]] to dataLocaleTypes.[[