/* * Copyright (c) 2021 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/builtins/builtins_locale.h" #include "ecmascript/intl/locale_helper.h" #include "ecmascript/global_env.h" #include "ecmascript/object_factory-inl.h" namespace panda::ecmascript::builtins { // 10.1.3 Intl.Locale( tag [, options] ) JSTaggedValue BuiltinsLocale::LocaleConstructor(EcmaRuntimeCallInfo *argv) { JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, Locale, Constructor); [[maybe_unused]] EcmaHandleScope scope(thread); EcmaVM *ecmaVm = thread->GetEcmaVM(); ObjectFactory *factory = ecmaVm->GetFactory(); // 1. If NewTarget is undefined, throw a TypeError exception. JSHandle newTarget = GetNewTarget(argv); if (newTarget->IsUndefined()) { THROW_TYPE_ERROR_AND_RETURN(thread, "newTarget is undefined", JSTaggedValue::Exception()); } // 6. Let locale be ? OrdinaryCreateFromConstructor(NewTarget, %LocalePrototype%, internalSlotsList). JSHandle constructor = GetConstructor(argv); JSHandle newObject = factory->NewJSObjectByConstructor(JSHandle(constructor), newTarget); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle locale =JSHandle::Cast(newObject); // 7. If Type(tag) is not String or Object, throw a TypeError exception. JSHandle tag = GetCallArg(argv, 0); if (!tag->IsString() && !tag->IsJSObject()) { THROW_TYPE_ERROR_AND_RETURN(thread, "tag is not String or Object", JSTaggedValue::Exception()); } // 8. If Type(tag) is Object and tag has an [[InitializedLocale]] internal slot, then // a.Let tag be tag.[[Locale]]. // 9. Else, // a.Let tag be ? ToString(tag). JSHandle localeString = factory->GetEmptyString(); if (!tag->IsJSLocale()) { localeString = JSTaggedValue::ToString(thread, tag); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); } else { icu::Locale *icuLocale = (JSHandle::Cast(tag))->GetIcuLocale(); localeString = intl::LocaleHelper::ToLanguageTag(thread, *icuLocale); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); } // 10. If options is undefined, then // a.Let options be ! ObjectCreate(null). // 11. Else // a.Let options be ? ToObject(options). JSHandle options = GetCallArg(argv, 1); JSHandle optionsObj; if (options->IsUndefined()) { optionsObj = factory->CreateNullJSObject(); } else { optionsObj = JSTaggedValue::ToObject(thread, options); } RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle result = JSLocale::InitializeLocale(thread, locale, localeString, optionsObj); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return result.GetTaggedValue(); } JSTaggedValue BuiltinsLocale::Maximize(EcmaRuntimeCallInfo *argv) { JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, Locale, Maximize); [[maybe_unused]] EcmaHandleScope scope(thread); // 1. Let loc be the this value. JSHandle loc = GetThis(argv); // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). if (!loc->IsJSLocale()) { THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); } // 3. Let maximal be the result of the Add Likely Subtags algorithm applied to loc.[[Locale]]. If an error is // signaled, set maximal to loc.[[Locale]]. JSHandle locale = JSHandle::Cast(loc); icu::Locale source(*(locale->GetIcuLocale())); UErrorCode status = U_ZERO_ERROR; source.addLikelySubtags(status); ASSERT(U_SUCCESS(status)); ASSERT(!source.isBogus()); // 4. Return ! Construct(%Locale%, maximal). EcmaVM *ecmaVm = thread->GetEcmaVM(); JSHandle env = ecmaVm->GetGlobalEnv(); ObjectFactory *factory = ecmaVm->GetFactory(); JSHandle ctor(env->GetLocaleFunction()); JSHandle obj = factory->NewJSObjectByConstructor(ctor); factory->NewJSIntlIcuData(JSHandle::Cast(obj), source, JSLocale::FreeIcuLocale); return obj.GetTaggedValue(); } JSTaggedValue BuiltinsLocale::Minimize(EcmaRuntimeCallInfo *argv) { JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, Locale, Minimize); [[maybe_unused]] EcmaHandleScope scope(thread); // 1. Let loc be the this value. JSHandle loc = GetThis(argv); // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). if (!loc->IsJSLocale()) { THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); } // 3. Let minimal be the result of the Remove Likely Subtags algorithm applied to loc.[[Locale]]. // If an error is signaled, set minimal to loc.[[Locale]]. JSHandle locale = JSHandle::Cast(loc); icu::Locale source(*(locale->GetIcuLocale())); UErrorCode status = U_ZERO_ERROR; source.minimizeSubtags(status); ASSERT(U_SUCCESS(status)); ASSERT(!source.isBogus()); [[maybe_unused]] auto res = source.toLanguageTag(status); // 4. Return ! Construct(%Locale%, minimal). EcmaVM *ecmaVm = thread->GetEcmaVM(); JSHandle env = ecmaVm->GetGlobalEnv(); ObjectFactory *factory = ecmaVm->GetFactory(); JSHandle ctor(env->GetLocaleFunction()); JSHandle obj = factory->NewJSObjectByConstructor(ctor); factory->NewJSIntlIcuData(JSHandle::Cast(obj), source, JSLocale::FreeIcuLocale); return obj.GetTaggedValue(); } JSTaggedValue BuiltinsLocale::ToString(EcmaRuntimeCallInfo *argv) { JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, Locale, ToString); [[maybe_unused]] EcmaHandleScope scope(thread); // 1. Let loc be the this value. JSHandle loc = GetThis(argv); // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). if (!loc->IsJSLocale()) { THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); } // 3. Return loc.[[Locale]]. JSHandle result = JSLocale::ToString(thread, JSHandle::Cast(loc)); return result.GetTaggedValue(); } JSTaggedValue BuiltinsLocale::GetBaseName(EcmaRuntimeCallInfo *argv) { JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, Locale, GetBaseName); [[maybe_unused]] EcmaHandleScope scope(thread); // 1. Let loc be the this value. JSHandle loc = GetThis(argv); // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). if (!loc->IsJSLocale()) { THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); } // 3. Let locale be loc.[[Locale]]. // 4. Return the substring of locale corresponding to the unicode_language_id production. JSHandle locale = JSHandle::Cast(loc); icu::Locale icuLocale = icu::Locale::createFromName(locale->GetIcuLocale()->getBaseName()); JSHandle baseName = intl::LocaleHelper::ToLanguageTag(thread, icuLocale); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return baseName.GetTaggedValue(); } JSTaggedValue BuiltinsLocale::GetCalendar(EcmaRuntimeCallInfo *argv) { JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, Locale, GetCalendar); [[maybe_unused]] EcmaHandleScope scope(thread); // 1. Let loc be the this value. JSHandle loc = GetThis(argv); // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). if (!loc->IsJSLocale()) { THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); } // 3. Return loc.[[Calendar]]. JSHandle locale = JSHandle::Cast(loc); JSTaggedValue calendar = JSLocale::NormalizeKeywordValue(thread, locale, "ca"); return calendar; } JSTaggedValue BuiltinsLocale::GetCaseFirst(EcmaRuntimeCallInfo *argv) { // This property only exists if %Locale%.[[RelevantExtensionKeys]] contains "kf". JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, Locale, GetCaseFirst); [[maybe_unused]] EcmaHandleScope scope(thread); // 1. Let loc be the this value. JSHandle loc = GetThis(argv); // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). if (!loc->IsJSLocale()) { THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); } // 3. Return loc.[[CaseFirst]]. JSHandle locale = JSHandle::Cast(loc); JSTaggedValue caseFirst = JSLocale::NormalizeKeywordValue(thread, locale, "kf"); return caseFirst; } JSTaggedValue BuiltinsLocale::GetCollation(EcmaRuntimeCallInfo *argv) { JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, Locale, GetCollation); [[maybe_unused]] EcmaHandleScope scope(thread); // 1. Let loc be the this value. JSHandle loc = GetThis(argv); // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). if (!loc->IsJSLocale()) { THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); } // 3. Return loc.[[Collation]]. JSHandle locale = JSHandle::Cast(loc); JSTaggedValue collation = JSLocale::NormalizeKeywordValue(thread, locale, "co"); return collation; } JSTaggedValue BuiltinsLocale::GetHourCycle(EcmaRuntimeCallInfo *argv) { JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, Locale, GetHourCycle); [[maybe_unused]] EcmaHandleScope scope(thread); // 1. Let loc be the this value. JSHandle loc = GetThis(argv); // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). if (!loc->IsJSLocale()) { THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); } // 3. Return loc.[[HourCycle]]. JSHandle locale = JSHandle::Cast(loc); JSTaggedValue hourCycle = JSLocale::NormalizeKeywordValue(thread, locale, "hc"); return hourCycle; } JSTaggedValue BuiltinsLocale::GetNumeric(EcmaRuntimeCallInfo *argv) { // This property only exists if %Locale%.[[RelevantExtensionKeys]] contains "kn". JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, Locale, GetNumeric); [[maybe_unused]] EcmaHandleScope scope(thread); // 1. Let loc be the this value. JSHandle loc = GetThis(argv); // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). if (!loc->IsJSLocale()) { THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); } // 3. Return loc.[[Numeric]]. JSHandle locale = JSHandle::Cast(loc); icu::Locale *icuLocale = locale->GetIcuLocale(); UErrorCode status = U_ZERO_ERROR; auto numeric = icuLocale->getUnicodeKeywordValue("kn", status); JSTaggedValue result = (numeric == "true") ? JSTaggedValue::True() : JSTaggedValue::False(); return result; } JSTaggedValue BuiltinsLocale::GetNumberingSystem(EcmaRuntimeCallInfo *argv) { JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, Locale, GetNumberingSystem); [[maybe_unused]] EcmaHandleScope scope(thread); // 1. Let loc be the this value. JSHandle loc = GetThis(argv); // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). if (!loc->IsJSLocale()) { THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); } // 3. Return loc.[[NumberingSystem]]. JSHandle locale = JSHandle::Cast(loc); JSTaggedValue numberingSystem = JSLocale::NormalizeKeywordValue(thread, locale, "nu"); return numberingSystem; } JSTaggedValue BuiltinsLocale::GetLanguage(EcmaRuntimeCallInfo *argv) { JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, Locale, GetLanguage); [[maybe_unused]] EcmaHandleScope scope(thread); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); // 1. Let loc be the this value. JSHandle loc = GetThis(argv); // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). if (!loc->IsJSLocale()) { THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); } // 3. Let locale be loc.[[Locale]]. JSHandle locale = JSHandle::Cast(loc); // 4. Assert: locale matches the unicode_locale_id production. // 5. Return the substring of locale corresponding to the unicode_language_subtag production of the // unicode_language_id. JSHandle result = JSHandle::Cast(thread->GlobalConstants()->GetHandledUndefinedString()); CString language = locale->GetIcuLocale()->getLanguage(); if (language.empty()) { return result.GetTaggedValue(); } result = factory->NewFromUtf8(language); return result.GetTaggedValue(); } JSTaggedValue BuiltinsLocale::GetScript(EcmaRuntimeCallInfo *argv) { JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, Locale, GetScript); [[maybe_unused]] EcmaHandleScope scope(thread); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); // 1. Let loc be the this value. JSHandle loc = GetThis(argv); // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). if (!loc->IsJSLocale()) { THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); } // 3. Let locale be loc.[[Locale]]. JSHandle locale = JSHandle::Cast(loc); // 4. Assert: locale matches the unicode_locale_id production. // 5. If the unicode_language_id production of locale does not contain the ["-" unicode_script_subtag] sequence, // return undefined. // 6. Return the substring of locale corresponding to the unicode_script_subtag production of the // unicode_language_id. JSHandle result(thread, JSTaggedValue::Undefined()); CString script = locale->GetIcuLocale()->getScript(); if (script.empty()) { return result.GetTaggedValue(); } result = factory->NewFromUtf8(script); return result.GetTaggedValue(); } JSTaggedValue BuiltinsLocale::GetRegion(EcmaRuntimeCallInfo *argv) { JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, Locale, GetRegion); [[maybe_unused]] EcmaHandleScope scope(thread); EcmaVM *ecmaVm = thread->GetEcmaVM(); const GlobalEnvConstants *globalConst = thread->GlobalConstants(); ObjectFactory *factory = ecmaVm->GetFactory(); // 1. Let loc be the this value. JSHandle loc = GetThis(argv); // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). if (!loc->IsJSLocale()) { THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); } // 3. Let locale be loc.[[Locale]]. JSHandle locale = JSHandle::Cast(loc); // 4. Assert: locale matches the unicode_locale_id production. // 5. If the unicode_language_id production of locale does not contain the ["-" unicode_region_subtag] sequence, // return undefined. // 6. Return the substring of locale corresponding to the unicode_region_subtag production of the // unicode_language_id. CString region = locale->GetIcuLocale()->getCountry(); if (region.empty()) { return globalConst->GetUndefined(); } return factory->NewFromUtf8(region).GetTaggedValue(); } } // namespace panda::ecmascript::builtins