1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sts=4 et sw=4 tw=99:
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 /*
8  * JS date methods.
9  *
10  * "For example, OS/360 devotes 26 bytes of the permanently
11  *  resident date-turnover routine to the proper handling of
12  *  December 31 on leap years (when it is Day 366).  That
13  *  might have been left to the operator."
14  *
15  * Frederick Brooks, 'The Second-System Effect'.
16  */
17 
18 #include "jsdate.h"
19 
20 #include "mozilla/ArrayUtils.h"
21 #include "mozilla/Atomics.h"
22 #include "mozilla/FloatingPoint.h"
23 #include "mozilla/Sprintf.h"
24 
25 #include <ctype.h>
26 #include <math.h>
27 #include <string.h>
28 
29 #include "jsapi.h"
30 #include "jsnum.h"
31 #include "jstypes.h"
32 #include "jsutil.h"
33 
34 #include "builtin/String.h"
35 #include "js/Conversions.h"
36 #include "js/Date.h"
37 #include "js/Wrapper.h"
38 #include "util/StringBuffer.h"
39 #include "vm/DateTime.h"
40 #include "vm/GlobalObject.h"
41 #include "vm/Interpreter.h"
42 #include "vm/JSContext.h"
43 #include "vm/JSObject.h"
44 #include "vm/StringType.h"
45 #include "vm/Time.h"
46 
47 #include "vm/JSObject-inl.h"
48 
49 using namespace js;
50 
51 using mozilla::ArrayLength;
52 using mozilla::Atomic;
53 using mozilla::IsFinite;
54 using mozilla::IsNaN;
55 using mozilla::NumbersAreIdentical;
56 using mozilla::Relaxed;
57 
58 using JS::AutoCheckCannotGC;
59 using JS::ClippedTime;
60 using JS::GenericNaN;
61 using JS::TimeClip;
62 using JS::ToInteger;
63 
64 // When this value is non-zero, we'll round the time by this resolution.
65 static Atomic<uint32_t, Relaxed> sResolutionUsec;
66 // This is not implemented yet, but we will use this to know to jitter the time
67 // in the JS shell
68 static Atomic<bool, Relaxed> sJitter;
69 // The callback we will use for the Gecko implementation of Timer
70 // Clamping/Jittering
71 static Atomic<JS::ReduceMicrosecondTimePrecisionCallback, Relaxed>
72     sReduceMicrosecondTimePrecisionCallback;
73 
74 /*
75  * The JS 'Date' object is patterned after the Java 'Date' object.
76  * Here is a script:
77  *
78  *    today = new Date();
79  *
80  *    print(today.toLocaleString());
81  *
82  *    weekDay = today.getDay();
83  *
84  *
85  * These Java (and ECMA-262) methods are supported:
86  *
87  *     UTC
88  *     getDate (getUTCDate)
89  *     getDay (getUTCDay)
90  *     getHours (getUTCHours)
91  *     getMinutes (getUTCMinutes)
92  *     getMonth (getUTCMonth)
93  *     getSeconds (getUTCSeconds)
94  *     getMilliseconds (getUTCMilliseconds)
95  *     getTime
96  *     getTimezoneOffset
97  *     getYear
98  *     getFullYear (getUTCFullYear)
99  *     parse
100  *     setDate (setUTCDate)
101  *     setHours (setUTCHours)
102  *     setMinutes (setUTCMinutes)
103  *     setMonth (setUTCMonth)
104  *     setSeconds (setUTCSeconds)
105  *     setMilliseconds (setUTCMilliseconds)
106  *     setTime
107  *     setYear (setFullYear, setUTCFullYear)
108  *     toGMTString (toUTCString)
109  *     toLocaleString
110  *     toString
111  *
112  *
113  * These Java methods are not supported
114  *
115  *     setDay
116  *     before
117  *     after
118  *     equals
119  *     hashCode
120  */
121 
Day(double t)122 static inline double Day(double t) { return floor(t / msPerDay); }
123 
TimeWithinDay(double t)124 static double TimeWithinDay(double t) {
125   double result = fmod(t, msPerDay);
126   if (result < 0) result += msPerDay;
127   return result;
128 }
129 
130 /* ES5 15.9.1.3. */
IsLeapYear(double year)131 static inline bool IsLeapYear(double year) {
132   MOZ_ASSERT(ToInteger(year) == year);
133   return fmod(year, 4) == 0 && (fmod(year, 100) != 0 || fmod(year, 400) == 0);
134 }
135 
DaysInYear(double year)136 static inline double DaysInYear(double year) {
137   if (!IsFinite(year)) return GenericNaN();
138   return IsLeapYear(year) ? 366 : 365;
139 }
140 
DayFromYear(double y)141 static inline double DayFromYear(double y) {
142   return 365 * (y - 1970) + floor((y - 1969) / 4.0) -
143          floor((y - 1901) / 100.0) + floor((y - 1601) / 400.0);
144 }
145 
TimeFromYear(double y)146 static inline double TimeFromYear(double y) {
147   return DayFromYear(y) * msPerDay;
148 }
149 
YearFromTime(double t)150 static double YearFromTime(double t) {
151   if (!IsFinite(t)) return GenericNaN();
152 
153   MOZ_ASSERT(ToInteger(t) == t);
154 
155   double y = floor(t / (msPerDay * 365.2425)) + 1970;
156   double t2 = TimeFromYear(y);
157 
158   /*
159    * Adjust the year if the approximation was wrong.  Since the year was
160    * computed using the average number of ms per year, it will usually
161    * be wrong for dates within several hours of a year transition.
162    */
163   if (t2 > t) {
164     y--;
165   } else {
166     if (t2 + msPerDay * DaysInYear(y) <= t) y++;
167   }
168   return y;
169 }
170 
DaysInFebruary(double year)171 static inline int DaysInFebruary(double year) {
172   return IsLeapYear(year) ? 29 : 28;
173 }
174 
175 /* ES5 15.9.1.4. */
DayWithinYear(double t,double year)176 static inline double DayWithinYear(double t, double year) {
177   MOZ_ASSERT_IF(IsFinite(t), YearFromTime(t) == year);
178   return Day(t) - DayFromYear(year);
179 }
180 
MonthFromTime(double t)181 static double MonthFromTime(double t) {
182   if (!IsFinite(t)) return GenericNaN();
183 
184   double year = YearFromTime(t);
185   double d = DayWithinYear(t, year);
186 
187   int step;
188   if (d < (step = 31)) return 0;
189   if (d < (step += DaysInFebruary(year))) return 1;
190   if (d < (step += 31)) return 2;
191   if (d < (step += 30)) return 3;
192   if (d < (step += 31)) return 4;
193   if (d < (step += 30)) return 5;
194   if (d < (step += 31)) return 6;
195   if (d < (step += 31)) return 7;
196   if (d < (step += 30)) return 8;
197   if (d < (step += 31)) return 9;
198   if (d < (step += 30)) return 10;
199   return 11;
200 }
201 
202 /* ES5 15.9.1.5. */
DateFromTime(double t)203 static double DateFromTime(double t) {
204   if (!IsFinite(t)) return GenericNaN();
205 
206   double year = YearFromTime(t);
207   double d = DayWithinYear(t, year);
208 
209   int next;
210   if (d <= (next = 30)) return d + 1;
211   int step = next;
212   if (d <= (next += DaysInFebruary(year))) return d - step;
213   step = next;
214   if (d <= (next += 31)) return d - step;
215   step = next;
216   if (d <= (next += 30)) return d - step;
217   step = next;
218   if (d <= (next += 31)) return d - step;
219   step = next;
220   if (d <= (next += 30)) return d - step;
221   step = next;
222   if (d <= (next += 31)) return d - step;
223   step = next;
224   if (d <= (next += 31)) return d - step;
225   step = next;
226   if (d <= (next += 30)) return d - step;
227   step = next;
228   if (d <= (next += 31)) return d - step;
229   step = next;
230   if (d <= (next += 30)) return d - step;
231   step = next;
232   return d - step;
233 }
234 
235 /* ES5 15.9.1.6. */
WeekDay(double t)236 static int WeekDay(double t) {
237   /*
238    * We can't assert TimeClip(t) == t because we call this function with
239    * local times, which can be offset outside TimeClip's permitted range.
240    */
241   MOZ_ASSERT(ToInteger(t) == t);
242   int result = (int(Day(t)) + 4) % 7;
243   if (result < 0) result += 7;
244   return result;
245 }
246 
DayFromMonth(int month,bool isLeapYear)247 static inline int DayFromMonth(int month, bool isLeapYear) {
248   /*
249    * The following array contains the day of year for the first day of
250    * each month, where index 0 is January, and day 0 is January 1.
251    */
252   static const int firstDayOfMonth[2][13] = {
253       {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
254       {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}};
255 
256   MOZ_ASSERT(0 <= month && month <= 12);
257   return firstDayOfMonth[isLeapYear][month];
258 }
259 
260 template <typename T>
261 static inline int DayFromMonth(T month, bool isLeapYear) = delete;
262 
263 /* ES5 15.9.1.12 (out of order to accommodate DaylightSavingTA). */
MakeDay(double year,double month,double date)264 static double MakeDay(double year, double month, double date) {
265   /* Step 1. */
266   if (!IsFinite(year) || !IsFinite(month) || !IsFinite(date))
267     return GenericNaN();
268 
269   /* Steps 2-4. */
270   double y = ToInteger(year);
271   double m = ToInteger(month);
272   double dt = ToInteger(date);
273 
274   /* Step 5. */
275   double ym = y + floor(m / 12);
276 
277   /* Step 6. */
278   int mn = int(fmod(m, 12.0));
279   if (mn < 0) mn += 12;
280 
281   /* Steps 7-8. */
282   bool leap = IsLeapYear(ym);
283 
284   double yearday = floor(TimeFromYear(ym) / msPerDay);
285   double monthday = DayFromMonth(mn, leap);
286 
287   return yearday + monthday + dt - 1;
288 }
289 
290 /* ES5 15.9.1.13 (out of order to accommodate DaylightSavingTA). */
MakeDate(double day,double time)291 static inline double MakeDate(double day, double time) {
292   /* Step 1. */
293   if (!IsFinite(day) || !IsFinite(time)) return GenericNaN();
294 
295   /* Step 2. */
296   return day * msPerDay + time;
297 }
298 
MakeDate(double year,unsigned month,unsigned day)299 JS_PUBLIC_API double JS::MakeDate(double year, unsigned month, unsigned day) {
300   MOZ_ASSERT(month <= 11);
301   MOZ_ASSERT(day >= 1 && day <= 31);
302 
303   return ::MakeDate(MakeDay(year, month, day), 0);
304 }
305 
MakeDate(double year,unsigned month,unsigned day,double time)306 JS_PUBLIC_API double JS::MakeDate(double year, unsigned month, unsigned day,
307                                   double time) {
308   MOZ_ASSERT(month <= 11);
309   MOZ_ASSERT(day >= 1 && day <= 31);
310 
311   return ::MakeDate(MakeDay(year, month, day), time);
312 }
313 
YearFromTime(double time)314 JS_PUBLIC_API double JS::YearFromTime(double time) {
315   return ::YearFromTime(time);
316 }
317 
MonthFromTime(double time)318 JS_PUBLIC_API double JS::MonthFromTime(double time) {
319   return ::MonthFromTime(time);
320 }
321 
DayFromTime(double time)322 JS_PUBLIC_API double JS::DayFromTime(double time) { return DateFromTime(time); }
323 
DayFromYear(double year)324 JS_PUBLIC_API double JS::DayFromYear(double year) {
325   return ::DayFromYear(year);
326 }
327 
DayWithinYear(double time,double year)328 JS_PUBLIC_API double JS::DayWithinYear(double time, double year) {
329   return ::DayWithinYear(time, year);
330 }
331 
SetReduceMicrosecondTimePrecisionCallback(JS::ReduceMicrosecondTimePrecisionCallback callback)332 JS_PUBLIC_API void JS::SetReduceMicrosecondTimePrecisionCallback(
333     JS::ReduceMicrosecondTimePrecisionCallback callback) {
334   sReduceMicrosecondTimePrecisionCallback = callback;
335 }
336 
SetTimeResolutionUsec(uint32_t resolution,bool jitter)337 JS_PUBLIC_API void JS::SetTimeResolutionUsec(uint32_t resolution, bool jitter) {
338   sResolutionUsec = resolution;
339   sJitter = jitter;
340 }
341 
342 /*
343  * Find a year for which any given date will fall on the same weekday.
344  *
345  * This function should be used with caution when used other than
346  * for determining DST; it hasn't been proven not to produce an
347  * incorrect year for times near year boundaries.
348  */
EquivalentYearForDST(int year)349 static int EquivalentYearForDST(int year) {
350   /*
351    * Years and leap years on which Jan 1 is a Sunday, Monday, etc.
352    *
353    * yearStartingWith[0][i] is an example non-leap year where
354    * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
355    *
356    * yearStartingWith[1][i] is an example leap year where
357    * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
358    *
359    * Keep two different mappings, one for past years (< 1970), and a
360    * different one for future years (> 2037).
361    */
362   static const int pastYearStartingWith[2][7] = {
363       {1978, 1973, 1974, 1975, 1981, 1971, 1977},
364       {1984, 1996, 1980, 1992, 1976, 1988, 1972}};
365   static const int futureYearStartingWith[2][7] = {
366       {2034, 2035, 2030, 2031, 2037, 2027, 2033},
367       {2012, 2024, 2036, 2020, 2032, 2016, 2028}};
368 
369   int day = int(DayFromYear(year) + 4) % 7;
370   if (day < 0) day += 7;
371 
372   const auto& yearStartingWith =
373       year < 1970 ? pastYearStartingWith : futureYearStartingWith;
374   return yearStartingWith[IsLeapYear(year)][day];
375 }
376 
377 // Return true if |t| is representable as a 32-bit time_t variable, that means
378 // the year is in [1970, 2038).
IsRepresentableAsTime32(double t)379 static bool IsRepresentableAsTime32(double t) {
380   return 0.0 <= t && t < 2145916800000.0;
381 }
382 
383 /* ES5 15.9.1.8. */
DaylightSavingTA(double t)384 static double DaylightSavingTA(double t) {
385   if (!IsFinite(t)) return GenericNaN();
386 
387   /*
388    * If earlier than 1970 or after 2038, potentially beyond the ken of
389    * many OSes, map it to an equivalent year before asking.
390    */
391   if (!IsRepresentableAsTime32(t)) {
392     int year = EquivalentYearForDST(int(YearFromTime(t)));
393     double day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
394     t = MakeDate(day, TimeWithinDay(t));
395   }
396 
397   int64_t utcMilliseconds = static_cast<int64_t>(t);
398   int64_t offsetMilliseconds =
399       DateTimeInfo::getDSTOffsetMilliseconds(utcMilliseconds);
400   return static_cast<double>(offsetMilliseconds);
401 }
402 
AdjustTime(double date)403 static double AdjustTime(double date) {
404   double localTZA = DateTimeInfo::localTZA();
405   double t = DaylightSavingTA(date) + localTZA;
406   t = (localTZA >= 0) ? fmod(t, msPerDay) : -fmod(msPerDay - t, msPerDay);
407   return t;
408 }
409 
410 /* ES5 15.9.1.9. */
LocalTime(double t)411 static double LocalTime(double t) { return t + AdjustTime(t); }
412 
UTC(double t)413 static double UTC(double t) {
414   // Following the ES2017 specification creates undesirable results at DST
415   // transitions. For example when transitioning from PST to PDT,
416   // |new Date(2016,2,13,2,0,0).toTimeString()| returns the string value
417   // "01:00:00 GMT-0800 (PST)" instead of "03:00:00 GMT-0700 (PDT)". Follow
418   // V8 and subtract one hour before computing the offset.
419   // Spec bug: https://bugs.ecmascript.org/show_bug.cgi?id=4007
420 
421   return t - AdjustTime(t - DateTimeInfo::localTZA() - msPerHour);
422 }
423 
424 /* ES5 15.9.1.10. */
HourFromTime(double t)425 static double HourFromTime(double t) {
426   double result = fmod(floor(t / msPerHour), HoursPerDay);
427   if (result < 0) result += HoursPerDay;
428   return result;
429 }
430 
MinFromTime(double t)431 static double MinFromTime(double t) {
432   double result = fmod(floor(t / msPerMinute), MinutesPerHour);
433   if (result < 0) result += MinutesPerHour;
434   return result;
435 }
436 
SecFromTime(double t)437 static double SecFromTime(double t) {
438   double result = fmod(floor(t / msPerSecond), SecondsPerMinute);
439   if (result < 0) result += SecondsPerMinute;
440   return result;
441 }
442 
msFromTime(double t)443 static double msFromTime(double t) {
444   double result = fmod(t, msPerSecond);
445   if (result < 0) result += msPerSecond;
446   return result;
447 }
448 
449 /* ES5 15.9.1.11. */
MakeTime(double hour,double min,double sec,double ms)450 static double MakeTime(double hour, double min, double sec, double ms) {
451   /* Step 1. */
452   if (!IsFinite(hour) || !IsFinite(min) || !IsFinite(sec) || !IsFinite(ms)) {
453     return GenericNaN();
454   }
455 
456   /* Step 2. */
457   double h = ToInteger(hour);
458 
459   /* Step 3. */
460   double m = ToInteger(min);
461 
462   /* Step 4. */
463   double s = ToInteger(sec);
464 
465   /* Step 5. */
466   double milli = ToInteger(ms);
467 
468   /* Steps 6-7. */
469   return h * msPerHour + m * msPerMinute + s * msPerSecond + milli;
470 }
471 
472 /**
473  * end of ECMA 'support' functions
474  */
475 
476 /* for use by date_parse */
477 
478 static const char* const wtb[] = {
479     // clang-format off
480     "am", "pm",
481     "monday", "tuesday", "wednesday", "thursday", "friday",
482     "saturday", "sunday",
483     "january", "february", "march", "april", "may", "june",
484     "july", "august", "september", "october", "november", "december",
485     "gmt", "ut", "utc",
486     "est", "edt",
487     "cst", "cdt",
488     "mst", "mdt",
489     "pst", "pdt"
490     /* time zone table needs to be expanded */
491     // clang-format on
492 };
493 
494 static const int ttb[] = {
495     // clang-format off
496     -1, -2, 0, 0, 0, 0, 0, 0, 0,       /* AM/PM */
497     2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
498     10000 + 0, 10000 + 0, 10000 + 0,   /* GMT/UT/UTC */
499     10000 + 5 * 60, 10000 + 4 * 60,    /* EST/EDT */
500     10000 + 6 * 60, 10000 + 5 * 60,    /* CST/CDT */
501     10000 + 7 * 60, 10000 + 6 * 60,    /* MST/MDT */
502     10000 + 8 * 60, 10000 + 7 * 60     /* PST/PDT */
503     // clang-format on
504 };
505 
506 template <typename CharT>
RegionMatches(const char * s1,int s1off,const CharT * s2,int s2off,int count)507 static bool RegionMatches(const char* s1, int s1off, const CharT* s2, int s2off,
508                           int count) {
509   while (count > 0 && s1[s1off] && s2[s2off]) {
510     if (unicode::ToLowerCase(s1[s1off]) != unicode::ToLowerCase(s2[s2off]))
511       break;
512 
513     s1off++;
514     s2off++;
515     count--;
516   }
517 
518   return count == 0;
519 }
520 
521 // ES2017 draft rev (TODO: Add git hash when PR 642 is merged.)
522 // 20.3.3.4
523 // Date.UTC(year [, month [, date [, hours [, minutes [, seconds [, ms]]]]]])
date_UTC(JSContext * cx,unsigned argc,Value * vp)524 static bool date_UTC(JSContext* cx, unsigned argc, Value* vp) {
525   CallArgs args = CallArgsFromVp(argc, vp);
526 
527   // Step 1.
528   double y;
529   if (!ToNumber(cx, args.get(0), &y)) return false;
530 
531   // Step 2.
532   double m;
533   if (args.length() >= 2) {
534     if (!ToNumber(cx, args[1], &m)) return false;
535   } else {
536     m = 0;
537   }
538 
539   // Step 3.
540   double dt;
541   if (args.length() >= 3) {
542     if (!ToNumber(cx, args[2], &dt)) return false;
543   } else {
544     dt = 1;
545   }
546 
547   // Step 4.
548   double h;
549   if (args.length() >= 4) {
550     if (!ToNumber(cx, args[3], &h)) return false;
551   } else {
552     h = 0;
553   }
554 
555   // Step 5.
556   double min;
557   if (args.length() >= 5) {
558     if (!ToNumber(cx, args[4], &min)) return false;
559   } else {
560     min = 0;
561   }
562 
563   // Step 6.
564   double s;
565   if (args.length() >= 6) {
566     if (!ToNumber(cx, args[5], &s)) return false;
567   } else {
568     s = 0;
569   }
570 
571   // Step 7.
572   double milli;
573   if (args.length() >= 7) {
574     if (!ToNumber(cx, args[6], &milli)) return false;
575   } else {
576     milli = 0;
577   }
578 
579   // Step 8.
580   double yr = y;
581   if (!IsNaN(y)) {
582     double yint = ToInteger(y);
583     if (0 <= yint && yint <= 99) yr = 1900 + yint;
584   }
585 
586   // Step 9.
587   ClippedTime time =
588       TimeClip(MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli)));
589   args.rval().set(TimeValue(time));
590   return true;
591 }
592 
593 /*
594  * Read and convert decimal digits from s[*i] into *result
595  * while *i < limit.
596  *
597  * Succeed if any digits are converted. Advance *i only
598  * as digits are consumed.
599  */
600 template <typename CharT>
ParseDigits(size_t * result,const CharT * s,size_t * i,size_t limit)601 static bool ParseDigits(size_t* result, const CharT* s, size_t* i,
602                         size_t limit) {
603   size_t init = *i;
604   *result = 0;
605   while (*i < limit && ('0' <= s[*i] && s[*i] <= '9')) {
606     *result *= 10;
607     *result += (s[*i] - '0');
608     ++(*i);
609   }
610   return *i != init;
611 }
612 
613 /*
614  * Read and convert decimal digits to the right of a decimal point,
615  * representing a fractional integer, from s[*i] into *result
616  * while *i < limit.
617  *
618  * Succeed if any digits are converted. Advance *i only
619  * as digits are consumed.
620  */
621 template <typename CharT>
ParseFractional(double * result,const CharT * s,size_t * i,size_t limit)622 static bool ParseFractional(double* result, const CharT* s, size_t* i,
623                             size_t limit) {
624   double factor = 0.1;
625   size_t init = *i;
626   *result = 0.0;
627   while (*i < limit && ('0' <= s[*i] && s[*i] <= '9')) {
628     *result += (s[*i] - '0') * factor;
629     factor *= 0.1;
630     ++(*i);
631   }
632   return *i != init;
633 }
634 
635 /*
636  * Read and convert exactly n decimal digits from s[*i]
637  * to s[min(*i+n,limit)] into *result.
638  *
639  * Succeed if exactly n digits are converted. Advance *i only
640  * on success.
641  */
642 template <typename CharT>
ParseDigitsN(size_t n,size_t * result,const CharT * s,size_t * i,size_t limit)643 static bool ParseDigitsN(size_t n, size_t* result, const CharT* s, size_t* i,
644                          size_t limit) {
645   size_t init = *i;
646 
647   if (ParseDigits(result, s, i, Min(limit, init + n))) return (*i - init) == n;
648 
649   *i = init;
650   return false;
651 }
652 
653 /*
654  * Read and convert n or less decimal digits from s[*i]
655  * to s[min(*i+n,limit)] into *result.
656  *
657  * Succeed only if greater than zero but less than or equal to n digits are
658  * converted. Advance *i only on success.
659  */
660 template <typename CharT>
ParseDigitsNOrLess(size_t n,size_t * result,const CharT * s,size_t * i,size_t limit)661 static bool ParseDigitsNOrLess(size_t n, size_t* result, const CharT* s,
662                                size_t* i, size_t limit) {
663   size_t init = *i;
664 
665   if (ParseDigits(result, s, i, Min(limit, init + n)))
666     return ((*i - init) > 0) && ((*i - init) <= n);
667 
668   *i = init;
669   return false;
670 }
671 
DaysInMonth(int year,int month)672 static int DaysInMonth(int year, int month) {
673   bool leap = IsLeapYear(year);
674   int result = int(DayFromMonth(month, leap) - DayFromMonth(month - 1, leap));
675   return result;
676 }
677 
678 /*
679  * Parse a string according to the formats specified in section 20.3.1.16
680  * of the ECMAScript standard. These formats are based upon a simplification
681  * of the ISO 8601 Extended Format. As per the spec omitted month and day
682  * values are defaulted to '01', omitted HH:mm:ss values are defaulted to '00'
683  * and an omitted sss field is defaulted to '000'.
684  *
685  * For cross compatibility we allow the following extensions.
686  *
687  * These are:
688  *
689  *   Standalone time part:
690  *     Any of the time formats below can be parsed without a date part.
691  *     E.g. "T19:00:00Z" will parse successfully. The date part will then
692  *     default to 1970-01-01.
693  *
694  *   'T' from the time part may be replaced with a space character:
695  *     "1970-01-01 12:00:00Z" will parse successfully. Note that only a single
696  *     space is permitted and this is not permitted in the standalone
697  *     version above.
698  *
699  *   One or more decimal digits for milliseconds:
700  *     The specification requires exactly three decimal digits for
701  *     the fractional part but we allow for one or more digits.
702  *
703  *   Time zone specifier without ':':
704  *     We allow the time zone to be specified without a ':' character.
705  *     E.g. "T19:00:00+0700" is equivalent to "T19:00:00+07:00".
706  *
707  *   One or two digits for months, days, hours, minutes and seconds:
708  *     The specification requires exactly two decimal digits for the fields
709  *     above. We allow for one or two decimal digits. I.e. "1970-1-1" is
710  *     equivalent to "1970-01-01".
711  *
712  * Date part:
713  *
714  *  Year:
715  *     YYYY (eg 1997)
716  *
717  *  Year and month:
718  *     YYYY-MM (eg 1997-07)
719  *
720  *  Complete date:
721  *     YYYY-MM-DD (eg 1997-07-16)
722  *
723  * Time part:
724  *
725  *  Hours and minutes:
726  *     Thh:mmTZD (eg T19:20+01:00)
727  *
728  *  Hours, minutes and seconds:
729  *     Thh:mm:ssTZD (eg T19:20:30+01:00)
730  *
731  *  Hours, minutes, seconds and a decimal fraction of a second:
732  *     Thh:mm:ss.sTZD (eg T19:20:30.45+01:00)
733  *
734  * where:
735  *
736  *   YYYY = four-digit year or six digit year as +YYYYYY or -YYYYYY
737  *   MM   = one or two-digit month (01=January, etc.)
738  *   DD   = one or two-digit day of month (01 through 31)
739  *   hh   = one or two digits of hour (00 through 23) (am/pm NOT allowed)
740  *   mm   = one or two digits of minute (00 through 59)
741  *   ss   = one or two digits of second (00 through 59)
742  *   sss  = one or more digits representing a decimal fraction of a second
743  *   TZD  = time zone designator (Z or +hh:mm or -hh:mm or missing for local)
744  */
745 template <typename CharT>
ParseISOStyleDate(const CharT * s,size_t length,ClippedTime * result)746 static bool ParseISOStyleDate(const CharT* s, size_t length,
747                               ClippedTime* result) {
748   size_t i = 0;
749   int tzMul = 1;
750   int dateMul = 1;
751   size_t year = 1970;
752   size_t month = 1;
753   size_t day = 1;
754   size_t hour = 0;
755   size_t min = 0;
756   size_t sec = 0;
757   double frac = 0;
758   bool isLocalTime = false;
759   size_t tzHour = 0;
760   size_t tzMin = 0;
761 
762 #define PEEK(ch) (i < length && s[i] == ch)
763 
764 #define NEED(ch)                   \
765   if (i >= length || s[i] != ch) { \
766     return false;                  \
767   } else {                         \
768     ++i;                           \
769   }
770 
771 #define DONE_DATE_UNLESS(ch)       \
772   if (i >= length || s[i] != ch) { \
773     goto done_date;                \
774   } else {                         \
775     ++i;                           \
776   }
777 
778 #define DONE_UNLESS(ch)            \
779   if (i >= length || s[i] != ch) { \
780     goto done;                     \
781   } else {                         \
782     ++i;                           \
783   }
784 
785 #define NEED_NDIGITS(n, field)                   \
786   if (!ParseDigitsN(n, &field, s, &i, length)) { \
787     return false;                                \
788   }
789 
790 #define NEED_NDIGITS_OR_LESS(n, field)                 \
791   if (!ParseDigitsNOrLess(n, &field, s, &i, length)) { \
792     return false;                                      \
793   }
794 
795   if (PEEK('+') || PEEK('-')) {
796     if (PEEK('-')) dateMul = -1;
797     ++i;
798     NEED_NDIGITS(6, year);
799   } else if (!PEEK('T')) {
800     NEED_NDIGITS(4, year);
801   }
802   DONE_DATE_UNLESS('-');
803   NEED_NDIGITS_OR_LESS(2, month);
804   DONE_DATE_UNLESS('-');
805   NEED_NDIGITS_OR_LESS(2, day);
806 
807 done_date:
808   if (PEEK('T') || PEEK(' '))
809     i++;
810   else
811     goto done;
812 
813   NEED_NDIGITS_OR_LESS(2, hour);
814   NEED(':');
815   NEED_NDIGITS_OR_LESS(2, min);
816 
817   if (PEEK(':')) {
818     ++i;
819     NEED_NDIGITS_OR_LESS(2, sec);
820     if (PEEK('.')) {
821       ++i;
822       if (!ParseFractional(&frac, s, &i, length)) return false;
823     }
824   }
825 
826   if (PEEK('Z')) {
827     ++i;
828   } else if (PEEK('+') || PEEK('-')) {
829     if (PEEK('-')) tzMul = -1;
830     ++i;
831     NEED_NDIGITS(2, tzHour);
832     /*
833      * Non-standard extension to the ISO date format (permitted by ES5):
834      * allow "-0700" as a time zone offset, not just "-07:00".
835      */
836     if (PEEK(':')) ++i;
837     NEED_NDIGITS(2, tzMin);
838   } else {
839     isLocalTime = true;
840   }
841 
842 done:
843   if (year > 275943  // ceil(1e8/365) + 1970
844       || (month == 0 || month > 12) ||
845       (day == 0 || day > size_t(DaysInMonth(year, month))) || hour > 24 ||
846       ((hour == 24) && (min > 0 || sec > 0 || frac > 0)) || min > 59 ||
847       sec > 59 || tzHour > 23 || tzMin > 59) {
848     return false;
849   }
850 
851   if (i != length) return false;
852 
853   month -= 1; /* convert month to 0-based */
854 
855   double msec = MakeDate(MakeDay(dateMul * double(year), month, day),
856                          MakeTime(hour, min, sec, frac * 1000.0));
857 
858   if (isLocalTime)
859     msec = UTC(msec);
860   else
861     msec -= tzMul * (tzHour * msPerHour + tzMin * msPerMinute);
862 
863   *result = TimeClip(msec);
864   return NumbersAreIdentical(msec, result->toDouble());
865 
866 #undef PEEK
867 #undef NEED
868 #undef DONE_UNLESS
869 #undef NEED_NDIGITS
870 }
871 
872 template <typename CharT>
ParseDate(const CharT * s,size_t length,ClippedTime * result)873 static bool ParseDate(const CharT* s, size_t length, ClippedTime* result) {
874   if (ParseISOStyleDate(s, length, result)) return true;
875 
876   if (length == 0) return false;
877 
878   int year = -1;
879   int mon = -1;
880   int mday = -1;
881   int hour = -1;
882   int min = -1;
883   int sec = -1;
884   int tzOffset = -1;
885 
886   int prevc = 0;
887 
888   bool seenPlusMinus = false;
889   bool seenMonthName = false;
890 
891   size_t i = 0;
892   while (i < length) {
893     int c = s[i];
894     i++;
895     if (c <= ' ' || c == ',' || c == '-') {
896       if (c == '-' && '0' <= s[i] && s[i] <= '9') prevc = c;
897       continue;
898     }
899     if (c == '(') { /* comments) */
900       int depth = 1;
901       while (i < length) {
902         c = s[i];
903         i++;
904         if (c == '(') {
905           depth++;
906         } else if (c == ')') {
907           if (--depth <= 0) break;
908         }
909       }
910       continue;
911     }
912     if ('0' <= c && c <= '9') {
913       int n = c - '0';
914       while (i < length && '0' <= (c = s[i]) && c <= '9') {
915         n = n * 10 + c - '0';
916         i++;
917       }
918 
919       /*
920        * Allow TZA before the year, so 'Wed Nov 05 21:49:11 GMT-0800 1997'
921        * works.
922        *
923        * Uses of seenPlusMinus allow ':' in TZA, so Java no-timezone style
924        * of GMT+4:30 works.
925        */
926 
927       if ((prevc == '+' || prevc == '-') /*  && year>=0 */) {
928         /* Make ':' case below change tzOffset. */
929         seenPlusMinus = true;
930 
931         /* offset */
932         if (n < 24)
933           n = n * 60; /* EG. "GMT-3" */
934         else
935           n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
936 
937         if (prevc == '+') /* plus means east of GMT */
938           n = -n;
939 
940         if (tzOffset != 0 && tzOffset != -1) return false;
941 
942         tzOffset = n;
943       } else if (prevc == '/' && mon >= 0 && mday >= 0 && year < 0) {
944         if (c <= ' ' || c == ',' || c == '/' || i >= length)
945           year = n;
946         else
947           return false;
948       } else if (c == ':') {
949         if (hour < 0)
950           hour = /*byte*/ n;
951         else if (min < 0)
952           min = /*byte*/ n;
953         else
954           return false;
955       } else if (c == '/') {
956         /*
957          * Until it is determined that mon is the actual month, keep
958          * it as 1-based rather than 0-based.
959          */
960         if (mon < 0)
961           mon = /*byte*/ n;
962         else if (mday < 0)
963           mday = /*byte*/ n;
964         else
965           return false;
966       } else if (i < length && c != ',' && c > ' ' && c != '-' && c != '(') {
967         return false;
968       } else if (seenPlusMinus && n < 60) { /* handle GMT-3:30 */
969         if (tzOffset < 0)
970           tzOffset -= n;
971         else
972           tzOffset += n;
973       } else if (hour >= 0 && min < 0) {
974         min = /*byte*/ n;
975       } else if (prevc == ':' && min >= 0 && sec < 0) {
976         sec = /*byte*/ n;
977       } else if (mon < 0) {
978         mon = /*byte*/ n;
979       } else if (mon >= 0 && mday < 0) {
980         mday = /*byte*/ n;
981       } else if (mon >= 0 && mday >= 0 && year < 0) {
982         year = n;
983       } else {
984         return false;
985       }
986       prevc = 0;
987     } else if (c == '/' || c == ':' || c == '+' || c == '-') {
988       prevc = c;
989     } else {
990       size_t st = i - 1;
991       while (i < length) {
992         c = s[i];
993         if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'))) break;
994         i++;
995       }
996 
997       if (i <= st + 1) return false;
998 
999       int k;
1000       for (k = ArrayLength(wtb); --k >= 0;) {
1001         if (RegionMatches(wtb[k], 0, s, st, i - st)) {
1002           int action = ttb[k];
1003           if (action != 0) {
1004             if (action < 0) {
1005               /*
1006                * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as
1007                * 12:30, instead of blindly adding 12 if PM.
1008                */
1009               MOZ_ASSERT(action == -1 || action == -2);
1010               if (hour > 12 || hour < 0) return false;
1011 
1012               if (action == -1 && hour == 12) /* am */
1013                 hour = 0;
1014               else if (action == -2 && hour != 12) /* pm */
1015                 hour += 12;
1016             } else if (action <= 13) { /* month! */
1017               /*
1018                * Adjust mon to be 1-based until the final values
1019                * for mon, mday and year are adjusted below.
1020                */
1021               if (seenMonthName) return false;
1022 
1023               seenMonthName = true;
1024               int temp = /*byte*/ (action - 2) + 1;
1025 
1026               if (mon < 0) {
1027                 mon = temp;
1028               } else if (mday < 0) {
1029                 mday = mon;
1030                 mon = temp;
1031               } else if (year < 0) {
1032                 year = mon;
1033                 mon = temp;
1034               } else {
1035                 return false;
1036               }
1037             } else {
1038               tzOffset = action - 10000;
1039             }
1040           }
1041           break;
1042         }
1043       }
1044 
1045       if (k < 0) return false;
1046 
1047       prevc = 0;
1048     }
1049   }
1050 
1051   if (year < 0 || mon < 0 || mday < 0) return false;
1052 
1053   /*
1054    * Case 1. The input string contains an English month name.
1055    *         The form of the string can be month f l, or f month l, or
1056    *         f l month which each evaluate to the same date.
1057    *         If f and l are both greater than or equal to 100 the date
1058    *         is invalid.
1059    *
1060    *         The year is taken to be either the greater of the values f, l or
1061    *         whichever is set to zero. If the year is greater than or equal to
1062    *         50 and less than 100, it is considered to be the number of years
1063    *         after 1900. If the year is less than 50 it is considered to be the
1064    *         number of years after 2000, otherwise it is considered to be the
1065    *         number of years after 0.
1066    *
1067    * Case 2. The input string is of the form "f/m/l" where f, m and l are
1068    *         integers, e.g. 7/16/45. mon, mday and year values are adjusted
1069    *         to achieve Chrome compatibility.
1070    *
1071    *         a. If 0 < f <= 12 and 0 < l <= 31, f/m/l is interpreted as
1072    *         month/day/year.
1073    *            i.  If year < 50, it is the number of years after 2000
1074    *            ii. If year >= 50, it is the number of years after 1900.
1075    *           iii. If year >= 100, it is the number of years after 0.
1076    *         b. If 31 < f and 0 < m <= 12 and 0 < l <= 31 f/m/l is
1077    *         interpreted as year/month/day
1078    *            i.  If year < 50, it is the number of years after 2000
1079    *            ii. If year >= 50, it is the number of years after 1900.
1080    *           iii. If year >= 100, it is the number of years after 0.
1081    */
1082   if (seenMonthName) {
1083     if (mday >= 100 && mon >= 100) return false;
1084 
1085     if (year > 0 && (mday == 0 || mday > year)) {
1086       int temp = year;
1087       year = mday;
1088       mday = temp;
1089     }
1090 
1091     if (mday <= 0 || mday > 31) return false;
1092 
1093   } else if (0 < mon && mon <= 12 && 0 < mday && mday <= 31) {
1094     /* (a) month/day/year */
1095   } else {
1096     /* (b) year/month/day */
1097     if (mon > 31 && mday <= 12 && year <= 31) {
1098       int temp = year;
1099       year = mon;
1100       mon = mday;
1101       mday = temp;
1102     } else {
1103       return false;
1104     }
1105   }
1106 
1107   if (year < 50)
1108     year += 2000;
1109   else if (year >= 50 && year < 100)
1110     year += 1900;
1111 
1112   mon -= 1; /* convert month to 0-based */
1113   if (sec < 0) sec = 0;
1114   if (min < 0) min = 0;
1115   if (hour < 0) hour = 0;
1116 
1117   double msec = MakeDate(MakeDay(year, mon, mday), MakeTime(hour, min, sec, 0));
1118 
1119   if (tzOffset == -1) /* no time zone specified, have to use local */
1120     msec = UTC(msec);
1121   else
1122     msec += tzOffset * msPerMinute;
1123 
1124   *result = TimeClip(msec);
1125   return true;
1126 }
1127 
ParseDate(JSLinearString * s,ClippedTime * result)1128 static bool ParseDate(JSLinearString* s, ClippedTime* result) {
1129   AutoCheckCannotGC nogc;
1130   return s->hasLatin1Chars()
1131              ? ParseDate(s->latin1Chars(nogc), s->length(), result)
1132              : ParseDate(s->twoByteChars(nogc), s->length(), result);
1133 }
1134 
date_parse(JSContext * cx,unsigned argc,Value * vp)1135 static bool date_parse(JSContext* cx, unsigned argc, Value* vp) {
1136   CallArgs args = CallArgsFromVp(argc, vp);
1137   if (args.length() == 0) {
1138     args.rval().setNaN();
1139     return true;
1140   }
1141 
1142   JSString* str = ToString<CanGC>(cx, args[0]);
1143   if (!str) return false;
1144 
1145   JSLinearString* linearStr = str->ensureLinear(cx);
1146   if (!linearStr) return false;
1147 
1148   ClippedTime result;
1149   if (!ParseDate(linearStr, &result)) {
1150     args.rval().setNaN();
1151     return true;
1152   }
1153 
1154   args.rval().set(TimeValue(result));
1155   return true;
1156 }
1157 
NowAsMillis(JSContext * cx)1158 static ClippedTime NowAsMillis(JSContext* cx) {
1159   double now = PRMJ_Now();
1160   bool clampAndJitter =
1161       JS::CompartmentCreationOptionsRef(js::GetContextCompartment(cx))
1162           .clampAndJitterTime();
1163   if (clampAndJitter && sReduceMicrosecondTimePrecisionCallback)
1164     now = sReduceMicrosecondTimePrecisionCallback(now);
1165   else if (clampAndJitter && sResolutionUsec) {
1166     double clamped = floor(now / sResolutionUsec) * sResolutionUsec;
1167 
1168     if (sJitter) {
1169       // Calculate a random midpoint for jittering. In the browser, we are
1170       // adversarial: Web Content may try to calculate the midpoint themselves
1171       // and use that to bypass it's security. In the JS Shell, we are not
1172       // adversarial, we want to jitter the time to recreate the operating
1173       // environment, but we do not concern ourselves with trying to prevent an
1174       // attacker from calculating the midpoint themselves. So we use a very
1175       // simple, very fast CRC with a hardcoded seed.
1176 
1177       uint64_t midpoint = *((uint64_t*)&clamped);
1178       midpoint ^= 0x0F00DD1E2BAD2DED;  // XOR in a 'secret'
1179       // MurmurHash3 internal component from
1180       //   https://searchfox.org/mozilla-central/rev/61d400da1c692453c2dc2c1cf37b616ce13dea5b/dom/canvas/MurmurHash3.cpp#85
1181       midpoint ^= midpoint >> 33;
1182       midpoint *= uint64_t{0xFF51AFD7ED558CCD};
1183       midpoint ^= midpoint >> 33;
1184       midpoint *= uint64_t{0xC4CEB9FE1A85EC53};
1185       midpoint ^= midpoint >> 33;
1186       midpoint %= sResolutionUsec;
1187 
1188       if (now > clamped + midpoint) {  // We're jittering up to the next step
1189         now = clamped + sResolutionUsec;
1190       } else {  // We're staying at the clamped value
1191         now = clamped;
1192       }
1193     } else {  // No jitter, only clamping
1194       now = clamped;
1195     }
1196   }
1197 
1198   return TimeClip(now / PRMJ_USEC_PER_MSEC);
1199 }
1200 
date_now(JSContext * cx,unsigned argc,Value * vp)1201 bool js::date_now(JSContext* cx, unsigned argc, Value* vp) {
1202   CallArgs args = CallArgsFromVp(argc, vp);
1203   args.rval().set(TimeValue(NowAsMillis(cx)));
1204   return true;
1205 }
1206 
setUTCTime(ClippedTime t)1207 void DateObject::setUTCTime(ClippedTime t) {
1208   for (size_t ind = COMPONENTS_START_SLOT; ind < RESERVED_SLOTS; ind++)
1209     setReservedSlot(ind, UndefinedValue());
1210 
1211   setFixedSlot(UTC_TIME_SLOT, TimeValue(t));
1212 }
1213 
setUTCTime(ClippedTime t,MutableHandleValue vp)1214 void DateObject::setUTCTime(ClippedTime t, MutableHandleValue vp) {
1215   setUTCTime(t);
1216   vp.set(TimeValue(t));
1217 }
1218 
fillLocalTimeSlots()1219 void DateObject::fillLocalTimeSlots() {
1220   /* Check if the cache is already populated. */
1221   if (!getReservedSlot(LOCAL_TIME_SLOT).isUndefined() &&
1222       getReservedSlot(TZA_SLOT).toDouble() == DateTimeInfo::localTZA()) {
1223     return;
1224   }
1225 
1226   /* Remember time zone used to generate the local cache. */
1227   setReservedSlot(TZA_SLOT, DoubleValue(DateTimeInfo::localTZA()));
1228 
1229   double utcTime = UTCTime().toNumber();
1230 
1231   if (!IsFinite(utcTime)) {
1232     for (size_t ind = COMPONENTS_START_SLOT; ind < RESERVED_SLOTS; ind++)
1233       setReservedSlot(ind, DoubleValue(utcTime));
1234     return;
1235   }
1236 
1237   double localTime = LocalTime(utcTime);
1238 
1239   setReservedSlot(LOCAL_TIME_SLOT, DoubleValue(localTime));
1240 
1241   int year = (int)floor(localTime / (msPerDay * 365.2425)) + 1970;
1242   double yearStartTime = TimeFromYear(year);
1243 
1244   /* Adjust the year in case the approximation was wrong, as in YearFromTime. */
1245   int yearDays;
1246   if (yearStartTime > localTime) {
1247     year--;
1248     yearStartTime -= (msPerDay * DaysInYear(year));
1249     yearDays = DaysInYear(year);
1250   } else {
1251     yearDays = DaysInYear(year);
1252     double nextStart = yearStartTime + (msPerDay * yearDays);
1253     if (nextStart <= localTime) {
1254       year++;
1255       yearStartTime = nextStart;
1256       yearDays = DaysInYear(year);
1257     }
1258   }
1259 
1260   setReservedSlot(LOCAL_YEAR_SLOT, Int32Value(year));
1261 
1262   uint64_t yearTime = uint64_t(localTime - yearStartTime);
1263   int yearSeconds = uint32_t(yearTime / 1000);
1264 
1265   int day = yearSeconds / int(SecondsPerDay);
1266 
1267   int step = -1, next = 30;
1268   int month;
1269 
1270   do {
1271     if (day <= next) {
1272       month = 0;
1273       break;
1274     }
1275     step = next;
1276     next += ((yearDays == 366) ? 29 : 28);
1277     if (day <= next) {
1278       month = 1;
1279       break;
1280     }
1281     step = next;
1282     if (day <= (next += 31)) {
1283       month = 2;
1284       break;
1285     }
1286     step = next;
1287     if (day <= (next += 30)) {
1288       month = 3;
1289       break;
1290     }
1291     step = next;
1292     if (day <= (next += 31)) {
1293       month = 4;
1294       break;
1295     }
1296     step = next;
1297     if (day <= (next += 30)) {
1298       month = 5;
1299       break;
1300     }
1301     step = next;
1302     if (day <= (next += 31)) {
1303       month = 6;
1304       break;
1305     }
1306     step = next;
1307     if (day <= (next += 31)) {
1308       month = 7;
1309       break;
1310     }
1311     step = next;
1312     if (day <= (next += 30)) {
1313       month = 8;
1314       break;
1315     }
1316     step = next;
1317     if (day <= (next += 31)) {
1318       month = 9;
1319       break;
1320     }
1321     step = next;
1322     if (day <= (next += 30)) {
1323       month = 10;
1324       break;
1325     }
1326     step = next;
1327     month = 11;
1328   } while (0);
1329 
1330   setReservedSlot(LOCAL_MONTH_SLOT, Int32Value(month));
1331   setReservedSlot(LOCAL_DATE_SLOT, Int32Value(day - step));
1332 
1333   int weekday = WeekDay(localTime);
1334   setReservedSlot(LOCAL_DAY_SLOT, Int32Value(weekday));
1335 
1336   setReservedSlot(LOCAL_SECONDS_INTO_YEAR_SLOT, Int32Value(yearSeconds));
1337 }
1338 
cachedLocalTime()1339 inline double DateObject::cachedLocalTime() {
1340   fillLocalTimeSlots();
1341   return getReservedSlot(LOCAL_TIME_SLOT).toDouble();
1342 }
1343 
IsDate(HandleValue v)1344 MOZ_ALWAYS_INLINE bool IsDate(HandleValue v) {
1345   return v.isObject() && v.toObject().is<DateObject>();
1346 }
1347 
1348 /*
1349  * See ECMA 15.9.5.4 thru 15.9.5.23
1350  */
getTime_impl(JSContext * cx,const CallArgs & args)1351 /* static */ MOZ_ALWAYS_INLINE bool DateObject::getTime_impl(
1352     JSContext* cx, const CallArgs& args) {
1353   args.rval().set(args.thisv().toObject().as<DateObject>().UTCTime());
1354   return true;
1355 }
1356 
date_getTime(JSContext * cx,unsigned argc,Value * vp)1357 static bool date_getTime(JSContext* cx, unsigned argc, Value* vp) {
1358   CallArgs args = CallArgsFromVp(argc, vp);
1359   return CallNonGenericMethod<IsDate, DateObject::getTime_impl>(cx, args);
1360 }
1361 
getYear_impl(JSContext * cx,const CallArgs & args)1362 /* static */ MOZ_ALWAYS_INLINE bool DateObject::getYear_impl(
1363     JSContext* cx, const CallArgs& args) {
1364   DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
1365   dateObj->fillLocalTimeSlots();
1366 
1367   Value yearVal = dateObj->getReservedSlot(LOCAL_YEAR_SLOT);
1368   if (yearVal.isInt32()) {
1369     /* Follow ECMA-262 to the letter, contrary to IE JScript. */
1370     int year = yearVal.toInt32() - 1900;
1371     args.rval().setInt32(year);
1372   } else {
1373     args.rval().set(yearVal);
1374   }
1375 
1376   return true;
1377 }
1378 
date_getYear(JSContext * cx,unsigned argc,Value * vp)1379 static bool date_getYear(JSContext* cx, unsigned argc, Value* vp) {
1380   CallArgs args = CallArgsFromVp(argc, vp);
1381   return CallNonGenericMethod<IsDate, DateObject::getYear_impl>(cx, args);
1382 }
1383 
getFullYear_impl(JSContext * cx,const CallArgs & args)1384 /* static */ MOZ_ALWAYS_INLINE bool DateObject::getFullYear_impl(
1385     JSContext* cx, const CallArgs& args) {
1386   DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
1387   dateObj->fillLocalTimeSlots();
1388 
1389   args.rval().set(dateObj->getReservedSlot(LOCAL_YEAR_SLOT));
1390   return true;
1391 }
1392 
date_getFullYear(JSContext * cx,unsigned argc,Value * vp)1393 static bool date_getFullYear(JSContext* cx, unsigned argc, Value* vp) {
1394   CallArgs args = CallArgsFromVp(argc, vp);
1395   return CallNonGenericMethod<IsDate, DateObject::getFullYear_impl>(cx, args);
1396 }
1397 
getUTCFullYear_impl(JSContext * cx,const CallArgs & args)1398 /* static */ MOZ_ALWAYS_INLINE bool DateObject::getUTCFullYear_impl(
1399     JSContext* cx, const CallArgs& args) {
1400   double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
1401   if (IsFinite(result)) result = YearFromTime(result);
1402 
1403   args.rval().setNumber(result);
1404   return true;
1405 }
1406 
date_getUTCFullYear(JSContext * cx,unsigned argc,Value * vp)1407 static bool date_getUTCFullYear(JSContext* cx, unsigned argc, Value* vp) {
1408   CallArgs args = CallArgsFromVp(argc, vp);
1409   return CallNonGenericMethod<IsDate, DateObject::getUTCFullYear_impl>(cx,
1410                                                                        args);
1411 }
1412 
getMonth_impl(JSContext * cx,const CallArgs & args)1413 /* static */ MOZ_ALWAYS_INLINE bool DateObject::getMonth_impl(
1414     JSContext* cx, const CallArgs& args) {
1415   DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
1416   dateObj->fillLocalTimeSlots();
1417 
1418   args.rval().set(dateObj->getReservedSlot(LOCAL_MONTH_SLOT));
1419   return true;
1420 }
1421 
date_getMonth(JSContext * cx,unsigned argc,Value * vp)1422 static bool date_getMonth(JSContext* cx, unsigned argc, Value* vp) {
1423   CallArgs args = CallArgsFromVp(argc, vp);
1424   return CallNonGenericMethod<IsDate, DateObject::getMonth_impl>(cx, args);
1425 }
1426 
getUTCMonth_impl(JSContext * cx,const CallArgs & args)1427 /* static */ MOZ_ALWAYS_INLINE bool DateObject::getUTCMonth_impl(
1428     JSContext* cx, const CallArgs& args) {
1429   double d = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
1430   args.rval().setNumber(MonthFromTime(d));
1431   return true;
1432 }
1433 
date_getUTCMonth(JSContext * cx,unsigned argc,Value * vp)1434 static bool date_getUTCMonth(JSContext* cx, unsigned argc, Value* vp) {
1435   CallArgs args = CallArgsFromVp(argc, vp);
1436   return CallNonGenericMethod<IsDate, DateObject::getUTCMonth_impl>(cx, args);
1437 }
1438 
getDate_impl(JSContext * cx,const CallArgs & args)1439 /* static */ MOZ_ALWAYS_INLINE bool DateObject::getDate_impl(
1440     JSContext* cx, const CallArgs& args) {
1441   DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
1442   dateObj->fillLocalTimeSlots();
1443 
1444   args.rval().set(dateObj->getReservedSlot(LOCAL_DATE_SLOT));
1445   return true;
1446 }
1447 
date_getDate(JSContext * cx,unsigned argc,Value * vp)1448 static bool date_getDate(JSContext* cx, unsigned argc, Value* vp) {
1449   CallArgs args = CallArgsFromVp(argc, vp);
1450   return CallNonGenericMethod<IsDate, DateObject::getDate_impl>(cx, args);
1451 }
1452 
getUTCDate_impl(JSContext * cx,const CallArgs & args)1453 /* static */ MOZ_ALWAYS_INLINE bool DateObject::getUTCDate_impl(
1454     JSContext* cx, const CallArgs& args) {
1455   double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
1456   if (IsFinite(result)) result = DateFromTime(result);
1457 
1458   args.rval().setNumber(result);
1459   return true;
1460 }
1461 
date_getUTCDate(JSContext * cx,unsigned argc,Value * vp)1462 static bool date_getUTCDate(JSContext* cx, unsigned argc, Value* vp) {
1463   CallArgs args = CallArgsFromVp(argc, vp);
1464   return CallNonGenericMethod<IsDate, DateObject::getUTCDate_impl>(cx, args);
1465 }
1466 
getDay_impl(JSContext * cx,const CallArgs & args)1467 /* static */ MOZ_ALWAYS_INLINE bool DateObject::getDay_impl(
1468     JSContext* cx, const CallArgs& args) {
1469   DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
1470   dateObj->fillLocalTimeSlots();
1471 
1472   args.rval().set(dateObj->getReservedSlot(LOCAL_DAY_SLOT));
1473   return true;
1474 }
1475 
date_getDay(JSContext * cx,unsigned argc,Value * vp)1476 static bool date_getDay(JSContext* cx, unsigned argc, Value* vp) {
1477   CallArgs args = CallArgsFromVp(argc, vp);
1478   return CallNonGenericMethod<IsDate, DateObject::getDay_impl>(cx, args);
1479 }
1480 
getUTCDay_impl(JSContext * cx,const CallArgs & args)1481 /* static */ MOZ_ALWAYS_INLINE bool DateObject::getUTCDay_impl(
1482     JSContext* cx, const CallArgs& args) {
1483   double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
1484   if (IsFinite(result)) result = WeekDay(result);
1485 
1486   args.rval().setNumber(result);
1487   return true;
1488 }
1489 
date_getUTCDay(JSContext * cx,unsigned argc,Value * vp)1490 static bool date_getUTCDay(JSContext* cx, unsigned argc, Value* vp) {
1491   CallArgs args = CallArgsFromVp(argc, vp);
1492   return CallNonGenericMethod<IsDate, DateObject::getUTCDay_impl>(cx, args);
1493 }
1494 
getHours_impl(JSContext * cx,const CallArgs & args)1495 /* static */ MOZ_ALWAYS_INLINE bool DateObject::getHours_impl(
1496     JSContext* cx, const CallArgs& args) {
1497   DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
1498   dateObj->fillLocalTimeSlots();
1499 
1500   // Note: LOCAL_SECONDS_INTO_YEAR_SLOT is guaranteed to contain an
1501   // int32 or NaN after the call to fillLocalTimeSlots.
1502   Value yearSeconds = dateObj->getReservedSlot(LOCAL_SECONDS_INTO_YEAR_SLOT);
1503   if (yearSeconds.isDouble()) {
1504     MOZ_ASSERT(IsNaN(yearSeconds.toDouble()));
1505     args.rval().set(yearSeconds);
1506   } else {
1507     args.rval().setInt32((yearSeconds.toInt32() / int(SecondsPerHour)) %
1508                          int(HoursPerDay));
1509   }
1510   return true;
1511 }
1512 
date_getHours(JSContext * cx,unsigned argc,Value * vp)1513 static bool date_getHours(JSContext* cx, unsigned argc, Value* vp) {
1514   CallArgs args = CallArgsFromVp(argc, vp);
1515   return CallNonGenericMethod<IsDate, DateObject::getHours_impl>(cx, args);
1516 }
1517 
getUTCHours_impl(JSContext * cx,const CallArgs & args)1518 /* static */ MOZ_ALWAYS_INLINE bool DateObject::getUTCHours_impl(
1519     JSContext* cx, const CallArgs& args) {
1520   double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
1521   if (IsFinite(result)) result = HourFromTime(result);
1522 
1523   args.rval().setNumber(result);
1524   return true;
1525 }
1526 
date_getUTCHours(JSContext * cx,unsigned argc,Value * vp)1527 static bool date_getUTCHours(JSContext* cx, unsigned argc, Value* vp) {
1528   CallArgs args = CallArgsFromVp(argc, vp);
1529   return CallNonGenericMethod<IsDate, DateObject::getUTCHours_impl>(cx, args);
1530 }
1531 
getMinutes_impl(JSContext * cx,const CallArgs & args)1532 /* static */ MOZ_ALWAYS_INLINE bool DateObject::getMinutes_impl(
1533     JSContext* cx, const CallArgs& args) {
1534   DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
1535   dateObj->fillLocalTimeSlots();
1536 
1537   // Note: LOCAL_SECONDS_INTO_YEAR_SLOT is guaranteed to contain an
1538   // int32 or NaN after the call to fillLocalTimeSlots.
1539   Value yearSeconds = dateObj->getReservedSlot(LOCAL_SECONDS_INTO_YEAR_SLOT);
1540   if (yearSeconds.isDouble()) {
1541     MOZ_ASSERT(IsNaN(yearSeconds.toDouble()));
1542     args.rval().set(yearSeconds);
1543   } else {
1544     args.rval().setInt32((yearSeconds.toInt32() / int(SecondsPerMinute)) %
1545                          int(MinutesPerHour));
1546   }
1547   return true;
1548 }
1549 
date_getMinutes(JSContext * cx,unsigned argc,Value * vp)1550 static bool date_getMinutes(JSContext* cx, unsigned argc, Value* vp) {
1551   CallArgs args = CallArgsFromVp(argc, vp);
1552   return CallNonGenericMethod<IsDate, DateObject::getMinutes_impl>(cx, args);
1553 }
1554 
getUTCMinutes_impl(JSContext * cx,const CallArgs & args)1555 /* static */ MOZ_ALWAYS_INLINE bool DateObject::getUTCMinutes_impl(
1556     JSContext* cx, const CallArgs& args) {
1557   double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
1558   if (IsFinite(result)) result = MinFromTime(result);
1559 
1560   args.rval().setNumber(result);
1561   return true;
1562 }
1563 
date_getUTCMinutes(JSContext * cx,unsigned argc,Value * vp)1564 static bool date_getUTCMinutes(JSContext* cx, unsigned argc, Value* vp) {
1565   CallArgs args = CallArgsFromVp(argc, vp);
1566   return CallNonGenericMethod<IsDate, DateObject::getUTCMinutes_impl>(cx, args);
1567 }
1568 
1569 /*
1570  * Date.getSeconds is mapped to getUTCSeconds. As long as no supported time
1571  * zone has a fractional-minute component, the differences in their
1572  * specifications aren't observable.
1573  *
1574  * We'll have to split the implementations if a new time zone with a
1575  * fractional-minute component is introduced or once we implement ES6's
1576  * 20.3.1.7 Local Time Zone Adjustment: time zones with adjustments like that
1577  * did historically exist, e.g.
1578  * https://en.wikipedia.org/wiki/UTC%E2%88%9200:25:21
1579  */
1580 
getUTCSeconds_impl(JSContext * cx,const CallArgs & args)1581 /* static */ MOZ_ALWAYS_INLINE bool DateObject::getUTCSeconds_impl(
1582     JSContext* cx, const CallArgs& args) {
1583   DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
1584   dateObj->fillLocalTimeSlots();
1585 
1586   // Note: LOCAL_SECONDS_INTO_YEAR_SLOT is guaranteed to contain an
1587   // int32 or NaN after the call to fillLocalTimeSlots.
1588   Value yearSeconds = dateObj->getReservedSlot(LOCAL_SECONDS_INTO_YEAR_SLOT);
1589   if (yearSeconds.isDouble()) {
1590     MOZ_ASSERT(IsNaN(yearSeconds.toDouble()));
1591     args.rval().set(yearSeconds);
1592   } else {
1593     args.rval().setInt32(yearSeconds.toInt32() % int(SecondsPerMinute));
1594   }
1595   return true;
1596 }
1597 
date_getUTCSeconds(JSContext * cx,unsigned argc,Value * vp)1598 static bool date_getUTCSeconds(JSContext* cx, unsigned argc, Value* vp) {
1599   CallArgs args = CallArgsFromVp(argc, vp);
1600   return CallNonGenericMethod<IsDate, DateObject::getUTCSeconds_impl>(cx, args);
1601 }
1602 /*
1603  * Date.getMilliseconds is mapped to getUTCMilliseconds for the same reasons
1604  * that getSeconds is mapped to getUTCSeconds (see above).  No known LocalTZA
1605  * has *ever* included a fractional-second component, however, so we can keep
1606  * this simplification even if we stop implementing ES5 local-time computation
1607  * semantics.
1608  */
1609 
getUTCMilliseconds_impl(JSContext * cx,const CallArgs & args)1610 /* static */ MOZ_ALWAYS_INLINE bool DateObject::getUTCMilliseconds_impl(
1611     JSContext* cx, const CallArgs& args) {
1612   double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
1613   if (IsFinite(result)) result = msFromTime(result);
1614 
1615   args.rval().setNumber(result);
1616   return true;
1617 }
1618 
date_getUTCMilliseconds(JSContext * cx,unsigned argc,Value * vp)1619 static bool date_getUTCMilliseconds(JSContext* cx, unsigned argc, Value* vp) {
1620   CallArgs args = CallArgsFromVp(argc, vp);
1621   return CallNonGenericMethod<IsDate, DateObject::getUTCMilliseconds_impl>(
1622       cx, args);
1623 }
1624 
getTimezoneOffset_impl(JSContext * cx,const CallArgs & args)1625 /* static */ MOZ_ALWAYS_INLINE bool DateObject::getTimezoneOffset_impl(
1626     JSContext* cx, const CallArgs& args) {
1627   DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
1628   double utctime = dateObj->UTCTime().toNumber();
1629   double localtime = dateObj->cachedLocalTime();
1630 
1631   /*
1632    * Return the time zone offset in minutes for the current locale that is
1633    * appropriate for this time. This value would be a constant except for
1634    * daylight savings time.
1635    */
1636   double result = (utctime - localtime) / msPerMinute;
1637   args.rval().setNumber(result);
1638   return true;
1639 }
1640 
date_getTimezoneOffset(JSContext * cx,unsigned argc,Value * vp)1641 static bool date_getTimezoneOffset(JSContext* cx, unsigned argc, Value* vp) {
1642   CallArgs args = CallArgsFromVp(argc, vp);
1643   return CallNonGenericMethod<IsDate, DateObject::getTimezoneOffset_impl>(cx,
1644                                                                           args);
1645 }
1646 
date_setTime_impl(JSContext * cx,const CallArgs & args)1647 MOZ_ALWAYS_INLINE bool date_setTime_impl(JSContext* cx, const CallArgs& args) {
1648   Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1649   if (args.length() == 0) {
1650     dateObj->setUTCTime(ClippedTime::invalid(), args.rval());
1651     return true;
1652   }
1653 
1654   double result;
1655   if (!ToNumber(cx, args[0], &result)) return false;
1656 
1657   dateObj->setUTCTime(TimeClip(result), args.rval());
1658   return true;
1659 }
1660 
date_setTime(JSContext * cx,unsigned argc,Value * vp)1661 static bool date_setTime(JSContext* cx, unsigned argc, Value* vp) {
1662   CallArgs args = CallArgsFromVp(argc, vp);
1663   return CallNonGenericMethod<IsDate, date_setTime_impl>(cx, args);
1664 }
1665 
GetMsecsOrDefault(JSContext * cx,const CallArgs & args,unsigned i,double t,double * millis)1666 static bool GetMsecsOrDefault(JSContext* cx, const CallArgs& args, unsigned i,
1667                               double t, double* millis) {
1668   if (args.length() <= i) {
1669     *millis = msFromTime(t);
1670     return true;
1671   }
1672   return ToNumber(cx, args[i], millis);
1673 }
1674 
GetSecsOrDefault(JSContext * cx,const CallArgs & args,unsigned i,double t,double * sec)1675 static bool GetSecsOrDefault(JSContext* cx, const CallArgs& args, unsigned i,
1676                              double t, double* sec) {
1677   if (args.length() <= i) {
1678     *sec = SecFromTime(t);
1679     return true;
1680   }
1681   return ToNumber(cx, args[i], sec);
1682 }
1683 
GetMinsOrDefault(JSContext * cx,const CallArgs & args,unsigned i,double t,double * mins)1684 static bool GetMinsOrDefault(JSContext* cx, const CallArgs& args, unsigned i,
1685                              double t, double* mins) {
1686   if (args.length() <= i) {
1687     *mins = MinFromTime(t);
1688     return true;
1689   }
1690   return ToNumber(cx, args[i], mins);
1691 }
1692 
1693 /* ES6 20.3.4.23. */
date_setMilliseconds_impl(JSContext * cx,const CallArgs & args)1694 MOZ_ALWAYS_INLINE bool date_setMilliseconds_impl(JSContext* cx,
1695                                                  const CallArgs& args) {
1696   Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1697 
1698   // Steps 1-2.
1699   double t = LocalTime(dateObj->UTCTime().toNumber());
1700 
1701   // Steps 3-4.
1702   double ms;
1703   if (!ToNumber(cx, args.get(0), &ms)) return false;
1704 
1705   // Step 5.
1706   double time = MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms);
1707 
1708   // Step 6.
1709   ClippedTime u = TimeClip(UTC(MakeDate(Day(t), time)));
1710 
1711   // Steps 7-8.
1712   dateObj->setUTCTime(u, args.rval());
1713   return true;
1714 }
1715 
date_setMilliseconds(JSContext * cx,unsigned argc,Value * vp)1716 static bool date_setMilliseconds(JSContext* cx, unsigned argc, Value* vp) {
1717   CallArgs args = CallArgsFromVp(argc, vp);
1718   return CallNonGenericMethod<IsDate, date_setMilliseconds_impl>(cx, args);
1719 }
1720 
1721 /* ES5 15.9.5.29. */
date_setUTCMilliseconds_impl(JSContext * cx,const CallArgs & args)1722 MOZ_ALWAYS_INLINE bool date_setUTCMilliseconds_impl(JSContext* cx,
1723                                                     const CallArgs& args) {
1724   Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1725 
1726   /* Step 1. */
1727   double t = dateObj->UTCTime().toNumber();
1728 
1729   /* Step 2. */
1730   double milli;
1731   if (!ToNumber(cx, args.get(0), &milli)) return false;
1732   double time =
1733       MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), milli);
1734 
1735   /* Step 3. */
1736   ClippedTime v = TimeClip(MakeDate(Day(t), time));
1737 
1738   /* Steps 4-5. */
1739   dateObj->setUTCTime(v, args.rval());
1740   return true;
1741 }
1742 
date_setUTCMilliseconds(JSContext * cx,unsigned argc,Value * vp)1743 static bool date_setUTCMilliseconds(JSContext* cx, unsigned argc, Value* vp) {
1744   CallArgs args = CallArgsFromVp(argc, vp);
1745   return CallNonGenericMethod<IsDate, date_setUTCMilliseconds_impl>(cx, args);
1746 }
1747 
1748 /* ES5 15.9.5.30. */
date_setSeconds_impl(JSContext * cx,const CallArgs & args)1749 MOZ_ALWAYS_INLINE bool date_setSeconds_impl(JSContext* cx,
1750                                             const CallArgs& args) {
1751   Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1752 
1753   // Steps 1-2.
1754   double t = LocalTime(dateObj->UTCTime().toNumber());
1755 
1756   // Steps 3-4.
1757   double s;
1758   if (!ToNumber(cx, args.get(0), &s)) return false;
1759 
1760   // Steps 5-6.
1761   double milli;
1762   if (!GetMsecsOrDefault(cx, args, 1, t, &milli)) return false;
1763 
1764   // Step 7.
1765   double date =
1766       MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli));
1767 
1768   // Step 8.
1769   ClippedTime u = TimeClip(UTC(date));
1770 
1771   // Step 9.
1772   dateObj->setUTCTime(u, args.rval());
1773   return true;
1774 }
1775 
1776 /* ES6 20.3.4.26. */
date_setSeconds(JSContext * cx,unsigned argc,Value * vp)1777 static bool date_setSeconds(JSContext* cx, unsigned argc, Value* vp) {
1778   CallArgs args = CallArgsFromVp(argc, vp);
1779   return CallNonGenericMethod<IsDate, date_setSeconds_impl>(cx, args);
1780 }
1781 
date_setUTCSeconds_impl(JSContext * cx,const CallArgs & args)1782 MOZ_ALWAYS_INLINE bool date_setUTCSeconds_impl(JSContext* cx,
1783                                                const CallArgs& args) {
1784   Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1785 
1786   /* Step 1. */
1787   double t = dateObj->UTCTime().toNumber();
1788 
1789   /* Step 2. */
1790   double s;
1791   if (!ToNumber(cx, args.get(0), &s)) return false;
1792 
1793   /* Step 3. */
1794   double milli;
1795   if (!GetMsecsOrDefault(cx, args, 1, t, &milli)) return false;
1796 
1797   /* Step 4. */
1798   double date =
1799       MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli));
1800 
1801   /* Step 5. */
1802   ClippedTime v = TimeClip(date);
1803 
1804   /* Steps 6-7. */
1805   dateObj->setUTCTime(v, args.rval());
1806   return true;
1807 }
1808 
1809 /* ES5 15.9.5.32. */
date_setUTCSeconds(JSContext * cx,unsigned argc,Value * vp)1810 static bool date_setUTCSeconds(JSContext* cx, unsigned argc, Value* vp) {
1811   CallArgs args = CallArgsFromVp(argc, vp);
1812   return CallNonGenericMethod<IsDate, date_setUTCSeconds_impl>(cx, args);
1813 }
1814 
date_setMinutes_impl(JSContext * cx,const CallArgs & args)1815 MOZ_ALWAYS_INLINE bool date_setMinutes_impl(JSContext* cx,
1816                                             const CallArgs& args) {
1817   Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1818 
1819   // Steps 1-2.
1820   double t = LocalTime(dateObj->UTCTime().toNumber());
1821 
1822   // Steps 3-4.
1823   double m;
1824   if (!ToNumber(cx, args.get(0), &m)) return false;
1825 
1826   // Steps 5-6.
1827   double s;
1828   if (!GetSecsOrDefault(cx, args, 1, t, &s)) return false;
1829 
1830   // Steps 7-8.
1831   double milli;
1832   if (!GetMsecsOrDefault(cx, args, 2, t, &milli)) return false;
1833 
1834   // Step 9.
1835   double date = MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli));
1836 
1837   // Step 10.
1838   ClippedTime u = TimeClip(UTC(date));
1839 
1840   // Steps 11-12.
1841   dateObj->setUTCTime(u, args.rval());
1842   return true;
1843 }
1844 
1845 /* ES6 20.3.4.24. */
date_setMinutes(JSContext * cx,unsigned argc,Value * vp)1846 static bool date_setMinutes(JSContext* cx, unsigned argc, Value* vp) {
1847   // Steps 1-2 (the effectful parts).
1848   CallArgs args = CallArgsFromVp(argc, vp);
1849   return CallNonGenericMethod<IsDate, date_setMinutes_impl>(cx, args);
1850 }
1851 
date_setUTCMinutes_impl(JSContext * cx,const CallArgs & args)1852 MOZ_ALWAYS_INLINE bool date_setUTCMinutes_impl(JSContext* cx,
1853                                                const CallArgs& args) {
1854   Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1855 
1856   /* Step 1. */
1857   double t = dateObj->UTCTime().toNumber();
1858 
1859   /* Step 2. */
1860   double m;
1861   if (!ToNumber(cx, args.get(0), &m)) return false;
1862 
1863   /* Step 3. */
1864   double s;
1865   if (!GetSecsOrDefault(cx, args, 1, t, &s)) return false;
1866 
1867   /* Step 4. */
1868   double milli;
1869   if (!GetMsecsOrDefault(cx, args, 2, t, &milli)) return false;
1870 
1871   /* Step 5. */
1872   double date = MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli));
1873 
1874   /* Step 6. */
1875   ClippedTime v = TimeClip(date);
1876 
1877   /* Steps 7-8. */
1878   dateObj->setUTCTime(v, args.rval());
1879   return true;
1880 }
1881 
1882 /* ES5 15.9.5.34. */
date_setUTCMinutes(JSContext * cx,unsigned argc,Value * vp)1883 static bool date_setUTCMinutes(JSContext* cx, unsigned argc, Value* vp) {
1884   CallArgs args = CallArgsFromVp(argc, vp);
1885   return CallNonGenericMethod<IsDate, date_setUTCMinutes_impl>(cx, args);
1886 }
1887 
date_setHours_impl(JSContext * cx,const CallArgs & args)1888 MOZ_ALWAYS_INLINE bool date_setHours_impl(JSContext* cx, const CallArgs& args) {
1889   Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1890 
1891   // Steps 1-2.
1892   double t = LocalTime(dateObj->UTCTime().toNumber());
1893 
1894   // Steps 3-4.
1895   double h;
1896   if (!ToNumber(cx, args.get(0), &h)) return false;
1897 
1898   // Steps 5-6.
1899   double m;
1900   if (!GetMinsOrDefault(cx, args, 1, t, &m)) return false;
1901 
1902   // Steps 7-8.
1903   double s;
1904   if (!GetSecsOrDefault(cx, args, 2, t, &s)) return false;
1905 
1906   // Steps 9-10.
1907   double milli;
1908   if (!GetMsecsOrDefault(cx, args, 3, t, &milli)) return false;
1909 
1910   // Step 11.
1911   double date = MakeDate(Day(t), MakeTime(h, m, s, milli));
1912 
1913   // Step 12.
1914   ClippedTime u = TimeClip(UTC(date));
1915 
1916   // Steps 13-14.
1917   dateObj->setUTCTime(u, args.rval());
1918   return true;
1919 }
1920 
1921 /* ES5 15.9.5.35. */
date_setHours(JSContext * cx,unsigned argc,Value * vp)1922 static bool date_setHours(JSContext* cx, unsigned argc, Value* vp) {
1923   CallArgs args = CallArgsFromVp(argc, vp);
1924   return CallNonGenericMethod<IsDate, date_setHours_impl>(cx, args);
1925 }
1926 
date_setUTCHours_impl(JSContext * cx,const CallArgs & args)1927 MOZ_ALWAYS_INLINE bool date_setUTCHours_impl(JSContext* cx,
1928                                              const CallArgs& args) {
1929   Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1930 
1931   /* Step 1. */
1932   double t = dateObj->UTCTime().toNumber();
1933 
1934   /* Step 2. */
1935   double h;
1936   if (!ToNumber(cx, args.get(0), &h)) return false;
1937 
1938   /* Step 3. */
1939   double m;
1940   if (!GetMinsOrDefault(cx, args, 1, t, &m)) return false;
1941 
1942   /* Step 4. */
1943   double s;
1944   if (!GetSecsOrDefault(cx, args, 2, t, &s)) return false;
1945 
1946   /* Step 5. */
1947   double milli;
1948   if (!GetMsecsOrDefault(cx, args, 3, t, &milli)) return false;
1949 
1950   /* Step 6. */
1951   double newDate = MakeDate(Day(t), MakeTime(h, m, s, milli));
1952 
1953   /* Step 7. */
1954   ClippedTime v = TimeClip(newDate);
1955 
1956   /* Steps 8-9. */
1957   dateObj->setUTCTime(v, args.rval());
1958   return true;
1959 }
1960 
1961 /* ES5 15.9.5.36. */
date_setUTCHours(JSContext * cx,unsigned argc,Value * vp)1962 static bool date_setUTCHours(JSContext* cx, unsigned argc, Value* vp) {
1963   CallArgs args = CallArgsFromVp(argc, vp);
1964   return CallNonGenericMethod<IsDate, date_setUTCHours_impl>(cx, args);
1965 }
1966 
date_setDate_impl(JSContext * cx,const CallArgs & args)1967 MOZ_ALWAYS_INLINE bool date_setDate_impl(JSContext* cx, const CallArgs& args) {
1968   Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1969 
1970   /* Step 1. */
1971   double t = LocalTime(dateObj->UTCTime().toNumber());
1972 
1973   /* Step 2. */
1974   double date;
1975   if (!ToNumber(cx, args.get(0), &date)) return false;
1976 
1977   /* Step 3. */
1978   double newDate = MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date),
1979                             TimeWithinDay(t));
1980 
1981   /* Step 4. */
1982   ClippedTime u = TimeClip(UTC(newDate));
1983 
1984   /* Steps 5-6. */
1985   dateObj->setUTCTime(u, args.rval());
1986   return true;
1987 }
1988 
1989 /* ES5 15.9.5.37. */
date_setDate(JSContext * cx,unsigned argc,Value * vp)1990 static bool date_setDate(JSContext* cx, unsigned argc, Value* vp) {
1991   CallArgs args = CallArgsFromVp(argc, vp);
1992   return CallNonGenericMethod<IsDate, date_setDate_impl>(cx, args);
1993 }
1994 
date_setUTCDate_impl(JSContext * cx,const CallArgs & args)1995 MOZ_ALWAYS_INLINE bool date_setUTCDate_impl(JSContext* cx,
1996                                             const CallArgs& args) {
1997   Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
1998 
1999   /* Step 1. */
2000   double t = dateObj->UTCTime().toNumber();
2001 
2002   /* Step 2. */
2003   double date;
2004   if (!ToNumber(cx, args.get(0), &date)) return false;
2005 
2006   /* Step 3. */
2007   double newDate = MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date),
2008                             TimeWithinDay(t));
2009 
2010   /* Step 4. */
2011   ClippedTime v = TimeClip(newDate);
2012 
2013   /* Steps 5-6. */
2014   dateObj->setUTCTime(v, args.rval());
2015   return true;
2016 }
2017 
date_setUTCDate(JSContext * cx,unsigned argc,Value * vp)2018 static bool date_setUTCDate(JSContext* cx, unsigned argc, Value* vp) {
2019   CallArgs args = CallArgsFromVp(argc, vp);
2020   return CallNonGenericMethod<IsDate, date_setUTCDate_impl>(cx, args);
2021 }
2022 
GetDateOrDefault(JSContext * cx,const CallArgs & args,unsigned i,double t,double * date)2023 static bool GetDateOrDefault(JSContext* cx, const CallArgs& args, unsigned i,
2024                              double t, double* date) {
2025   if (args.length() <= i) {
2026     *date = DateFromTime(t);
2027     return true;
2028   }
2029   return ToNumber(cx, args[i], date);
2030 }
2031 
GetMonthOrDefault(JSContext * cx,const CallArgs & args,unsigned i,double t,double * month)2032 static bool GetMonthOrDefault(JSContext* cx, const CallArgs& args, unsigned i,
2033                               double t, double* month) {
2034   if (args.length() <= i) {
2035     *month = MonthFromTime(t);
2036     return true;
2037   }
2038   return ToNumber(cx, args[i], month);
2039 }
2040 
2041 /* ES5 15.9.5.38. */
date_setMonth_impl(JSContext * cx,const CallArgs & args)2042 MOZ_ALWAYS_INLINE bool date_setMonth_impl(JSContext* cx, const CallArgs& args) {
2043   Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2044 
2045   /* Step 1. */
2046   double t = LocalTime(dateObj->UTCTime().toNumber());
2047 
2048   /* Step 2. */
2049   double m;
2050   if (!ToNumber(cx, args.get(0), &m)) return false;
2051 
2052   /* Step 3. */
2053   double date;
2054   if (!GetDateOrDefault(cx, args, 1, t, &date)) return false;
2055 
2056   /* Step 4. */
2057   double newDate =
2058       MakeDate(MakeDay(YearFromTime(t), m, date), TimeWithinDay(t));
2059 
2060   /* Step 5. */
2061   ClippedTime u = TimeClip(UTC(newDate));
2062 
2063   /* Steps 6-7. */
2064   dateObj->setUTCTime(u, args.rval());
2065   return true;
2066 }
2067 
date_setMonth(JSContext * cx,unsigned argc,Value * vp)2068 static bool date_setMonth(JSContext* cx, unsigned argc, Value* vp) {
2069   CallArgs args = CallArgsFromVp(argc, vp);
2070   return CallNonGenericMethod<IsDate, date_setMonth_impl>(cx, args);
2071 }
2072 
2073 /* ES5 15.9.5.39. */
date_setUTCMonth_impl(JSContext * cx,const CallArgs & args)2074 MOZ_ALWAYS_INLINE bool date_setUTCMonth_impl(JSContext* cx,
2075                                              const CallArgs& args) {
2076   Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2077 
2078   /* Step 1. */
2079   double t = dateObj->UTCTime().toNumber();
2080 
2081   /* Step 2. */
2082   double m;
2083   if (!ToNumber(cx, args.get(0), &m)) return false;
2084 
2085   /* Step 3. */
2086   double date;
2087   if (!GetDateOrDefault(cx, args, 1, t, &date)) return false;
2088 
2089   /* Step 4. */
2090   double newDate =
2091       MakeDate(MakeDay(YearFromTime(t), m, date), TimeWithinDay(t));
2092 
2093   /* Step 5. */
2094   ClippedTime v = TimeClip(newDate);
2095 
2096   /* Steps 6-7. */
2097   dateObj->setUTCTime(v, args.rval());
2098   return true;
2099 }
2100 
date_setUTCMonth(JSContext * cx,unsigned argc,Value * vp)2101 static bool date_setUTCMonth(JSContext* cx, unsigned argc, Value* vp) {
2102   CallArgs args = CallArgsFromVp(argc, vp);
2103   return CallNonGenericMethod<IsDate, date_setUTCMonth_impl>(cx, args);
2104 }
2105 
ThisLocalTimeOrZero(Handle<DateObject * > dateObj)2106 static double ThisLocalTimeOrZero(Handle<DateObject*> dateObj) {
2107   double t = dateObj->UTCTime().toNumber();
2108   if (IsNaN(t)) return +0;
2109   return LocalTime(t);
2110 }
2111 
ThisUTCTimeOrZero(Handle<DateObject * > dateObj)2112 static double ThisUTCTimeOrZero(Handle<DateObject*> dateObj) {
2113   double t = dateObj->as<DateObject>().UTCTime().toNumber();
2114   return IsNaN(t) ? +0 : t;
2115 }
2116 
2117 /* ES5 15.9.5.40. */
date_setFullYear_impl(JSContext * cx,const CallArgs & args)2118 MOZ_ALWAYS_INLINE bool date_setFullYear_impl(JSContext* cx,
2119                                              const CallArgs& args) {
2120   Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2121 
2122   /* Step 1. */
2123   double t = ThisLocalTimeOrZero(dateObj);
2124 
2125   /* Step 2. */
2126   double y;
2127   if (!ToNumber(cx, args.get(0), &y)) return false;
2128 
2129   /* Step 3. */
2130   double m;
2131   if (!GetMonthOrDefault(cx, args, 1, t, &m)) return false;
2132 
2133   /* Step 4. */
2134   double date;
2135   if (!GetDateOrDefault(cx, args, 2, t, &date)) return false;
2136 
2137   /* Step 5. */
2138   double newDate = MakeDate(MakeDay(y, m, date), TimeWithinDay(t));
2139 
2140   /* Step 6. */
2141   ClippedTime u = TimeClip(UTC(newDate));
2142 
2143   /* Steps 7-8. */
2144   dateObj->setUTCTime(u, args.rval());
2145   return true;
2146 }
2147 
date_setFullYear(JSContext * cx,unsigned argc,Value * vp)2148 static bool date_setFullYear(JSContext* cx, unsigned argc, Value* vp) {
2149   CallArgs args = CallArgsFromVp(argc, vp);
2150   return CallNonGenericMethod<IsDate, date_setFullYear_impl>(cx, args);
2151 }
2152 
2153 /* ES5 15.9.5.41. */
date_setUTCFullYear_impl(JSContext * cx,const CallArgs & args)2154 MOZ_ALWAYS_INLINE bool date_setUTCFullYear_impl(JSContext* cx,
2155                                                 const CallArgs& args) {
2156   Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2157 
2158   /* Step 1. */
2159   double t = ThisUTCTimeOrZero(dateObj);
2160 
2161   /* Step 2. */
2162   double y;
2163   if (!ToNumber(cx, args.get(0), &y)) return false;
2164 
2165   /* Step 3. */
2166   double m;
2167   if (!GetMonthOrDefault(cx, args, 1, t, &m)) return false;
2168 
2169   /* Step 4. */
2170   double date;
2171   if (!GetDateOrDefault(cx, args, 2, t, &date)) return false;
2172 
2173   /* Step 5. */
2174   double newDate = MakeDate(MakeDay(y, m, date), TimeWithinDay(t));
2175 
2176   /* Step 6. */
2177   ClippedTime v = TimeClip(newDate);
2178 
2179   /* Steps 7-8. */
2180   dateObj->setUTCTime(v, args.rval());
2181   return true;
2182 }
2183 
date_setUTCFullYear(JSContext * cx,unsigned argc,Value * vp)2184 static bool date_setUTCFullYear(JSContext* cx, unsigned argc, Value* vp) {
2185   CallArgs args = CallArgsFromVp(argc, vp);
2186   return CallNonGenericMethod<IsDate, date_setUTCFullYear_impl>(cx, args);
2187 }
2188 
2189 /* ES5 Annex B.2.5. */
date_setYear_impl(JSContext * cx,const CallArgs & args)2190 MOZ_ALWAYS_INLINE bool date_setYear_impl(JSContext* cx, const CallArgs& args) {
2191   Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2192 
2193   /* Step 1. */
2194   double t = ThisLocalTimeOrZero(dateObj);
2195 
2196   /* Step 2. */
2197   double y;
2198   if (!ToNumber(cx, args.get(0), &y)) return false;
2199 
2200   /* Step 3. */
2201   if (IsNaN(y)) {
2202     dateObj->setUTCTime(ClippedTime::invalid(), args.rval());
2203     return true;
2204   }
2205 
2206   /* Step 4. */
2207   double yint = ToInteger(y);
2208   if (0 <= yint && yint <= 99) yint += 1900;
2209 
2210   /* Step 5. */
2211   double day = MakeDay(yint, MonthFromTime(t), DateFromTime(t));
2212 
2213   /* Step 6. */
2214   double u = UTC(MakeDate(day, TimeWithinDay(t)));
2215 
2216   /* Steps 7-8. */
2217   dateObj->setUTCTime(TimeClip(u), args.rval());
2218   return true;
2219 }
2220 
date_setYear(JSContext * cx,unsigned argc,Value * vp)2221 static bool date_setYear(JSContext* cx, unsigned argc, Value* vp) {
2222   CallArgs args = CallArgsFromVp(argc, vp);
2223   return CallNonGenericMethod<IsDate, date_setYear_impl>(cx, args);
2224 }
2225 
2226 /* constants for toString, toUTCString */
2227 static const char js_NaN_date_str[] = "Invalid Date";
2228 static const char* const days[] = {"Sun", "Mon", "Tue", "Wed",
2229                                    "Thu", "Fri", "Sat"};
2230 static const char* const months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
2231                                      "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
2232 
2233 /* ES5 B.2.6. */
date_toGMTString_impl(JSContext * cx,const CallArgs & args)2234 MOZ_ALWAYS_INLINE bool date_toGMTString_impl(JSContext* cx,
2235                                              const CallArgs& args) {
2236   double utctime =
2237       args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
2238 
2239   JSString* str;
2240   if (!IsFinite(utctime)) {
2241     str = NewStringCopyZ<CanGC>(cx, js_NaN_date_str);
2242   } else {
2243     char buf[100];
2244     SprintfLiteral(buf, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
2245                    days[int(WeekDay(utctime))], int(DateFromTime(utctime)),
2246                    months[int(MonthFromTime(utctime))],
2247                    int(YearFromTime(utctime)), int(HourFromTime(utctime)),
2248                    int(MinFromTime(utctime)), int(SecFromTime(utctime)));
2249 
2250     str = NewStringCopyZ<CanGC>(cx, buf);
2251   }
2252 
2253   if (!str) return false;
2254   args.rval().setString(str);
2255   return true;
2256 }
2257 
date_toGMTString(JSContext * cx,unsigned argc,Value * vp)2258 static bool date_toGMTString(JSContext* cx, unsigned argc, Value* vp) {
2259   CallArgs args = CallArgsFromVp(argc, vp);
2260   return CallNonGenericMethod<IsDate, date_toGMTString_impl>(cx, args);
2261 }
2262 
2263 /* ES6 draft 2015-01-15 20.3.4.36. */
date_toISOString_impl(JSContext * cx,const CallArgs & args)2264 MOZ_ALWAYS_INLINE bool date_toISOString_impl(JSContext* cx,
2265                                              const CallArgs& args) {
2266   double utctime =
2267       args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
2268   if (!IsFinite(utctime)) {
2269     JS_ReportErrorNumberASCII(cx, js::GetErrorMessage, nullptr,
2270                               JSMSG_INVALID_DATE);
2271     return false;
2272   }
2273 
2274   char buf[100];
2275   int year = int(YearFromTime(utctime));
2276   if (year < 0 || year > 9999) {
2277     SprintfLiteral(buf, "%+.6d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ",
2278                    int(YearFromTime(utctime)), int(MonthFromTime(utctime)) + 1,
2279                    int(DateFromTime(utctime)), int(HourFromTime(utctime)),
2280                    int(MinFromTime(utctime)), int(SecFromTime(utctime)),
2281                    int(msFromTime(utctime)));
2282   } else {
2283     SprintfLiteral(buf, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ",
2284                    int(YearFromTime(utctime)), int(MonthFromTime(utctime)) + 1,
2285                    int(DateFromTime(utctime)), int(HourFromTime(utctime)),
2286                    int(MinFromTime(utctime)), int(SecFromTime(utctime)),
2287                    int(msFromTime(utctime)));
2288   }
2289 
2290   JSString* str = NewStringCopyZ<CanGC>(cx, buf);
2291   if (!str) return false;
2292   args.rval().setString(str);
2293   return true;
2294 }
2295 
date_toISOString(JSContext * cx,unsigned argc,Value * vp)2296 static bool date_toISOString(JSContext* cx, unsigned argc, Value* vp) {
2297   CallArgs args = CallArgsFromVp(argc, vp);
2298   return CallNonGenericMethod<IsDate, date_toISOString_impl>(cx, args);
2299 }
2300 
2301 /* ES5 15.9.5.44. */
date_toJSON(JSContext * cx,unsigned argc,Value * vp)2302 static bool date_toJSON(JSContext* cx, unsigned argc, Value* vp) {
2303   CallArgs args = CallArgsFromVp(argc, vp);
2304 
2305   /* Step 1. */
2306   RootedObject obj(cx, ToObject(cx, args.thisv()));
2307   if (!obj) return false;
2308 
2309   /* Step 2. */
2310   RootedValue tv(cx, ObjectValue(*obj));
2311   if (!ToPrimitive(cx, JSTYPE_NUMBER, &tv)) return false;
2312 
2313   /* Step 3. */
2314   if (tv.isDouble() && !IsFinite(tv.toDouble())) {
2315     args.rval().setNull();
2316     return true;
2317   }
2318 
2319   /* Step 4. */
2320   RootedValue toISO(cx);
2321   if (!GetProperty(cx, obj, obj, cx->names().toISOString, &toISO)) return false;
2322 
2323   /* Step 5. */
2324   if (!IsCallable(toISO)) {
2325     JS_ReportErrorFlagsAndNumberASCII(cx, JSREPORT_ERROR, js::GetErrorMessage,
2326                                       nullptr, JSMSG_BAD_TOISOSTRING_PROP);
2327     return false;
2328   }
2329 
2330   /* Step 6. */
2331   return Call(cx, toISO, obj, args.rval());
2332 }
2333 
2334 /* Interface to PRMJTime date struct. */
ToPRMJTime(double localTime,double utcTime)2335 static PRMJTime ToPRMJTime(double localTime, double utcTime) {
2336   double year = YearFromTime(localTime);
2337 
2338   PRMJTime prtm;
2339   prtm.tm_usec = int32_t(msFromTime(localTime)) * 1000;
2340   prtm.tm_sec = int8_t(SecFromTime(localTime));
2341   prtm.tm_min = int8_t(MinFromTime(localTime));
2342   prtm.tm_hour = int8_t(HourFromTime(localTime));
2343   prtm.tm_mday = int8_t(DateFromTime(localTime));
2344   prtm.tm_mon = int8_t(MonthFromTime(localTime));
2345   prtm.tm_wday = int8_t(WeekDay(localTime));
2346   prtm.tm_year = year;
2347   prtm.tm_yday = int16_t(DayWithinYear(localTime, year));
2348   prtm.tm_isdst = (DaylightSavingTA(utcTime) != 0);
2349 
2350   return prtm;
2351 }
2352 
FormatTime(char * buf,int buflen,const char * fmt,double utcTime,double localTime)2353 static size_t FormatTime(char* buf, int buflen, const char* fmt, double utcTime,
2354                          double localTime) {
2355   PRMJTime prtm = ToPRMJTime(localTime, utcTime);
2356   int eqivalentYear = IsRepresentableAsTime32(utcTime)
2357                           ? prtm.tm_year
2358                           : EquivalentYearForDST(prtm.tm_year);
2359   int offsetInSeconds = (int)floor((localTime - utcTime) / msPerSecond);
2360 
2361   return PRMJ_FormatTime(buf, buflen, fmt, &prtm, eqivalentYear,
2362                          offsetInSeconds);
2363 }
2364 
2365 enum class FormatSpec { DateTime, Date, Time };
2366 
FormatDate(JSContext * cx,double utcTime,FormatSpec format,MutableHandleValue rval)2367 static bool FormatDate(JSContext* cx, double utcTime, FormatSpec format,
2368                        MutableHandleValue rval) {
2369   JSString* str;
2370   if (!IsFinite(utcTime)) {
2371     str = NewStringCopyZ<CanGC>(cx, js_NaN_date_str);
2372   } else {
2373     MOZ_ASSERT(NumbersAreIdentical(TimeClip(utcTime).toDouble(), utcTime));
2374 
2375     double localTime = LocalTime(utcTime);
2376 
2377     int offset = 0;
2378     char tzbuf[100];
2379     bool usetz = false;
2380     if (format == FormatSpec::DateTime || format == FormatSpec::Time) {
2381       /*
2382        * Offset from GMT in minutes.  The offset includes daylight
2383        * savings, if it applies.
2384        */
2385       int minutes = (int)floor((localTime - utcTime) / msPerMinute);
2386 
2387       /* Map 510 minutes to 0830 hours. */
2388       offset = (minutes / 60) * 100 + minutes % 60;
2389 
2390       /*
2391        * Print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997".
2392        *
2393        * The TZA is printed as 'GMT-0800' rather than as 'PST' to avoid
2394        * operating-system dependence on strftime (which PRMJ_FormatTime
2395        * calls, for %Z only.)  win32 prints PST as
2396        * 'Pacific Standard Time.'  This way we always know what we're
2397        * getting, and can parse it if we produce it.  The OS time zone
2398        * string is included as a comment.
2399        */
2400 
2401       /* get a time zone string from the OS to include as a comment. */
2402       size_t tzlen =
2403           FormatTime(tzbuf, sizeof tzbuf, "(%Z)", utcTime, localTime);
2404       if (tzlen != 0) {
2405         /*
2406          * Decide whether to use the resulting time zone string.
2407          *
2408          * Reject it if it contains any non-ASCII or non-printable
2409          * characters.  It's then likely in some other character
2410          * encoding, and we probably won't display it correctly.
2411          */
2412         usetz = true;
2413         for (size_t i = 0; i < tzlen; i++) {
2414           char16_t c = tzbuf[i];
2415           if (c > 127 || !isprint(c)) {
2416             usetz = false;
2417             break;
2418           }
2419         }
2420 
2421         /* Also reject it if it's not parenthesized or if it's '()'. */
2422         if (tzbuf[0] != '(' || tzbuf[1] == ')') usetz = false;
2423       }
2424     }
2425 
2426     char buf[100];
2427     switch (format) {
2428       case FormatSpec::DateTime:
2429         /* Tue Oct 31 2000 09:41:40 GMT-0800 (PST) */
2430         SprintfLiteral(
2431             buf, "%s %s %.2d %.4d %.2d:%.2d:%.2d GMT%+.4d%s%s",
2432             days[int(WeekDay(localTime))],
2433             months[int(MonthFromTime(localTime))], int(DateFromTime(localTime)),
2434             int(YearFromTime(localTime)), int(HourFromTime(localTime)),
2435             int(MinFromTime(localTime)), int(SecFromTime(localTime)), offset,
2436             usetz ? " " : "", usetz ? tzbuf : "");
2437         break;
2438       case FormatSpec::Date:
2439         /* Tue Oct 31 2000 */
2440         SprintfLiteral(buf, "%s %s %.2d %.4d", days[int(WeekDay(localTime))],
2441                        months[int(MonthFromTime(localTime))],
2442                        int(DateFromTime(localTime)),
2443                        int(YearFromTime(localTime)));
2444         break;
2445       case FormatSpec::Time:
2446         /* 09:41:40 GMT-0800 (PST) */
2447         SprintfLiteral(buf, "%.2d:%.2d:%.2d GMT%+.4d%s%s",
2448                        int(HourFromTime(localTime)),
2449                        int(MinFromTime(localTime)), int(SecFromTime(localTime)),
2450                        offset, usetz ? " " : "", usetz ? tzbuf : "");
2451         break;
2452     }
2453 
2454     str = NewStringCopyZ<CanGC>(cx, buf);
2455   }
2456 
2457   if (!str) return false;
2458   rval.setString(str);
2459   return true;
2460 }
2461 
2462 #if !EXPOSE_INTL_API
ToLocaleFormatHelper(JSContext * cx,HandleObject obj,const char * format,MutableHandleValue rval)2463 static bool ToLocaleFormatHelper(JSContext* cx, HandleObject obj,
2464                                  const char* format, MutableHandleValue rval) {
2465   double utcTime = obj->as<DateObject>().UTCTime().toNumber();
2466 
2467   char buf[100];
2468   if (!IsFinite(utcTime)) {
2469     strcpy(buf, js_NaN_date_str);
2470   } else {
2471     double localTime = LocalTime(utcTime);
2472 
2473     /* Let PRMJTime format it. */
2474     size_t result_len = FormatTime(buf, sizeof buf, format, utcTime, localTime);
2475 
2476     /* If it failed, default to toString. */
2477     if (result_len == 0)
2478       return FormatDate(cx, utcTime, FormatSpec::DateTime, rval);
2479 
2480     /* Hacked check against undesired 2-digit year 00/00/00 form. */
2481     if (strcmp(format, "%x") == 0 && result_len >= 6 &&
2482         /* Format %x means use OS settings, which may have 2-digit yr, so
2483            hack end of 3/11/22 or 11.03.22 or 11Mar22 to use 4-digit yr...*/
2484         !isdigit(buf[result_len - 3]) && isdigit(buf[result_len - 2]) &&
2485         isdigit(buf[result_len - 1]) &&
2486         /* ...but not if starts with 4-digit year, like 2022/3/11. */
2487         !(isdigit(buf[0]) && isdigit(buf[1]) && isdigit(buf[2]) &&
2488           isdigit(buf[3]))) {
2489       int year = int(YearFromTime(localTime));
2490       snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2), "%d",
2491                year);
2492     }
2493   }
2494 
2495   if (cx->runtime()->localeCallbacks &&
2496       cx->runtime()->localeCallbacks->localeToUnicode)
2497     return cx->runtime()->localeCallbacks->localeToUnicode(cx, buf, rval);
2498 
2499   JSString* str = NewStringCopyZ<CanGC>(cx, buf);
2500   if (!str) return false;
2501   rval.setString(str);
2502   return true;
2503 }
2504 
2505 /* ES5 15.9.5.5. */
date_toLocaleString_impl(JSContext * cx,const CallArgs & args)2506 MOZ_ALWAYS_INLINE bool date_toLocaleString_impl(JSContext* cx,
2507                                                 const CallArgs& args) {
2508   /*
2509    * Use '%#c' for windows, because '%c' is backward-compatible and non-y2k
2510    * with msvc; '%#c' requests that a full year be used in the result string.
2511    */
2512   static const char format[] =
2513 #if defined(_WIN32) && !defined(__MWERKS__)
2514       "%#c"
2515 #else
2516       "%c"
2517 #endif
2518       ;
2519 
2520   Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2521   return ToLocaleFormatHelper(cx, dateObj, format, args.rval());
2522 }
2523 
date_toLocaleString(JSContext * cx,unsigned argc,Value * vp)2524 static bool date_toLocaleString(JSContext* cx, unsigned argc, Value* vp) {
2525   CallArgs args = CallArgsFromVp(argc, vp);
2526   return CallNonGenericMethod<IsDate, date_toLocaleString_impl>(cx, args);
2527 }
2528 
2529 /* ES5 15.9.5.6. */
date_toLocaleDateString_impl(JSContext * cx,const CallArgs & args)2530 MOZ_ALWAYS_INLINE bool date_toLocaleDateString_impl(JSContext* cx,
2531                                                     const CallArgs& args) {
2532   /*
2533    * Use '%#x' for windows, because '%x' is backward-compatible and non-y2k
2534    * with msvc; '%#x' requests that a full year be used in the result string.
2535    */
2536   static const char format[] =
2537 #if defined(_WIN32) && !defined(__MWERKS__)
2538       "%#x"
2539 #else
2540       "%x"
2541 #endif
2542       ;
2543 
2544   Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2545   return ToLocaleFormatHelper(cx, dateObj, format, args.rval());
2546 }
2547 
date_toLocaleDateString(JSContext * cx,unsigned argc,Value * vp)2548 static bool date_toLocaleDateString(JSContext* cx, unsigned argc, Value* vp) {
2549   CallArgs args = CallArgsFromVp(argc, vp);
2550   return CallNonGenericMethod<IsDate, date_toLocaleDateString_impl>(cx, args);
2551 }
2552 
2553 /* ES5 15.9.5.7. */
date_toLocaleTimeString_impl(JSContext * cx,const CallArgs & args)2554 MOZ_ALWAYS_INLINE bool date_toLocaleTimeString_impl(JSContext* cx,
2555                                                     const CallArgs& args) {
2556   Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2557   return ToLocaleFormatHelper(cx, dateObj, "%X", args.rval());
2558 }
2559 
date_toLocaleTimeString(JSContext * cx,unsigned argc,Value * vp)2560 static bool date_toLocaleTimeString(JSContext* cx, unsigned argc, Value* vp) {
2561   CallArgs args = CallArgsFromVp(argc, vp);
2562   return CallNonGenericMethod<IsDate, date_toLocaleTimeString_impl>(cx, args);
2563 }
2564 #endif /* !EXPOSE_INTL_API */
2565 
2566 /* ES5 15.9.5.4. */
date_toTimeString_impl(JSContext * cx,const CallArgs & args)2567 MOZ_ALWAYS_INLINE bool date_toTimeString_impl(JSContext* cx,
2568                                               const CallArgs& args) {
2569   return FormatDate(
2570       cx, args.thisv().toObject().as<DateObject>().UTCTime().toNumber(),
2571       FormatSpec::Time, args.rval());
2572 }
2573 
date_toTimeString(JSContext * cx,unsigned argc,Value * vp)2574 static bool date_toTimeString(JSContext* cx, unsigned argc, Value* vp) {
2575   CallArgs args = CallArgsFromVp(argc, vp);
2576   return CallNonGenericMethod<IsDate, date_toTimeString_impl>(cx, args);
2577 }
2578 
2579 /* ES5 15.9.5.3. */
date_toDateString_impl(JSContext * cx,const CallArgs & args)2580 MOZ_ALWAYS_INLINE bool date_toDateString_impl(JSContext* cx,
2581                                               const CallArgs& args) {
2582   return FormatDate(
2583       cx, args.thisv().toObject().as<DateObject>().UTCTime().toNumber(),
2584       FormatSpec::Date, args.rval());
2585 }
2586 
date_toDateString(JSContext * cx,unsigned argc,Value * vp)2587 static bool date_toDateString(JSContext* cx, unsigned argc, Value* vp) {
2588   CallArgs args = CallArgsFromVp(argc, vp);
2589   return CallNonGenericMethod<IsDate, date_toDateString_impl>(cx, args);
2590 }
2591 
date_toSource_impl(JSContext * cx,const CallArgs & args)2592 MOZ_ALWAYS_INLINE bool date_toSource_impl(JSContext* cx, const CallArgs& args) {
2593   StringBuffer sb(cx);
2594   if (!sb.append("(new Date(") ||
2595       !NumberValueToStringBuffer(
2596           cx, args.thisv().toObject().as<DateObject>().UTCTime(), sb) ||
2597       !sb.append("))")) {
2598     return false;
2599   }
2600 
2601   JSString* str = sb.finishString();
2602   if (!str) return false;
2603   args.rval().setString(str);
2604   return true;
2605 }
2606 
date_toSource(JSContext * cx,unsigned argc,Value * vp)2607 static bool date_toSource(JSContext* cx, unsigned argc, Value* vp) {
2608   CallArgs args = CallArgsFromVp(argc, vp);
2609   return CallNonGenericMethod<IsDate, date_toSource_impl>(cx, args);
2610 }
2611 
2612 // ES6 20.3.4.41.
date_toString_impl(JSContext * cx,const CallArgs & args)2613 MOZ_ALWAYS_INLINE bool date_toString_impl(JSContext* cx, const CallArgs& args) {
2614   // Steps 1-2.
2615   return FormatDate(
2616       cx, args.thisv().toObject().as<DateObject>().UTCTime().toNumber(),
2617       FormatSpec::DateTime, args.rval());
2618 }
2619 
date_toString(JSContext * cx,unsigned argc,Value * vp)2620 bool date_toString(JSContext* cx, unsigned argc, Value* vp) {
2621   CallArgs args = CallArgsFromVp(argc, vp);
2622   return CallNonGenericMethod<IsDate, date_toString_impl>(cx, args);
2623 }
2624 
date_valueOf_impl(JSContext * cx,const CallArgs & args)2625 MOZ_ALWAYS_INLINE bool date_valueOf_impl(JSContext* cx, const CallArgs& args) {
2626   Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
2627   args.rval().set(dateObj->UTCTime());
2628   return true;
2629 }
2630 
date_valueOf(JSContext * cx,unsigned argc,Value * vp)2631 bool js::date_valueOf(JSContext* cx, unsigned argc, Value* vp) {
2632   CallArgs args = CallArgsFromVp(argc, vp);
2633   return CallNonGenericMethod<IsDate, date_valueOf_impl>(cx, args);
2634 }
2635 
2636 // ES6 20.3.4.45 Date.prototype[@@toPrimitive]
date_toPrimitive(JSContext * cx,unsigned argc,Value * vp)2637 static bool date_toPrimitive(JSContext* cx, unsigned argc, Value* vp) {
2638   CallArgs args = CallArgsFromVp(argc, vp);
2639 
2640   // Steps 1-2.
2641   if (!args.thisv().isObject()) {
2642     ReportIncompatible(cx, args);
2643     return false;
2644   }
2645 
2646   // Steps 3-5.
2647   JSType hint;
2648   if (!GetFirstArgumentAsTypeHint(cx, args, &hint)) return false;
2649   if (hint == JSTYPE_UNDEFINED) hint = JSTYPE_STRING;
2650 
2651   args.rval().set(args.thisv());
2652   RootedObject obj(cx, &args.thisv().toObject());
2653   return OrdinaryToPrimitive(cx, obj, hint, args.rval());
2654 }
2655 
2656 static const JSFunctionSpec date_static_methods[] = {
2657     JS_FN("UTC", date_UTC, 7, 0), JS_FN("parse", date_parse, 1, 0),
2658     JS_FN("now", date_now, 0, 0), JS_FS_END};
2659 
2660 static const JSFunctionSpec date_methods[] = {
2661     JS_FN("getTime", date_getTime, 0, 0),
2662     JS_FN("getTimezoneOffset", date_getTimezoneOffset, 0, 0),
2663     JS_FN("getYear", date_getYear, 0, 0),
2664     JS_FN("getFullYear", date_getFullYear, 0, 0),
2665     JS_FN("getUTCFullYear", date_getUTCFullYear, 0, 0),
2666     JS_FN("getMonth", date_getMonth, 0, 0),
2667     JS_FN("getUTCMonth", date_getUTCMonth, 0, 0),
2668     JS_FN("getDate", date_getDate, 0, 0),
2669     JS_FN("getUTCDate", date_getUTCDate, 0, 0),
2670     JS_FN("getDay", date_getDay, 0, 0),
2671     JS_FN("getUTCDay", date_getUTCDay, 0, 0),
2672     JS_FN("getHours", date_getHours, 0, 0),
2673     JS_FN("getUTCHours", date_getUTCHours, 0, 0),
2674     JS_FN("getMinutes", date_getMinutes, 0, 0),
2675     JS_FN("getUTCMinutes", date_getUTCMinutes, 0, 0),
2676     JS_FN("getSeconds", date_getUTCSeconds, 0, 0),
2677     JS_FN("getUTCSeconds", date_getUTCSeconds, 0, 0),
2678     JS_FN("getMilliseconds", date_getUTCMilliseconds, 0, 0),
2679     JS_FN("getUTCMilliseconds", date_getUTCMilliseconds, 0, 0),
2680     JS_FN("setTime", date_setTime, 1, 0),
2681     JS_FN("setYear", date_setYear, 1, 0),
2682     JS_FN("setFullYear", date_setFullYear, 3, 0),
2683     JS_FN("setUTCFullYear", date_setUTCFullYear, 3, 0),
2684     JS_FN("setMonth", date_setMonth, 2, 0),
2685     JS_FN("setUTCMonth", date_setUTCMonth, 2, 0),
2686     JS_FN("setDate", date_setDate, 1, 0),
2687     JS_FN("setUTCDate", date_setUTCDate, 1, 0),
2688     JS_FN("setHours", date_setHours, 4, 0),
2689     JS_FN("setUTCHours", date_setUTCHours, 4, 0),
2690     JS_FN("setMinutes", date_setMinutes, 3, 0),
2691     JS_FN("setUTCMinutes", date_setUTCMinutes, 3, 0),
2692     JS_FN("setSeconds", date_setSeconds, 2, 0),
2693     JS_FN("setUTCSeconds", date_setUTCSeconds, 2, 0),
2694     JS_FN("setMilliseconds", date_setMilliseconds, 1, 0),
2695     JS_FN("setUTCMilliseconds", date_setUTCMilliseconds, 1, 0),
2696     JS_FN("toUTCString", date_toGMTString, 0, 0),
2697 #if EXPOSE_INTL_API
2698     JS_SELF_HOSTED_FN(js_toLocaleString_str, "Date_toLocaleString", 0, 0),
2699     JS_SELF_HOSTED_FN("toLocaleDateString", "Date_toLocaleDateString", 0, 0),
2700     JS_SELF_HOSTED_FN("toLocaleTimeString", "Date_toLocaleTimeString", 0, 0),
2701 #else
2702     JS_FN(js_toLocaleString_str, date_toLocaleString, 0, 0),
2703     JS_FN("toLocaleDateString", date_toLocaleDateString, 0, 0),
2704     JS_FN("toLocaleTimeString", date_toLocaleTimeString, 0, 0),
2705 #endif
2706     JS_FN("toDateString", date_toDateString, 0, 0),
2707     JS_FN("toTimeString", date_toTimeString, 0, 0),
2708     JS_FN("toISOString", date_toISOString, 0, 0),
2709     JS_FN(js_toJSON_str, date_toJSON, 1, 0),
2710     JS_FN(js_toSource_str, date_toSource, 0, 0),
2711     JS_FN(js_toString_str, date_toString, 0, 0),
2712     JS_FN(js_valueOf_str, date_valueOf, 0, 0),
2713     JS_SYM_FN(toPrimitive, date_toPrimitive, 1, JSPROP_READONLY),
2714     JS_FS_END};
2715 
NewDateObject(JSContext * cx,const CallArgs & args,ClippedTime t)2716 static bool NewDateObject(JSContext* cx, const CallArgs& args, ClippedTime t) {
2717   MOZ_ASSERT(args.isConstructing());
2718 
2719   RootedObject proto(cx);
2720   if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) return false;
2721 
2722   JSObject* obj = NewDateObjectMsec(cx, t, proto);
2723   if (!obj) return false;
2724 
2725   args.rval().setObject(*obj);
2726   return true;
2727 }
2728 
ToDateString(JSContext * cx,const CallArgs & args,ClippedTime t)2729 static bool ToDateString(JSContext* cx, const CallArgs& args, ClippedTime t) {
2730   return FormatDate(cx, t.toDouble(), FormatSpec::DateTime, args.rval());
2731 }
2732 
DateNoArguments(JSContext * cx,const CallArgs & args)2733 static bool DateNoArguments(JSContext* cx, const CallArgs& args) {
2734   MOZ_ASSERT(args.length() == 0);
2735 
2736   ClippedTime now = NowAsMillis(cx);
2737 
2738   if (args.isConstructing()) return NewDateObject(cx, args, now);
2739 
2740   return ToDateString(cx, args, now);
2741 }
2742 
DateOneArgument(JSContext * cx,const CallArgs & args)2743 static bool DateOneArgument(JSContext* cx, const CallArgs& args) {
2744   MOZ_ASSERT(args.length() == 1);
2745 
2746   if (args.isConstructing()) {
2747     if (args[0].isObject()) {
2748       RootedObject obj(cx, &args[0].toObject());
2749 
2750       ESClass cls;
2751       if (!GetBuiltinClass(cx, obj, &cls)) return false;
2752 
2753       if (cls == ESClass::Date) {
2754         RootedValue unboxed(cx);
2755         if (!Unbox(cx, obj, &unboxed)) return false;
2756 
2757         return NewDateObject(cx, args, TimeClip(unboxed.toNumber()));
2758       }
2759     }
2760 
2761     if (!ToPrimitive(cx, args[0])) return false;
2762 
2763     ClippedTime t;
2764     if (args[0].isString()) {
2765       JSLinearString* linearStr = args[0].toString()->ensureLinear(cx);
2766       if (!linearStr) return false;
2767 
2768       if (!ParseDate(linearStr, &t)) t = ClippedTime::invalid();
2769     } else {
2770       double d;
2771       if (!ToNumber(cx, args[0], &d)) return false;
2772       t = TimeClip(d);
2773     }
2774 
2775     return NewDateObject(cx, args, t);
2776   }
2777 
2778   return ToDateString(cx, args, NowAsMillis(cx));
2779 }
2780 
DateMultipleArguments(JSContext * cx,const CallArgs & args)2781 static bool DateMultipleArguments(JSContext* cx, const CallArgs& args) {
2782   MOZ_ASSERT(args.length() >= 2);
2783 
2784   // Step 3.
2785   if (args.isConstructing()) {
2786     // Steps 3a-b.
2787     double y;
2788     if (!ToNumber(cx, args[0], &y)) return false;
2789 
2790     // Steps 3c-d.
2791     double m;
2792     if (!ToNumber(cx, args[1], &m)) return false;
2793 
2794     // Steps 3e-f.
2795     double dt;
2796     if (args.length() >= 3) {
2797       if (!ToNumber(cx, args[2], &dt)) return false;
2798     } else {
2799       dt = 1;
2800     }
2801 
2802     // Steps 3g-h.
2803     double h;
2804     if (args.length() >= 4) {
2805       if (!ToNumber(cx, args[3], &h)) return false;
2806     } else {
2807       h = 0;
2808     }
2809 
2810     // Steps 3i-j.
2811     double min;
2812     if (args.length() >= 5) {
2813       if (!ToNumber(cx, args[4], &min)) return false;
2814     } else {
2815       min = 0;
2816     }
2817 
2818     // Steps 3k-l.
2819     double s;
2820     if (args.length() >= 6) {
2821       if (!ToNumber(cx, args[5], &s)) return false;
2822     } else {
2823       s = 0;
2824     }
2825 
2826     // Steps 3m-n.
2827     double milli;
2828     if (args.length() >= 7) {
2829       if (!ToNumber(cx, args[6], &milli)) return false;
2830     } else {
2831       milli = 0;
2832     }
2833 
2834     // Step 3o.
2835     double yr = y;
2836     if (!IsNaN(y)) {
2837       double yint = ToInteger(y);
2838       if (0 <= yint && yint <= 99) yr = 1900 + yint;
2839     }
2840 
2841     // Step 3p.
2842     double finalDate = MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli));
2843 
2844     // Steps 3q-t.
2845     return NewDateObject(cx, args, TimeClip(UTC(finalDate)));
2846   }
2847 
2848   return ToDateString(cx, args, NowAsMillis(cx));
2849 }
2850 
DateConstructor(JSContext * cx,unsigned argc,Value * vp)2851 bool js::DateConstructor(JSContext* cx, unsigned argc, Value* vp) {
2852   CallArgs args = CallArgsFromVp(argc, vp);
2853 
2854   if (args.length() == 0) return DateNoArguments(cx, args);
2855 
2856   if (args.length() == 1) return DateOneArgument(cx, args);
2857 
2858   return DateMultipleArguments(cx, args);
2859 }
2860 
2861 // ES6 final draft 20.3.4.
CreateDatePrototype(JSContext * cx,JSProtoKey key)2862 static JSObject* CreateDatePrototype(JSContext* cx, JSProtoKey key) {
2863   return GlobalObject::createBlankPrototype(cx, cx->global(),
2864                                             &DateObject::protoClass_);
2865 }
2866 
FinishDateClassInit(JSContext * cx,HandleObject ctor,HandleObject proto)2867 static bool FinishDateClassInit(JSContext* cx, HandleObject ctor,
2868                                 HandleObject proto) {
2869   /*
2870    * Date.prototype.toGMTString has the same initial value as
2871    * Date.prototype.toUTCString.
2872    */
2873   RootedValue toUTCStringFun(cx);
2874   RootedId toUTCStringId(cx, NameToId(cx->names().toUTCString));
2875   RootedId toGMTStringId(cx, NameToId(cx->names().toGMTString));
2876   return NativeGetProperty(cx, proto.as<NativeObject>(), toUTCStringId,
2877                            &toUTCStringFun) &&
2878          NativeDefineDataProperty(cx, proto.as<NativeObject>(), toGMTStringId,
2879                                   toUTCStringFun, 0);
2880 }
2881 
2882 static const ClassSpec DateObjectClassSpec = {
2883     GenericCreateConstructor<DateConstructor, 7, gc::AllocKind::FUNCTION>,
2884     CreateDatePrototype,
2885     date_static_methods,
2886     nullptr,
2887     date_methods,
2888     nullptr,
2889     FinishDateClassInit};
2890 
2891 const Class DateObject::class_ = {js_Date_str,
2892                                   JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
2893                                       JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
2894                                   JS_NULL_CLASS_OPS, &DateObjectClassSpec};
2895 
2896 const Class DateObject::protoClass_ = {js_Object_str,
2897                                        JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
2898                                        JS_NULL_CLASS_OPS, &DateObjectClassSpec};
2899 
NewDateObjectMsec(JSContext * cx,ClippedTime t,HandleObject proto)2900 JSObject* js::NewDateObjectMsec(JSContext* cx, ClippedTime t,
2901                                 HandleObject proto /* = nullptr */) {
2902   JSObject* obj = NewObjectWithClassProto(cx, &DateObject::class_, proto);
2903   if (!obj) return nullptr;
2904   obj->as<DateObject>().setUTCTime(t);
2905   return obj;
2906 }
2907 
NewDateObject(JSContext * cx,int year,int mon,int mday,int hour,int min,int sec)2908 JS_FRIEND_API JSObject* js::NewDateObject(JSContext* cx, int year, int mon,
2909                                           int mday, int hour, int min,
2910                                           int sec) {
2911   MOZ_ASSERT(mon < 12);
2912   double msec_time =
2913       MakeDate(MakeDay(year, mon, mday), MakeTime(hour, min, sec, 0.0));
2914   return NewDateObjectMsec(cx, TimeClip(UTC(msec_time)));
2915 }
2916 
DateIsValid(JSContext * cx,HandleObject obj,bool * isValid)2917 JS_FRIEND_API bool js::DateIsValid(JSContext* cx, HandleObject obj,
2918                                    bool* isValid) {
2919   ESClass cls;
2920   if (!GetBuiltinClass(cx, obj, &cls)) return false;
2921 
2922   if (cls != ESClass::Date) {
2923     *isValid = false;
2924     return true;
2925   }
2926 
2927   RootedValue unboxed(cx);
2928   if (!Unbox(cx, obj, &unboxed)) return false;
2929 
2930   *isValid = !IsNaN(unboxed.toNumber());
2931   return true;
2932 }
2933 
DateGetMsecSinceEpoch(JSContext * cx,HandleObject obj,double * msecsSinceEpoch)2934 JS_FRIEND_API bool js::DateGetMsecSinceEpoch(JSContext* cx, HandleObject obj,
2935                                              double* msecsSinceEpoch) {
2936   ESClass cls;
2937   if (!GetBuiltinClass(cx, obj, &cls)) return false;
2938 
2939   if (cls != ESClass::Date) {
2940     *msecsSinceEpoch = 0;
2941     return true;
2942   }
2943 
2944   RootedValue unboxed(cx);
2945   if (!Unbox(cx, obj, &unboxed)) return false;
2946 
2947   *msecsSinceEpoch = unboxed.toNumber();
2948   return true;
2949 }
2950