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