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