* 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 {
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();
JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
if (newTarget->IsUndefined()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "newTarget is undefined", JSTaggedValue::Exception());
}
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);
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());
}
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(thread);
localeString = intl::LocaleHelper::ToLanguageTag(thread, *icuLocale);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
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);
JSHandle<JSTaggedValue> loc = GetThis(argv);
if (!loc->IsJSLocale()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception());
}
JSHandle<JSLocale> locale = JSHandle<JSLocale>::Cast(loc);
icu::Locale source(*(locale->GetIcuLocale(thread)));
UErrorCode status = U_ZERO_ERROR;
source.addLikelySubtags(status);
ASSERT(U_SUCCESS(status));
ASSERT(!source.isBogus());
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);
JSHandle<JSTaggedValue> loc = GetThis(argv);
if (!loc->IsJSLocale()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception());
}
JSHandle<JSLocale> locale = JSHandle<JSLocale>::Cast(loc);
icu::Locale source(*(locale->GetIcuLocale(thread)));
UErrorCode status = U_ZERO_ERROR;
source.minimizeSubtags(status);
ASSERT(U_SUCCESS(status));
ASSERT(!source.isBogus());
[[maybe_unused]] auto res = source.toLanguageTag<CString>(status);
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);
JSHandle<JSTaggedValue> loc = GetThis(argv);
if (!loc->IsJSLocale()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception());
}
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);
JSHandle<JSTaggedValue> loc = GetThis(argv);
if (!loc->IsJSLocale()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception());
}
JSHandle<JSLocale> locale = JSHandle<JSLocale>::Cast(loc);
icu::Locale icuLocale = icu::Locale::createFromName(locale->GetIcuLocale(thread)->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);
JSHandle<JSTaggedValue> loc = GetThis(argv);
if (!loc->IsJSLocale()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception());
}
JSHandle<JSLocale> locale = JSHandle<JSLocale>::Cast(loc);
JSTaggedValue calendar = JSLocale::NormalizeKeywordValue(thread, locale, "ca");
return calendar;
}
JSTaggedValue BuiltinsLocale::GetCaseFirst(EcmaRuntimeCallInfo *argv)
{
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, Locale, GetCaseFirst);
[[maybe_unused]] EcmaHandleScope scope(thread);
JSHandle<JSTaggedValue> loc = GetThis(argv);
if (!loc->IsJSLocale()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception());
}
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);
JSHandle<JSTaggedValue> loc = GetThis(argv);
if (!loc->IsJSLocale()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception());
}
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);
JSHandle<JSTaggedValue> loc = GetThis(argv);
if (!loc->IsJSLocale()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception());
}
JSHandle<JSLocale> locale = JSHandle<JSLocale>::Cast(loc);
JSTaggedValue hourCycle = JSLocale::NormalizeKeywordValue(thread, locale, "hc");
return hourCycle;
}
JSTaggedValue BuiltinsLocale::GetNumeric(EcmaRuntimeCallInfo *argv)
{
JSThread *thread = argv->GetThread();
BUILTINS_API_TRACE(thread, Locale, GetNumeric);
[[maybe_unused]] EcmaHandleScope scope(thread);
JSHandle<JSTaggedValue> loc = GetThis(argv);
if (!loc->IsJSLocale()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception());
}
JSHandle<JSLocale> locale = JSHandle<JSLocale>::Cast(loc);
icu::Locale *icuLocale = locale->GetIcuLocale(thread);
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);
JSHandle<JSTaggedValue> loc = GetThis(argv);
if (!loc->IsJSLocale()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception());
}
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();
JSHandle<JSTaggedValue> loc = GetThis(argv);
if (!loc->IsJSLocale()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception());
}
JSHandle<JSLocale> locale = JSHandle<JSLocale>::Cast(loc);
JSHandle<EcmaString> result = JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledUndefinedString());
CString language = locale->GetIcuLocale(thread)->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();
JSHandle<JSTaggedValue> loc = GetThis(argv);
if (!loc->IsJSLocale()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception());
}
JSHandle<JSLocale> locale = JSHandle<JSLocale>::Cast(loc);
JSHandle<EcmaString> result(thread, JSTaggedValue::Undefined());
CString script = locale->GetIcuLocale(thread)->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();
JSHandle<JSTaggedValue> loc = GetThis(argv);
if (!loc->IsJSLocale()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception());
}
JSHandle<JSLocale> locale = JSHandle<JSLocale>::Cast(loc);
CString region = locale->GetIcuLocale(thread)->getCountry();
if (region.empty()) {
return globalConst->GetUndefined();
}
return factory->NewFromUtf8(region).GetTaggedValue();
}
}