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