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