• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(thread);
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(thread)));
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(thread)));
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(thread)->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(thread);
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(thread)->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(thread)->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(thread)->getCountry();
368     if (region.empty()) {
369         return globalConst->GetUndefined();
370     }
371     return factory->NewFromUtf8(region).GetTaggedValue();
372 }
373 }  // namespace panda::ecmascript::builtins
374