1/* 2 * Copyright (c) 2021 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include "ecmascript/builtins/builtins_locale.h" 17 18#include "ecmascript/intl/locale_helper.h" 19#include "ecmascript/global_env.h" 20#include "ecmascript/object_factory-inl.h" 21 22namespace panda::ecmascript::builtins { 23// 10.1.3 Intl.Locale( tag [, options] ) 24JSTaggedValue BuiltinsLocale::LocaleConstructor(EcmaRuntimeCallInfo *argv) 25{ 26 JSThread *thread = argv->GetThread(); 27 BUILTINS_API_TRACE(thread, Locale, Constructor); 28 [[maybe_unused]] EcmaHandleScope scope(thread); 29 EcmaVM *ecmaVm = thread->GetEcmaVM(); 30 ObjectFactory *factory = ecmaVm->GetFactory(); 31 32 // 1. If NewTarget is undefined, throw a TypeError exception. 33 JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv); 34 if (newTarget->IsUndefined()) { 35 THROW_TYPE_ERROR_AND_RETURN(thread, "newTarget is undefined", JSTaggedValue::Exception()); 36 } 37 38 // 6. Let locale be ? OrdinaryCreateFromConstructor(NewTarget, %LocalePrototype%, internalSlotsList). 39 JSHandle<JSTaggedValue> constructor = GetConstructor(argv); 40 JSHandle<JSObject> newObject = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), newTarget); 41 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 42 JSHandle<JSLocale> locale =JSHandle<JSLocale>::Cast(newObject); 43 44 // 7. If Type(tag) is not String or Object, throw a TypeError exception. 45 JSHandle<JSTaggedValue> tag = GetCallArg(argv, 0); 46 if (!tag->IsString() && !tag->IsJSObject()) { 47 THROW_TYPE_ERROR_AND_RETURN(thread, "tag is not String or Object", JSTaggedValue::Exception()); 48 } 49 50 // 8. If Type(tag) is Object and tag has an [[InitializedLocale]] internal slot, then 51 // a.Let tag be tag.[[Locale]]. 52 // 9. Else, 53 // a.Let tag be ? ToString(tag). 54 JSHandle<EcmaString> localeString = factory->GetEmptyString(); 55 if (!tag->IsJSLocale()) { 56 localeString = JSTaggedValue::ToString(thread, tag); 57 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 58 } else { 59 icu::Locale *icuLocale = (JSHandle<JSLocale>::Cast(tag))->GetIcuLocale(); 60 localeString = intl::LocaleHelper::ToLanguageTag(thread, *icuLocale); 61 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 62 } 63 // 10. If options is undefined, then 64 // a.Let options be ! ObjectCreate(null). 65 // 11. Else 66 // a.Let options be ? ToObject(options). 67 JSHandle<JSTaggedValue> options = GetCallArg(argv, 1); 68 JSHandle<JSObject> optionsObj; 69 if (options->IsUndefined()) { 70 optionsObj = factory->CreateNullJSObject(); 71 } else { 72 optionsObj = JSTaggedValue::ToObject(thread, options); 73 } 74 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 75 76 JSHandle<JSLocale> result = JSLocale::InitializeLocale(thread, locale, localeString, optionsObj); 77 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 78 return result.GetTaggedValue(); 79} 80 81JSTaggedValue BuiltinsLocale::Maximize(EcmaRuntimeCallInfo *argv) 82{ 83 JSThread *thread = argv->GetThread(); 84 BUILTINS_API_TRACE(thread, Locale, Maximize); 85 [[maybe_unused]] EcmaHandleScope scope(thread); 86 // 1. Let loc be the this value. 87 JSHandle<JSTaggedValue> loc = GetThis(argv); 88 89 // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). 90 if (!loc->IsJSLocale()) { 91 THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); 92 } 93 // 3. Let maximal be the result of the Add Likely Subtags algorithm applied to loc.[[Locale]]. If an error is 94 // signaled, set maximal to loc.[[Locale]]. 95 JSHandle<JSLocale> locale = JSHandle<JSLocale>::Cast(loc); 96 icu::Locale source(*(locale->GetIcuLocale())); 97 UErrorCode status = U_ZERO_ERROR; 98 source.addLikelySubtags(status); 99 ASSERT(U_SUCCESS(status)); 100 ASSERT(!source.isBogus()); 101 102 // 4. Return ! Construct(%Locale%, maximal). 103 EcmaVM *ecmaVm = thread->GetEcmaVM(); 104 JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv(); 105 ObjectFactory *factory = ecmaVm->GetFactory(); 106 107 JSHandle<JSFunction> ctor(env->GetLocaleFunction()); 108 JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(ctor); 109 factory->NewJSIntlIcuData(JSHandle<JSLocale>::Cast(obj), source, JSLocale::FreeIcuLocale); 110 return obj.GetTaggedValue(); 111} 112 113JSTaggedValue BuiltinsLocale::Minimize(EcmaRuntimeCallInfo *argv) 114{ 115 JSThread *thread = argv->GetThread(); 116 BUILTINS_API_TRACE(thread, Locale, Minimize); 117 [[maybe_unused]] EcmaHandleScope scope(thread); 118 // 1. Let loc be the this value. 119 JSHandle<JSTaggedValue> loc = GetThis(argv); 120 121 // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). 122 if (!loc->IsJSLocale()) { 123 THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); 124 } 125 126 // 3. Let minimal be the result of the Remove Likely Subtags algorithm applied to loc.[[Locale]]. 127 // If an error is signaled, set minimal to loc.[[Locale]]. 128 JSHandle<JSLocale> locale = JSHandle<JSLocale>::Cast(loc); 129 icu::Locale source(*(locale->GetIcuLocale())); 130 UErrorCode status = U_ZERO_ERROR; 131 source.minimizeSubtags(status); 132 ASSERT(U_SUCCESS(status)); 133 ASSERT(!source.isBogus()); 134 135 [[maybe_unused]] auto res = source.toLanguageTag<CString>(status); 136 137 // 4. Return ! Construct(%Locale%, minimal). 138 EcmaVM *ecmaVm = thread->GetEcmaVM(); 139 JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv(); 140 ObjectFactory *factory = ecmaVm->GetFactory(); 141 142 JSHandle<JSFunction> ctor(env->GetLocaleFunction()); 143 JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(ctor); 144 factory->NewJSIntlIcuData(JSHandle<JSLocale>::Cast(obj), source, JSLocale::FreeIcuLocale); 145 return obj.GetTaggedValue(); 146} 147 148JSTaggedValue BuiltinsLocale::ToString(EcmaRuntimeCallInfo *argv) 149{ 150 JSThread *thread = argv->GetThread(); 151 BUILTINS_API_TRACE(thread, Locale, ToString); 152 [[maybe_unused]] EcmaHandleScope scope(thread); 153 // 1. Let loc be the this value. 154 JSHandle<JSTaggedValue> loc = GetThis(argv); 155 // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). 156 if (!loc->IsJSLocale()) { 157 THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); 158 } 159 // 3. Return loc.[[Locale]]. 160 JSHandle<EcmaString> result = JSLocale::ToString(thread, JSHandle<JSLocale>::Cast(loc)); 161 return result.GetTaggedValue(); 162} 163 164JSTaggedValue BuiltinsLocale::GetBaseName(EcmaRuntimeCallInfo *argv) 165{ 166 JSThread *thread = argv->GetThread(); 167 BUILTINS_API_TRACE(thread, Locale, GetBaseName); 168 [[maybe_unused]] EcmaHandleScope scope(thread); 169 // 1. Let loc be the this value. 170 JSHandle<JSTaggedValue> loc = GetThis(argv); 171 // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). 172 if (!loc->IsJSLocale()) { 173 THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); 174 } 175 // 3. Let locale be loc.[[Locale]]. 176 // 4. Return the substring of locale corresponding to the unicode_language_id production. 177 JSHandle<JSLocale> locale = JSHandle<JSLocale>::Cast(loc); 178 icu::Locale icuLocale = icu::Locale::createFromName(locale->GetIcuLocale()->getBaseName()); 179 JSHandle<EcmaString> baseName = intl::LocaleHelper::ToLanguageTag(thread, icuLocale); 180 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 181 return baseName.GetTaggedValue(); 182} 183 184JSTaggedValue BuiltinsLocale::GetCalendar(EcmaRuntimeCallInfo *argv) 185{ 186 JSThread *thread = argv->GetThread(); 187 BUILTINS_API_TRACE(thread, Locale, GetCalendar); 188 [[maybe_unused]] EcmaHandleScope scope(thread); 189 // 1. Let loc be the this value. 190 JSHandle<JSTaggedValue> loc = GetThis(argv); 191 // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). 192 if (!loc->IsJSLocale()) { 193 THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); 194 } 195 // 3. Return loc.[[Calendar]]. 196 JSHandle<JSLocale> locale = JSHandle<JSLocale>::Cast(loc); 197 JSTaggedValue calendar = JSLocale::NormalizeKeywordValue(thread, locale, "ca"); 198 return calendar; 199} 200 201JSTaggedValue BuiltinsLocale::GetCaseFirst(EcmaRuntimeCallInfo *argv) 202{ 203 // This property only exists if %Locale%.[[RelevantExtensionKeys]] contains "kf". 204 JSThread *thread = argv->GetThread(); 205 BUILTINS_API_TRACE(thread, Locale, GetCaseFirst); 206 [[maybe_unused]] EcmaHandleScope scope(thread); 207 // 1. Let loc be the this value. 208 JSHandle<JSTaggedValue> loc = GetThis(argv); 209 // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). 210 if (!loc->IsJSLocale()) { 211 THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); 212 } 213 // 3. Return loc.[[CaseFirst]]. 214 JSHandle<JSLocale> locale = JSHandle<JSLocale>::Cast(loc); 215 JSTaggedValue caseFirst = JSLocale::NormalizeKeywordValue(thread, locale, "kf"); 216 return caseFirst; 217} 218 219JSTaggedValue BuiltinsLocale::GetCollation(EcmaRuntimeCallInfo *argv) 220{ 221 JSThread *thread = argv->GetThread(); 222 BUILTINS_API_TRACE(thread, Locale, GetCollation); 223 [[maybe_unused]] EcmaHandleScope scope(thread); 224 // 1. Let loc be the this value. 225 JSHandle<JSTaggedValue> loc = GetThis(argv); 226 // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). 227 if (!loc->IsJSLocale()) { 228 THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); 229 } 230 // 3. Return loc.[[Collation]]. 231 JSHandle<JSLocale> locale = JSHandle<JSLocale>::Cast(loc); 232 JSTaggedValue collation = JSLocale::NormalizeKeywordValue(thread, locale, "co"); 233 return collation; 234} 235 236JSTaggedValue BuiltinsLocale::GetHourCycle(EcmaRuntimeCallInfo *argv) 237{ 238 JSThread *thread = argv->GetThread(); 239 BUILTINS_API_TRACE(thread, Locale, GetHourCycle); 240 [[maybe_unused]] EcmaHandleScope scope(thread); 241 // 1. Let loc be the this value. 242 JSHandle<JSTaggedValue> loc = GetThis(argv); 243 // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). 244 if (!loc->IsJSLocale()) { 245 THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); 246 } 247 // 3. Return loc.[[HourCycle]]. 248 JSHandle<JSLocale> locale = JSHandle<JSLocale>::Cast(loc); 249 JSTaggedValue hourCycle = JSLocale::NormalizeKeywordValue(thread, locale, "hc"); 250 return hourCycle; 251} 252 253JSTaggedValue BuiltinsLocale::GetNumeric(EcmaRuntimeCallInfo *argv) 254{ 255 // This property only exists if %Locale%.[[RelevantExtensionKeys]] contains "kn". 256 JSThread *thread = argv->GetThread(); 257 BUILTINS_API_TRACE(thread, Locale, GetNumeric); 258 [[maybe_unused]] EcmaHandleScope scope(thread); 259 // 1. Let loc be the this value. 260 JSHandle<JSTaggedValue> loc = GetThis(argv); 261 // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). 262 if (!loc->IsJSLocale()) { 263 THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); 264 } 265 // 3. Return loc.[[Numeric]]. 266 JSHandle<JSLocale> locale = JSHandle<JSLocale>::Cast(loc); 267 icu::Locale *icuLocale = locale->GetIcuLocale(); 268 UErrorCode status = U_ZERO_ERROR; 269 auto numeric = icuLocale->getUnicodeKeywordValue<CString>("kn", status); 270 JSTaggedValue result = (numeric == "true") ? JSTaggedValue::True() : JSTaggedValue::False(); 271 return result; 272} 273 274JSTaggedValue BuiltinsLocale::GetNumberingSystem(EcmaRuntimeCallInfo *argv) 275{ 276 JSThread *thread = argv->GetThread(); 277 BUILTINS_API_TRACE(thread, Locale, GetNumberingSystem); 278 [[maybe_unused]] EcmaHandleScope scope(thread); 279 // 1. Let loc be the this value. 280 JSHandle<JSTaggedValue> loc = GetThis(argv); 281 // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). 282 if (!loc->IsJSLocale()) { 283 THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); 284 } 285 // 3. Return loc.[[NumberingSystem]]. 286 JSHandle<JSLocale> locale = JSHandle<JSLocale>::Cast(loc); 287 JSTaggedValue numberingSystem = JSLocale::NormalizeKeywordValue(thread, locale, "nu"); 288 return numberingSystem; 289} 290 291JSTaggedValue BuiltinsLocale::GetLanguage(EcmaRuntimeCallInfo *argv) 292{ 293 JSThread *thread = argv->GetThread(); 294 BUILTINS_API_TRACE(thread, Locale, GetLanguage); 295 [[maybe_unused]] EcmaHandleScope scope(thread); 296 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 297 // 1. Let loc be the this value. 298 JSHandle<JSTaggedValue> loc = GetThis(argv); 299 // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). 300 if (!loc->IsJSLocale()) { 301 THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); 302 } 303 // 3. Let locale be loc.[[Locale]]. 304 JSHandle<JSLocale> locale = JSHandle<JSLocale>::Cast(loc); 305 // 4. Assert: locale matches the unicode_locale_id production. 306 // 5. Return the substring of locale corresponding to the unicode_language_subtag production of the 307 // unicode_language_id. 308 JSHandle<EcmaString> result = JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledUndefinedString()); 309 CString language = locale->GetIcuLocale()->getLanguage(); 310 if (language.empty()) { 311 return result.GetTaggedValue(); 312 } 313 result = factory->NewFromUtf8(language); 314 return result.GetTaggedValue(); 315} 316 317JSTaggedValue BuiltinsLocale::GetScript(EcmaRuntimeCallInfo *argv) 318{ 319 JSThread *thread = argv->GetThread(); 320 BUILTINS_API_TRACE(thread, Locale, GetScript); 321 [[maybe_unused]] EcmaHandleScope scope(thread); 322 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 323 // 1. Let loc be the this value. 324 JSHandle<JSTaggedValue> loc = GetThis(argv); 325 // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). 326 if (!loc->IsJSLocale()) { 327 THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); 328 } 329 // 3. Let locale be loc.[[Locale]]. 330 JSHandle<JSLocale> locale = JSHandle<JSLocale>::Cast(loc); 331 332 // 4. Assert: locale matches the unicode_locale_id production. 333 // 5. If the unicode_language_id production of locale does not contain the ["-" unicode_script_subtag] sequence, 334 // return undefined. 335 // 6. Return the substring of locale corresponding to the unicode_script_subtag production of the 336 // unicode_language_id. 337 JSHandle<EcmaString> result(thread, JSTaggedValue::Undefined()); 338 CString script = locale->GetIcuLocale()->getScript(); 339 if (script.empty()) { 340 return result.GetTaggedValue(); 341 } 342 result = factory->NewFromUtf8(script); 343 return result.GetTaggedValue(); 344} 345 346JSTaggedValue BuiltinsLocale::GetRegion(EcmaRuntimeCallInfo *argv) 347{ 348 JSThread *thread = argv->GetThread(); 349 BUILTINS_API_TRACE(thread, Locale, GetRegion); 350 [[maybe_unused]] EcmaHandleScope scope(thread); 351 EcmaVM *ecmaVm = thread->GetEcmaVM(); 352 const GlobalEnvConstants *globalConst = thread->GlobalConstants(); 353 ObjectFactory *factory = ecmaVm->GetFactory(); 354 // 1. Let loc be the this value. 355 JSHandle<JSTaggedValue> loc = GetThis(argv); 356 // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). 357 if (!loc->IsJSLocale()) { 358 THROW_TYPE_ERROR_AND_RETURN(thread, "not locale", JSTaggedValue::Exception()); 359 } 360 // 3. Let locale be loc.[[Locale]]. 361 JSHandle<JSLocale> locale = JSHandle<JSLocale>::Cast(loc); 362 // 4. Assert: locale matches the unicode_locale_id production. 363 // 5. If the unicode_language_id production of locale does not contain the ["-" unicode_region_subtag] sequence, 364 // return undefined. 365 // 6. Return the substring of locale corresponding to the unicode_region_subtag production of the 366 // unicode_language_id. 367 CString region = locale->GetIcuLocale()->getCountry(); 368 if (region.empty()) { 369 return globalConst->GetUndefined(); 370 } 371 return factory->NewFromUtf8(region).GetTaggedValue(); 372} 373} // namespace panda::ecmascript::builtins 374