arkcompiler_ets_runtime/ecmascript/builtins/builtins_locale.cpp
liujia178 d0a5a0ba2d Fix intl/locale/property.js
Signed-off-by: liujia178 <liujia178@huawei.com>
2024-04-23 13:42:08 +08:00

376 lines
16 KiB
C++

/*
* 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/ecma_vm.h"
#include "ecmascript/global_env.h"
#include "ecmascript/js_locale.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<JSTaggedValue> 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<JSTaggedValue> constructor = GetConstructor(argv);
JSHandle<JSObject> newObject = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), newTarget);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSLocale> locale =JSHandle<JSLocale>::Cast(newObject);
// 7. If Type(tag) is not String or Object, throw a TypeError exception.
JSHandle<JSTaggedValue> 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<EcmaString> localeString = factory->GetEmptyString();
if (!tag->IsJSLocale()) {
localeString = JSTaggedValue::ToString(thread, tag);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
} else {
icu::Locale *icuLocale = (JSHandle<JSLocale>::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<JSTaggedValue> options = GetCallArg(argv, 1);
JSHandle<JSObject> optionsObj;
if (options->IsUndefined()) {
optionsObj = factory->CreateNullJSObject();
} else {
optionsObj = JSTaggedValue::ToObject(thread, options);
}
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSLocale> 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<JSTaggedValue> 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<JSLocale> locale = JSHandle<JSLocale>::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<GlobalEnv> env = ecmaVm->GetGlobalEnv();
ObjectFactory *factory = ecmaVm->GetFactory();
JSHandle<JSFunction> ctor(env->GetLocaleFunction());
JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(ctor);
factory->NewJSIntlIcuData(JSHandle<JSLocale>::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<JSTaggedValue> 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<JSLocale> locale = JSHandle<JSLocale>::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<CString>(status);
// 4. Return ! Construct(%Locale%, minimal).
EcmaVM *ecmaVm = thread->GetEcmaVM();
JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
ObjectFactory *factory = ecmaVm->GetFactory();
JSHandle<JSFunction> ctor(env->GetLocaleFunction());
JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(ctor);
factory->NewJSIntlIcuData(JSHandle<JSLocale>::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<JSTaggedValue> 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<EcmaString> result = JSLocale::ToString(thread, JSHandle<JSLocale>::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<JSTaggedValue> 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<JSLocale> locale = JSHandle<JSLocale>::Cast(loc);
icu::Locale icuLocale = icu::Locale::createFromName(locale->GetIcuLocale()->getBaseName());
JSHandle<EcmaString> 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<JSTaggedValue> 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<JSLocale> locale = JSHandle<JSLocale>::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<JSTaggedValue> 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<JSLocale> locale = JSHandle<JSLocale>::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<JSTaggedValue> 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<JSLocale> locale = JSHandle<JSLocale>::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<JSTaggedValue> 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<JSLocale> locale = JSHandle<JSLocale>::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<JSTaggedValue> 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<JSLocale> locale = JSHandle<JSLocale>::Cast(loc);
icu::Locale *icuLocale = locale->GetIcuLocale();
UErrorCode status = U_ZERO_ERROR;
auto numeric = icuLocale->getUnicodeKeywordValue<CString>("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<JSTaggedValue> 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<JSLocale> locale = JSHandle<JSLocale>::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<JSTaggedValue> 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<JSLocale> locale = JSHandle<JSLocale>::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<EcmaString> result = JSHandle<EcmaString>::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<JSTaggedValue> 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<JSLocale> locale = JSHandle<JSLocale>::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<EcmaString> 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<JSTaggedValue> 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<JSLocale> locale = JSHandle<JSLocale>::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