• 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 "builtins_date_time_format.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_date.h"
22 #include "ecmascript/js_date_time_format.h"
23 #include "ecmascript/js_intl.h"
24 #include "ecmascript/mem/barriers-inl.h"
25 
26 namespace panda::ecmascript::builtins {
27 // 13.2.1 Intl.DateTimeFormat ( [ locales [ , options ] ] )
DateTimeFormatConstructor(EcmaRuntimeCallInfo * argv)28 JSTaggedValue BuiltinsDateTimeFormat::DateTimeFormatConstructor(EcmaRuntimeCallInfo *argv)
29 {
30     JSThread *thread = argv->GetThread();
31     BUILTINS_API_TRACE(thread, DateTimeFormat, Constructor);
32     [[maybe_unused]] EcmaHandleScope scope(thread);
33     EcmaVM *ecmaVm = thread->GetEcmaVM();
34     JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
35     ObjectFactory *factory = ecmaVm->GetFactory();
36 
37     // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget.
38     JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
39     JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
40     if (newTarget->IsUndefined()) {
41         newTarget = constructor;
42     }
43 
44     // 2. Let dateTimeFormat be ? OrdinaryCreateFromConstructor(newTarget, "%DateTimeFormatPrototype%", «
45     //    [[InitializedDateTimeFormat]], [[Locale]], [[Calendar]], [[NumberingSystem]], [[TimeZone]], [[Weekday]],
46     //    [[Era]], [[Year]], [[Month]], [[Day]], [[Hour]], [[Minute]], [[Second]], [[TimeZoneName]], [[HourCycle]],
47     //    [[Pattern]], [[BoundFormat]] »).
48     JSHandle<JSDateTimeFormat> dateTimeFormat = JSHandle<JSDateTimeFormat>::Cast(
49         factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), newTarget));
50     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
51 
52     // 3. Perform ? InitializeDateTimeFormat(dateTimeFormat, locales, options).
53     JSHandle<JSTaggedValue> locales = GetCallArg(argv, 0);
54     JSHandle<JSTaggedValue> options = GetCallArg(argv, 1);
55     JSHandle<JSDateTimeFormat> dtf =
56         JSDateTimeFormat::InitializeDateTimeFormat(thread, dateTimeFormat, locales, options);
57     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
58 
59     // 4. Let this be the this value.
60     JSHandle<JSTaggedValue> thisValue = GetThis(argv);
61 
62     // 5. If NewTarget is undefined and Type(this) is Object and ? InstanceofOperator(this, %DateTimeFormat%) is true,
63     //    then
64     //    a. Perform ? DefinePropertyOrThrow(this, %Intl%.[[FallbackSymbol]], PropertyDescriptor{ [[Value]]:
65     //       dateTimeFormat, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }).
66     //    b. Return this.
67     bool isInstanceOf = JSObject::InstanceOf(thread, thisValue, env->GetDateTimeFormatFunction());
68     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
69     if (newTarget->IsUndefined() && thisValue->IsJSObject() && isInstanceOf) {
70         PropertyDescriptor descriptor(thread, JSHandle<JSTaggedValue>::Cast(dtf), false, false, false);
71         JSHandle<JSTaggedValue> key(thread, JSHandle<JSIntl>::Cast(env->GetIntlFunction())->GetFallbackSymbol());
72         JSTaggedValue::DefinePropertyOrThrow(thread, thisValue, key, descriptor);
73         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
74         return thisValue.GetTaggedValue();
75     }
76 
77     // 6. Return dateTimeFormat.
78     return dtf.GetTaggedValue();
79 }
80 
81 // 13.3.2 Intl.DateTimeFormat.supportedLocalesOf ( locales [ , options ] )
SupportedLocalesOf(EcmaRuntimeCallInfo * argv)82 JSTaggedValue BuiltinsDateTimeFormat::SupportedLocalesOf(EcmaRuntimeCallInfo *argv)
83 {
84     JSThread *thread = argv->GetThread();
85     BUILTINS_API_TRACE(thread, DateTimeFormat, SupportedLocalesOf);
86     [[maybe_unused]] EcmaHandleScope scope(thread);
87     // 1. Let availableLocales be %DateTimeFormat%.[[AvailableLocales]].
88     JSHandle<TaggedArray> availableLocales = JSDateTimeFormat::GainAvailableLocales(thread);
89 
90     // 2. Let requestedLocales be ? CanonicalizeLocaleList(locales).
91     JSHandle<JSTaggedValue> locales = GetCallArg(argv, 0);
92     JSHandle<TaggedArray> requestedLocales = intl::LocaleHelper::CanonicalizeLocaleList(thread, locales);
93     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
94 
95     // 3. Return ? SupportedLocales(availableLocales, requestedLocales, options).
96     JSHandle<JSTaggedValue> options = GetCallArg(argv, 1);
97     JSHandle<JSArray> result = JSLocale::SupportedLocales(thread, availableLocales, requestedLocales, options);
98     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
99     return result.GetTaggedValue();
100 }
101 
102 // 13.4.3 get Intl.DateTimeFormat.prototype.format
Format(EcmaRuntimeCallInfo * argv)103 JSTaggedValue BuiltinsDateTimeFormat::Format(EcmaRuntimeCallInfo *argv)
104 {
105     JSThread *thread = argv->GetThread();
106     BUILTINS_API_TRACE(thread, DateTimeFormat, Format);
107     [[maybe_unused]] EcmaHandleScope scope(thread);
108 
109     // 1. Let dtf be this value.
110     JSHandle<JSTaggedValue> thisValue = GetThis(argv);
111 
112     // 2. If Type(dtf) is not Object, throw a TypeError exception.
113     if (!thisValue->IsJSObject()) {
114         THROW_TYPE_ERROR_AND_RETURN(thread, "dtf is not object", JSTaggedValue::Exception());
115     }
116 
117     // 3. Let dtf be ? UnwrapDateTimeFormat(dtf).
118     JSHandle<JSTaggedValue> dtfValue = JSDateTimeFormat::UnwrapDateTimeFormat(thread, thisValue);
119     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
120     if (dtfValue->IsUndefined()) {
121         THROW_TYPE_ERROR_AND_RETURN(thread, "dtfValue is not object", JSTaggedValue::Exception());
122     }
123 
124     // 4. If dtf.[[BoundFormat]] is undefined, then
125     //    a. Let F be a new built-in function object as defined in DateTime Format Functions (13.1.5).
126     //    b. Set F.[[DateTimeFormat]] to dtf.
127     //    c. Set dtf.[[BoundFormat]] to F.
128     // 5. Return dtf.[[BoundFormat]].
129     JSHandle<JSDateTimeFormat> dtf = JSHandle<JSDateTimeFormat>::Cast(dtfValue);
130     JSHandle<JSTaggedValue> boundFormat(thread, dtf->GetBoundFormat());
131     if (boundFormat->IsUndefined()) {
132         ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
133         JSHandle<JSIntlBoundFunction> intlBoundFunc = factory->NewJSIntlBoundFunction(
134             MethodIndex::BUILTINS_DATE_TIME_FORMAT_ANONYMOUS_DATE_TIME_FORMAT);
135         intlBoundFunc->SetDateTimeFormat(thread, dtf);
136         dtf->SetBoundFormat(thread, intlBoundFunc);
137     }
138     return dtf->GetBoundFormat();
139 }
140 
141 // 13.1.5 DateTime Format Functions
AnonymousDateTimeFormat(EcmaRuntimeCallInfo * argv)142 JSTaggedValue BuiltinsDateTimeFormat::AnonymousDateTimeFormat(EcmaRuntimeCallInfo *argv)
143 {
144     // A DateTime format function is an anonymous built-in function that has a [[DateTimeFormat]] internal slot.
145     // When a DateTime format function F is called with optional argument date, the following steps are taken:
146     JSThread *thread = argv->GetThread();
147     BUILTINS_API_TRACE(thread, DateTimeFormat, AnonymousDateTimeFormat);
148     [[maybe_unused]] EcmaHandleScope scope(thread);
149     JSHandle<JSIntlBoundFunction> intlBoundFunc = JSHandle<JSIntlBoundFunction>::Cast(GetConstructor(argv));
150 
151     // 1. Let dtf be F.[[DateTimeFormat]].
152     JSHandle<JSTaggedValue> dtf(thread, intlBoundFunc->GetDateTimeFormat());
153 
154     // 2. Assert: Type(dtf) is Object and dtf has an [[InitializedDateTimeFormat]] internal slot.
155     ASSERT_PRINT(dtf->IsJSObject() && dtf->IsJSDateTimeFormat(), "dtf is not object or JSDateTimeFormat");
156 
157     // 3. If date is not provided or is undefined, then
158     //    a. Let x be Call(%Date_now%, undefined).
159     // 4. Else,
160     //    a. Let x be ? ToNumber(date).
161     JSHandle<JSTaggedValue> date = GetCallArg(argv, 0);
162     double x = 0.0;
163     if (date->IsUndefined()) {
164         x = JSDate::Now().GetNumber();
165     } else {
166         JSTaggedNumber xNumber = JSTaggedValue::ToNumber(thread, date);
167         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
168         x = xNumber.GetNumber();
169     }
170 
171     // 5. Return ? FormatDateTime(dtf, x).
172     JSHandle<EcmaString> result = JSDateTimeFormat::FormatDateTime(thread, JSHandle<JSDateTimeFormat>::Cast(dtf), x);
173     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
174     return result.GetTaggedValue();
175 }
176 
177 // 13.4.4 Intl.DateTimeFormat.prototype.formatToParts ( date )
FormatToParts(EcmaRuntimeCallInfo * argv)178 JSTaggedValue BuiltinsDateTimeFormat::FormatToParts(EcmaRuntimeCallInfo *argv)
179 {
180     JSThread *thread = argv->GetThread();
181     BUILTINS_API_TRACE(thread, DateTimeFormat, FormatToParts);
182     [[maybe_unused]] EcmaHandleScope scope(thread);
183     // 1. Let dtf be this value.
184     JSHandle<JSTaggedValue> dtf = GetThis(argv);
185     // 2. Perform ? RequireInternalSlot(dtf, [[InitializedDateTimeFormat]]).
186     if (!dtf->IsJSDateTimeFormat()) {
187         THROW_TYPE_ERROR_AND_RETURN(thread, "is not JSDateTimeFormat", JSTaggedValue::Exception());
188     }
189 
190     // 3. If date is undefined, then
191     //    a. Let x be Call(%Date_now%, undefined).
192     // 4. Else,
193     //    a. Let x be ? ToNumber(date).
194     JSHandle<JSTaggedValue> date = GetCallArg(argv, 0);
195     double x = 0.0;
196     if (date->IsUndefined()) {
197         x = JSDate::Now().GetNumber();
198     } else {
199         JSTaggedNumber xNumber = JSTaggedValue::ToNumber(thread, date);
200         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
201         x = xNumber.GetNumber();
202     }
203 
204     double xValue = JSDate::TimeClip(x);
205     if (std::isnan(xValue)) {
206         THROW_RANGE_ERROR_AND_RETURN(thread, "Invalid time value", JSTaggedValue::Exception());
207     }
208 
209     // 5. Return ? FormatDateTimeToParts(dtf, x).
210     JSHandle<JSArray> result =
211         JSDateTimeFormat::FormatDateTimeToParts(thread, JSHandle<JSDateTimeFormat>::Cast(dtf), xValue);
212     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
213     return result.GetTaggedValue();
214 }
215 
216 // 13.4.5 Intl.DateTimeFormat.prototype.resolvedOptions ()
ResolvedOptions(EcmaRuntimeCallInfo * argv)217 JSTaggedValue BuiltinsDateTimeFormat::ResolvedOptions(EcmaRuntimeCallInfo *argv)
218 {
219     JSThread *thread = argv->GetThread();
220     BUILTINS_API_TRACE(thread, DateTimeFormat, ResolvedOptions);
221     [[maybe_unused]] EcmaHandleScope scope(thread);
222     // 1. Let dtf be this value.
223     JSHandle<JSTaggedValue> thisValue = GetThis(argv);
224     // 2. If Type(dtf) is not Object, throw a TypeError exception.
225     if (!thisValue->IsJSObject()) {
226         THROW_TYPE_ERROR_AND_RETURN(thread, "this is not object", JSTaggedValue::Exception());
227     }
228     // 3. Let dtf be ? UnwrapDateTimeFormat(dtf).
229     thisValue = JSDateTimeFormat::UnwrapDateTimeFormat(thread, thisValue);
230     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
231 
232     // Perform ? RequireInternalSlot(dtf, [[InitializedDateTimeFormat]]).
233     if (!thisValue->IsJSDateTimeFormat()) {
234         THROW_TYPE_ERROR_AND_RETURN(thread, "dtf is not JSDateTimeFormat", JSTaggedValue::Exception());
235     }
236 
237     auto ecmaVm = thread->GetEcmaVM();
238     JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
239     ObjectFactory *factory = ecmaVm->GetFactory();
240     JSHandle<JSFunction> ctor(env->GetObjectFunction());
241     JSHandle<JSObject> options(factory->NewJSObjectByConstructor(ctor));
242     JSDateTimeFormat::ResolvedOptions(thread, JSHandle<JSDateTimeFormat>::Cast(thisValue), options);
243     // 6. Return options.
244     return options.GetTaggedValue();
245 }
246 
247 // Intl.DateTimeFormat.prototype.formatRange
FormatRange(EcmaRuntimeCallInfo * argv)248 JSTaggedValue BuiltinsDateTimeFormat::FormatRange(EcmaRuntimeCallInfo *argv)
249 {
250     JSThread *thread = argv->GetThread();
251     BUILTINS_API_TRACE(thread, DateTimeFormat, FormatRange);
252     [[maybe_unused]] EcmaHandleScope scope(thread);
253 
254     // 1. Let dtf be this value.
255     JSHandle<JSTaggedValue> thisValue = GetThis(argv);
256     // 2. If Type(dtf) is not Object, throw a TypeError exception.
257     if (!thisValue->IsJSObject()) {
258         THROW_TYPE_ERROR_AND_RETURN(thread, "this is not object", JSTaggedValue::Exception());
259     }
260 
261     // 3. If dtf does not have an [[InitializedDateTimeFormat]] internal slot, throw a TypeError exception.
262     if (!thisValue->IsJSDateTimeFormat()) {
263         THROW_TYPE_ERROR_AND_RETURN(thread, "this is not JSDateTimeFormat", JSTaggedValue::Exception());
264     }
265 
266     // 4. If startDate is undefined or endDate is undefined, throw a TypeError exception.
267     JSHandle<JSTaggedValue> startDate = GetCallArg(argv, 0);
268     JSHandle<JSTaggedValue> endDate = GetCallArg(argv, 1);
269     if (startDate->IsUndefined() || endDate->IsUndefined()) {
270         THROW_TYPE_ERROR_AND_RETURN(thread, "startDate or endDate is undefined", JSTaggedValue::Exception());
271     }
272 
273     // 5. Let x be ? ToNumber(startDate).
274     JSTaggedNumber valueX = JSTaggedValue::ToNumber(thread, startDate);
275     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
276     double x = valueX.GetNumber();
277 
278     // 6. Let y be ? ToNumber(endDate).
279     JSTaggedNumber valueY = JSTaggedValue::ToNumber(thread, endDate);
280     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
281     double y = valueY.GetNumber();
282     // 7. If x is greater than y, throw a RangeError exception.
283     if (x > y) {
284         THROW_RANGE_ERROR_AND_RETURN(thread, "x is greater than y", JSTaggedValue::Exception());
285     }
286 
287     // 8. Return ? FormatDateTimeRange(dtf, x, y)
288     JSHandle<JSDateTimeFormat> dtf = JSHandle<JSDateTimeFormat>::Cast(thisValue);
289     JSHandle<EcmaString> result = JSDateTimeFormat::NormDateTimeRange(thread, dtf, x, y);
290     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
291     return result.GetTaggedValue();
292 }
293 
294 // Intl.DateTimeFormat.prototype.formatRangeToParts
FormatRangeToParts(EcmaRuntimeCallInfo * argv)295 JSTaggedValue BuiltinsDateTimeFormat::FormatRangeToParts(EcmaRuntimeCallInfo *argv)
296 {
297     JSThread *thread = argv->GetThread();
298     BUILTINS_API_TRACE(thread, DateTimeFormat, FormatRangeToParts);
299     [[maybe_unused]] EcmaHandleScope scope(thread);
300     // 1. Let dtf be this value.
301     JSHandle<JSTaggedValue> thisValue = GetThis(argv);
302     // 2. If Type(dtf) is not Object, throw a TypeError exception.
303     if (!thisValue->IsJSObject()) {
304         THROW_TYPE_ERROR_AND_RETURN(thread, "this is not object", JSTaggedValue::Exception());
305     }
306 
307     // 3. If dtf does not have an [[InitializedDateTimeFormat]] internal slot,
308     //    throw a TypeError exception.
309     if (!thisValue->IsJSDateTimeFormat()) {
310         THROW_TYPE_ERROR_AND_RETURN(thread, "this is not JSDateTimeFormat", JSTaggedValue::Exception());
311     }
312 
313     // 4. If startDate is undefined or endDate is undefined, throw a TypeError exception.
314     JSHandle<JSTaggedValue> startDate = GetCallArg(argv, 0);
315     JSHandle<JSTaggedValue> endDate = GetCallArg(argv, 1);
316     if (startDate->IsUndefined() || endDate->IsUndefined()) {
317         THROW_TYPE_ERROR_AND_RETURN(thread, "startDate or endDate is undefined", JSTaggedValue::Exception());
318     }
319 
320     // 5. Let x be ? ToNumber(startDate).
321     JSTaggedNumber valueX = JSTaggedValue::ToNumber(thread, startDate);
322     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
323     double x = valueX.GetNumber();
324 
325     // 6. Let y be ? ToNumber(endDate).
326     JSTaggedNumber valueY = JSTaggedValue::ToNumber(thread, endDate);
327     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
328     double y = valueY.GetNumber();
329     // 7. If x is greater than y, throw a RangeError exception.
330     if (x > y) {
331         THROW_RANGE_ERROR_AND_RETURN(thread, "x is greater than y", JSTaggedValue::Exception());
332     }
333 
334     // 8. Return ? FormatDateTimeRangeToParts(dtf, x, y)
335     JSHandle<JSDateTimeFormat> dtf = JSHandle<JSDateTimeFormat>::Cast(thisValue);
336     JSHandle<JSArray> result = JSDateTimeFormat::NormDateTimeRangeToParts(thread, dtf, x, y);
337     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
338     return result.GetTaggedValue();
339 }
340 }  // namespace panda::ecmascript::builtins