• 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/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