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
22 namespace panda::ecmascript::builtins {
23 // 10.1.3 Intl.Locale( tag [, options] )
LocaleConstructor(EcmaRuntimeCallInfo * argv)24 JSTaggedValue 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
Maximize(EcmaRuntimeCallInfo * argv)81 JSTaggedValue 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
Minimize(EcmaRuntimeCallInfo * argv)113 JSTaggedValue 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
ToString(EcmaRuntimeCallInfo * argv)148 JSTaggedValue 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
GetBaseName(EcmaRuntimeCallInfo * argv)164 JSTaggedValue 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
GetCalendar(EcmaRuntimeCallInfo * argv)184 JSTaggedValue 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
GetCaseFirst(EcmaRuntimeCallInfo * argv)201 JSTaggedValue 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
GetCollation(EcmaRuntimeCallInfo * argv)219 JSTaggedValue 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
GetHourCycle(EcmaRuntimeCallInfo * argv)236 JSTaggedValue 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
GetNumeric(EcmaRuntimeCallInfo * argv)253 JSTaggedValue 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
GetNumberingSystem(EcmaRuntimeCallInfo * argv)274 JSTaggedValue 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
GetLanguage(EcmaRuntimeCallInfo * argv)291 JSTaggedValue 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
GetScript(EcmaRuntimeCallInfo * argv)317 JSTaggedValue 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
GetRegion(EcmaRuntimeCallInfo * argv)346 JSTaggedValue 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