MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1#include <compat.h> // IWYU pragma: keep
2
3#include <math.h>
4#include <stdint.h>
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8
9#ifdef _WIN32
10#define WIN32_LEAN_AND_MEAN
11#include <windows.h>
12#include <sys/timeb.h>
13#else
14#include <sys/time.h>
15#endif
16
17#include "ant.h"
18#include "internal.h"
19#include "errors.h"
20#include "descriptors.h"
21#include "runtime.h"
22#include "silver/engine.h"
23#include "modules/date.h"
24#include "modules/symbol.h"
25
26static const int month_days[] = {
27 31, 28, 31, 30, 31, 30,
28 31, 31, 30, 31, 30, 31
29};
30
31static const char month_names[] =
32 "Jan" "Feb" "Mar" "Apr" "May" "Jun"
33 "Jul" "Aug" "Sep" "Oct" "Nov" "Dec";
34
35static const char day_names[] =
36 "Sun" "Mon" "Tue" "Wed" "Thu" "Fri" "Sat";
37
38static inline double *date_fields_slot(date_fields_t *f, date_field_index_t index) {
39switch (index) {
40 case DATE_FIELD_YEAR: return &f->year;
41 case DATE_FIELD_MONTH: return &f->month;
42 case DATE_FIELD_DAY_OF_MONTH: return &f->day;
43 case DATE_FIELD_HOUR: return &f->hour;
44 case DATE_FIELD_MINUTE: return &f->minute;
45 case DATE_FIELD_SECOND: return &f->second;
46 case DATE_FIELD_MILLISECOND: return &f->millisecond;
47 case DATE_FIELD_DAY_OF_WEEK: return &f->weekday;
48 case DATE_FIELD_TZ_MINUTES: return &f->tz_minutes;
49 default: return NULL;
50}}
51
52static inline double date_fields_get(const date_fields_t *f, date_field_index_t index) {
53 double *slot = date_fields_slot((date_fields_t *)f, index);
54 return slot ? *slot : 0.0;
55}
56
57static inline void date_fields_set(date_fields_t *f, date_field_index_t index, double value) {
58 double *slot = date_fields_slot(f, index);
59 if (slot) *slot = value;
60}
61
62static inline int date_min_int(int a, int b) {
63 return (a < b) ? a : b;
64}
65
66static inline int to_upper_ascii(int c) {
67 if (c >= 'a' && c <= 'z') return c - ('a' - 'A');
68 return c;
69}
70
71static inline bool date_is_primitive(ant_value_t v) {
72 uint8_t t = vtype(v);
73 return
74 t == T_STR
75 || t == T_NUM
76 || t == T_BOOL
77 || t == T_NULL
78 || t == T_UNDEF
79 || t == T_SYMBOL
80 || t == T_BIGINT;
81}
82
83bool is_date_instance(ant_value_t value) {
84 if (!is_object_type(value)) return false;
85 ant_value_t brand = js_get_slot(js_as_obj(value), SLOT_BRAND);
86 return vtype(brand) == T_NUM && (int)js_getnum(brand) == BRAND_DATE;
87}
88
89static bool date_this_time_value(ant_t *js, ant_value_t this_val, double *out, ant_value_t *err) {
90 if (!is_date_instance(this_val)) {
91 if (err) *err = js_mkerr_typed(js, JS_ERR_TYPE, "not a Date object");
92 return false;
93 }
94
95 ant_value_t t = js_get_slot(js_as_obj(this_val), SLOT_DATA);
96 *out = (vtype(t) == T_NUM) ? tod(t) : JS_NAN;
97 return true;
98}
99
100static ant_value_t date_set_this_time_value(ant_t *js, ant_value_t this_val, double v) {
101 if (!is_date_instance(this_val)) {
102 return js_mkerr_typed(js, JS_ERR_TYPE, "not a Date object");
103 }
104
105 js_set_slot(js_as_obj(this_val), SLOT_DATA, tov(v));
106 return tov(v);
107}
108
109static int64_t math_mod(int64_t a, int64_t b) {
110 int64_t m = a % b;
111 return m + (m < 0) * b;
112}
113
114static int64_t floor_div_int64(int64_t a, int64_t b) {
115 int64_t m = a % b;
116 return (a - (m + (m < 0) * b)) / b;
117}
118
119static double date_timeclip(double t) {
120 if (t >= -8.64e15 && t <= 8.64e15) return trunc(t) + 0.0;
121 return JS_NAN;
122}
123
124static int64_t days_in_year(int64_t y) {
125 return 365 + !(y % 4) - !(y % 100) + !(y % 400);
126}
127
128static int64_t days_from_year(int64_t y) {
129 return 365 * (y - 1970)
130 + floor_div_int64(y - 1969, 4)
131 - floor_div_int64(y - 1901, 100)
132 + floor_div_int64(y - 1601, 400);
133}
134
135static int64_t year_from_days(int64_t *days) {
136 int64_t y, d1, nd, d = *days;
137 y = floor_div_int64(d * 10000, 3652425) + 1970;
138
139 while (true) {
140 d1 = d - days_from_year(y);
141
142 if (d1 < 0) {
143 y--;
144 d1 += days_in_year(y);
145 continue;
146 }
147
148 nd = days_in_year(y);
149 if (d1 < nd) break;
150
151 d1 -= nd;
152 y++;
153 }
154
155 *days = d1;
156 return y;
157}
158
159static int get_timezone_offset(int64_t time_ms) {
160#ifdef _WIN32
161 DWORD r;
162 TIME_ZONE_INFORMATION tzi;
163 r = GetTimeZoneInformation(&tzi);
164 if (r == TIME_ZONE_ID_INVALID) return 0;
165 if (r == TIME_ZONE_ID_DAYLIGHT) return (int)(tzi.Bias + tzi.DaylightBias);
166 return (int)tzi.Bias;
167#else
168 time_t ti;
169 struct tm tm_local;
170
171 int64_t time_s = time_ms / 1000;
172 if (sizeof(time_t) == 4) {
173 if ((time_t)-1 < 0) {
174 if (time_s < INT32_MIN) time_s = INT32_MIN;
175 else if (time_s > INT32_MAX) time_s = INT32_MAX;
176 } else {
177 if (time_s < 0) time_s = 0;
178 else if (time_s > UINT32_MAX) time_s = UINT32_MAX;
179 }
180 }
181
182 ti = (time_t)time_s;
183#ifdef _WIN32
184 localtime_s(&tm_local, &ti);
185#else
186 localtime_r(&ti, &tm_local);
187#endif
188
189#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__GLIBC__)
190 return (int)(-tm_local.tm_gmtoff / 60);
191#else
192 struct tm tm_gmt;
193#ifdef _WIN32
194 gmtime_s(&tm_gmt, &ti);
195#else
196 gmtime_r(&ti, &tm_gmt);
197#endif
198 tm_local.tm_isdst = 0;
199 return (int)(difftime(mktime(&tm_gmt), mktime(&tm_local)) / 60);
200#endif
201#endif
202}
203
204static int date_extract_fields_from_time(double dval, date_fields_t *fields, int is_local, int force) {
205 int64_t d, days, wd, y, i, md, h, m, s, ms, tz = 0;
206
207 if (isnan(dval)) {
208 if (!force) return 0;
209 d = 0;
210 } else {
211 d = (int64_t)dval;
212 if (is_local) {
213 tz = -get_timezone_offset(d);
214 d += tz * 60000;
215 }
216 }
217
218 h = math_mod(d, 86400000);
219 days = (d - h) / 86400000;
220 ms = h % 1000;
221 h = (h - ms) / 1000;
222 s = h % 60;
223 h = (h - s) / 60;
224 m = h % 60;
225 h = (h - m) / 60;
226 wd = math_mod(days + 4, 7);
227 y = year_from_days(&days);
228
229 for (i = 0; i < 11; i++) {
230 md = month_days[i];
231 if (i == 1) md += (int)days_in_year(y) - 365;
232 if (days < md) break;
233 days -= md;
234 }
235
236 fields->year = (double)y;
237 fields->month = (double)i;
238 fields->day = (double)(days + 1);
239 fields->hour = (double)h;
240 fields->minute = (double)m;
241 fields->second = (double)s;
242 fields->millisecond = (double)ms;
243 fields->weekday = (double)wd;
244 fields->tz_minutes = (double)tz;
245
246 return 1;
247}
248
249static int date_extract_fields(
250 ant_t *js, ant_value_t this_val,
251 date_fields_t *fields,
252 int is_local, int force, ant_value_t *err
253) {
254 double dval;
255 if (!date_this_time_value(js, this_val, &dval, err)) return -1;
256 return date_extract_fields_from_time(dval, fields, is_local, force);
257}
258
259static double date_make_fields(const date_fields_t *fields, int is_local) {
260 double y, m, dt, ym, mn, day, h, s, milli, time, tv;
261 int yi, mi, i;
262 int64_t days;
263 volatile double temp;
264 double d = JS_NAN;
265
266 y = fields->year;
267 m = fields->month;
268 dt = fields->day;
269
270 ym = y + floor(m / 12);
271 mn = fmod(m, 12);
272 if (mn < 0) mn += 12;
273
274 if (ym < -271821 || ym > 275760) return JS_NAN;
275
276 yi = (int)ym;
277 mi = (int)mn;
278
279 days = days_from_year(yi);
280 for (i = 0; i < mi; i++) {
281 days += month_days[i];
282 if (i == 1) days += days_in_year(yi) - 365;
283 }
284 day = (double)days + dt - 1;
285
286 h = fields->hour;
287 m = fields->minute;
288 s = fields->second;
289 milli = fields->millisecond;
290
291 time = h * 3600000;
292 time += (temp = m * 60000);
293 time += (temp = s * 1000);
294 time += milli;
295
296 tv = (temp = day * 86400000) + time;
297 if (!isfinite(tv)) return JS_NAN;
298
299 if (is_local) {
300 int64_t ti;
301 if (tv < (double)INT64_MIN) ti = INT64_MIN;
302 else if (tv >= 0x1p63) ti = INT64_MAX;
303 else ti = (int64_t)tv;
304 tv += (double)get_timezone_offset(ti) * 60000.0;
305 }
306
307 d = date_timeclip(tv);
308 return d;
309}
310
311ant_value_t get_date_string(ant_t *js, ant_value_t this_val, date_string_spec_t spec) {
312 char buf[96];
313 date_fields_t fields;
314
315 int res, fmt, pos;
316 int y, mon, d, h, m, s, ms, wd, tz;
317 ant_value_t err;
318
319 fmt = (int)spec.fmt;
320 res = date_extract_fields(
321 js, this_val,
322 &fields, spec.fmt == DATE_STRING_FMT_LOCAL,
323 0, &err
324 );
325
326 if (res < 0) return err;
327 if (!res) {
328 if (spec.fmt == DATE_STRING_FMT_ISO) return js_mkerr_typed(js, JS_ERR_RANGE, "Date value is NaN");
329 return js_mkstr(js, "Invalid Date", 12);
330 }
331
332 y = (int)fields.year;
333 mon = (int)fields.month;
334 d = (int)fields.day;
335 h = (int)fields.hour;
336 m = (int)fields.minute;
337 s = (int)fields.second;
338 ms = (int)fields.millisecond;
339 wd = (int)fields.weekday;
340 tz = (int)fields.tz_minutes;
341 pos = 0;
342
343 if (spec.part & DATE_STRING_PART_DATE) {
344 switch (fmt) {
345 case 0:
346 pos += snprintf(
347 buf + pos, sizeof(buf) - (size_t)pos,
348 "%.3s, %02d %.3s %0*d ",
349 day_names + wd * 3, d, month_names + mon * 3, 4 + (y < 0), y);
350 break;
351 case 1:
352 pos += snprintf(
353 buf + pos, sizeof(buf) - (size_t)pos,
354 "%.3s %.3s %02d %0*d",
355 day_names + wd * 3, month_names + mon * 3, d, 4 + (y < 0), y);
356 if (spec.part == DATE_STRING_PART_ALL) buf[pos++] = ' ';
357 break;
358 case 2:
359 if (y >= 0 && y <= 9999) {
360 pos += snprintf(buf + pos, sizeof(buf) - (size_t)pos, "%04d", y);
361 } else pos += snprintf(buf + pos, sizeof(buf) - (size_t)pos, "%+07d", y);
362 pos += snprintf(buf + pos, sizeof(buf) - (size_t)pos, "-%02d-%02dT", mon + 1, d);
363 break;
364 case 3:
365 pos += snprintf(
366 buf + pos, sizeof(buf) - (size_t)pos,
367 "%02d/%02d/%0*d", mon + 1, d, 4 + (y < 0), y);
368 if (spec.part == DATE_STRING_PART_ALL) {
369 buf[pos++] = ',';
370 buf[pos++] = ' ';
371 }
372 break;
373 default: break;
374 }
375 }
376
377 if (spec.part & DATE_STRING_PART_TIME) {
378 switch (fmt) {
379 case 0:
380 pos += snprintf(buf + pos, sizeof(buf) - (size_t)pos, "%02d:%02d:%02d GMT", h, m, s);
381 break;
382 case 1:
383 pos += snprintf(buf + pos, sizeof(buf) - (size_t)pos, "%02d:%02d:%02d GMT", h, m, s);
384 if (tz < 0) {
385 buf[pos++] = '-';
386 tz = -tz;
387 } else buf[pos++] = '+';
388 pos += snprintf(buf + pos, sizeof(buf) - (size_t)pos, "%02d%02d", tz / 60, tz % 60);
389 break;
390 case 2:
391 pos += snprintf(buf + pos, sizeof(buf) - (size_t)pos, "%02d:%02d:%02d.%03dZ", h, m, s, ms);
392 break;
393 case 3:
394 pos += snprintf(
395 buf + pos, sizeof(buf) - (size_t)pos,
396 "%02d:%02d:%02d %cM", (h + 11) % 12 + 1, m, s, (h < 12) ? 'A' : 'P');
397 break;
398 default: break;
399 }
400 }
401
402 if (pos <= 0) return js_mkstr(js, "", 0);
403 return js_mkstr(js, buf, (size_t)pos);
404}
405
406static int64_t date_now(void) {
407 struct timeval tv;
408 gettimeofday(&tv, NULL);
409 return (int64_t)tv.tv_sec * 1000 + (int64_t)(tv.tv_usec / 1000);
410}
411
412static bool string_skip_char(const uint8_t *sp, int *pp, int c) {
413 if (sp[*pp] == c) { *pp += 1; return true; }
414 return false;
415}
416
417static int string_skip_spaces(const uint8_t *sp, int *pp) {
418 int c;
419 while ((c = sp[*pp]) == ' ') *pp += 1;
420 return c;
421}
422
423static int string_skip_separators(const uint8_t *sp, int *pp) {
424 int c;
425 while ((c = sp[*pp]) == '-' || c == '/' || c == '.' || c == ',') *pp += 1;
426 return c;
427}
428
429static int string_skip_until(const uint8_t *sp, int *pp, const char *stoplist) {
430 int c;
431 while (!strchr(stoplist, c = sp[*pp])) *pp += 1;
432 return c;
433}
434
435static bool string_get_digits(const uint8_t *sp, int *pp, int *pval, int min_digits, int max_digits) {
436 int v = 0; int c;
437 int p = *pp;
438 int p_start = p;
439
440 while ((c = sp[p]) >= '0' && c <= '9') {
441 if (v >= 100000000) return false;
442 v = v * 10 + c - '0';
443 p++;
444 if (max_digits > 0 && p - p_start == max_digits) break;
445 }
446
447 if (p - p_start < min_digits) return false;
448 *pval = v;
449 *pp = p;
450
451 return true;
452}
453
454static bool string_get_milliseconds(const uint8_t *sp, int *pp, int *pval) {
455 int mul = 100; int ms = 0;
456 int c; int p_start; int p = *pp;
457
458 c = sp[p];
459 if (c == '.' || c == ',') {
460 p++;
461 p_start = p;
462
463 while ((c = sp[p]) >= '0' && c <= '9') {
464 ms += (c - '0') * mul;
465 mul /= 10;
466 p++;
467 if (p - p_start == 9) break;
468 }
469
470 if (p > p_start) {
471 *pval = ms;
472 *pp = p;
473 }
474 }
475
476 return true;
477}
478
479static bool string_get_tzoffset(const uint8_t *sp, int *pp, int *tzp, bool strict) {
480 int tz = 0; int p = *pp;
481 int sgn; int hh; int mm;
482
483 sgn = sp[p++];
484 if (sgn == '+' || sgn == '-') {
485 int n = p;
486 if (!string_get_digits(sp, &p, &hh, 1, 0)) return false;
487 n = p - n;
488 if (strict && n != 2 && n != 4) return false;
489
490 while (n > 4) {
491 n -= 2;
492 hh /= 100;
493 }
494
495 if (n > 2) {
496 mm = hh % 100;
497 hh = hh / 100;
498 } else {
499 mm = 0;
500 if (string_skip_char(sp, &p, ':') && !string_get_digits(sp, &p, &mm, 2, 2)) return false;
501 }
502
503 if (hh > 23 || mm > 59) return false;
504
505 tz = hh * 60 + mm;
506 if (sgn != '+') tz = -tz;
507 } else if (sgn != 'Z') return false;
508
509 *pp = p;
510 *tzp = tz;
511
512 return true;
513}
514
515static bool string_match(const uint8_t *sp, int *pp, const char *s) {
516 int p = *pp;
517
518 while (*s != '\0') {
519 if (to_upper_ascii(sp[p]) != to_upper_ascii(*s++)) return false;
520 p++;
521 }
522
523 *pp = p;
524 return true;
525}
526
527static int find_abbrev(const uint8_t *sp, int p, const char *list, int count) {
528 int n; int i;
529 for (n = 0; n < count; n++) {
530 for (i = 0;; i++) {
531 if (to_upper_ascii(sp[p + i]) != to_upper_ascii(list[n * 3 + i])) break;
532 if (i == 2) return n;
533 }
534 }
535 return -1;
536}
537
538static bool string_get_month(const uint8_t *sp, int *pp, int *pval) {
539 int n = find_abbrev(sp, *pp, month_names, 12);
540 if (n < 0) return false;
541 *pval = n + 1;
542 *pp += 3;
543 return true;
544}
545
546static bool parse_isostring(const uint8_t *sp, int fields[9], bool *is_local) {
547 int sgn;
548 int i;
549 int p = 0;
550
551 for (i = 0; i < 9; i++) fields[i] = (i == 2);
552 *is_local = false;
553
554 sgn = sp[p];
555 if (sgn == '-' || sgn == '+') {
556 p++;
557 if (!string_get_digits(sp, &p, &fields[0], 6, 6)) return false;
558 if (sgn == '-') {
559 if (fields[0] == 0) return false;
560 fields[0] = -fields[0];
561 }
562 } else {
563 if (!string_get_digits(sp, &p, &fields[0], 4, 4)) return false;
564 }
565
566 if (string_skip_char(sp, &p, '-')) {
567 if (!string_get_digits(sp, &p, &fields[1], 2, 2)) return false;
568 if (fields[1] < 1) return false;
569 fields[1] -= 1;
570
571 if (string_skip_char(sp, &p, '-')) {
572 if (!string_get_digits(sp, &p, &fields[2], 2, 2)) return false;
573 if (fields[2] < 1) return false;
574 }
575 }
576
577 if (string_skip_char(sp, &p, 'T')) {
578 *is_local = true;
579 if (!string_get_digits(sp, &p, &fields[3], 2, 2)
580 || !string_skip_char(sp, &p, ':')
581 || !string_get_digits(sp, &p, &fields[4], 2, 2)) {
582 fields[3] = 100;
583 return true;
584 }
585
586 if (string_skip_char(sp, &p, ':')) {
587 if (!string_get_digits(sp, &p, &fields[5], 2, 2)) return false;
588 string_get_milliseconds(sp, &p, &fields[6]);
589 }
590 }
591
592 if (sp[p]) {
593 *is_local = false;
594 if (!string_get_tzoffset(sp, &p, &fields[8], true)) return false;
595 }
596
597 return sp[p] == '\0';
598}
599
600typedef struct {
601 char name[6];
602 int16_t offset;
603} tzabbr_t;
604
605static const tzabbr_t js_tzabbr[] = {
606 {"GMT", 0}, {"UTC", 0}, {"UT", 0}, {"Z", 0},
607 {"EDT", -4 * 60}, {"EST", -5 * 60},
608 {"CDT", -5 * 60}, {"CST", -6 * 60},
609 {"MDT", -6 * 60}, {"MST", -7 * 60},
610 {"PDT", -7 * 60}, {"PST", -8 * 60},
611 {"WET", 0}, {"WEST", +1 * 60},
612 {"CET", +1 * 60}, {"CEST", +2 * 60},
613 {"EET", +2 * 60}, {"EEST", +3 * 60},
614};
615
616static bool string_get_tzabbr(const uint8_t *sp, int *pp, int *offset) {
617 for (int i = 0; i < DATE_COUNT_OF(js_tzabbr); i++) {
618 if (string_match(sp, pp, js_tzabbr[i].name)) {
619 *offset = js_tzabbr[i].offset;
620 return true;
621 }
622 }
623 return false;
624}
625
626static bool parse_otherstring(const uint8_t *sp, int fields[9], bool *is_local) {
627 int c; int i; int val;
628 int p = 0;
629 int p_start;
630 int num[3];
631
632 bool has_year = false;
633 bool has_mon = false;
634 bool has_time = false;
635 int num_index = 0;
636
637 fields[0] = 2001;
638 fields[1] = 1;
639 fields[2] = 1;
640
641 for (i = 3; i < 9; i++) fields[i] = 0;
642 *is_local = true;
643
644 while (string_skip_spaces(sp, &p)) {
645 p_start = p;
646
647 if ((c = sp[p]) == '+' || c == '-') {
648 if (has_time && string_get_tzoffset(sp, &p, &fields[8], false)) {
649 *is_local = false;
650 } else {
651 p++;
652 if (string_get_digits(sp, &p, &val, 1, 0)) {
653 if (c == '-') {
654 if (val == 0) return false;
655 val = -val;
656 }
657 fields[0] = val;
658 has_year = true;
659 }
660 }
661 } else if (string_get_digits(sp, &p, &val, 1, 0)) {
662 if (string_skip_char(sp, &p, ':')) {
663 fields[3] = val;
664 if (!string_get_digits(sp, &p, &fields[4], 1, 2)) return false;
665
666 if (string_skip_char(sp, &p, ':')) {
667 if (!string_get_digits(sp, &p, &fields[5], 1, 2)) return false;
668 string_get_milliseconds(sp, &p, &fields[6]);
669 } else if (sp[p] != '\0' && sp[p] != ' ') return false;
670
671 has_time = true;
672 } else {
673 if (p - p_start > 2) {
674 fields[0] = val;
675 has_year = true;
676 } else if (val < 1 || val > 31) {
677 fields[0] = val + (val < 100) * 1900 + (val < 50) * 100;
678 has_year = true;
679 } else {
680 if (num_index == 3) return false;
681 num[num_index++] = val;
682 }
683 }
684 } else if (string_get_month(sp, &p, &fields[1])) {
685 has_mon = true;
686 string_skip_until(sp, &p, "0123456789 -/(");
687 } else if (has_time && string_match(sp, &p, "PM")) {
688 if (fields[3] != 12) fields[3] += 12;
689 continue;
690 } else if (has_time && string_match(sp, &p, "AM")) {
691 if (fields[3] > 12) return false;
692 if (fields[3] == 12) fields[3] -= 12;
693 continue;
694 } else if (string_get_tzabbr(sp, &p, &fields[8])) {
695 *is_local = false;
696 continue;
697 } else if (c == '(') {
698 int level = 0;
699 while ((c = sp[p]) != '\0') {
700 p++;
701 level += (c == '(');
702 level -= (c == ')');
703 if (!level) break;
704 }
705 if (level > 0) return false;
706 } else if (c == ')') {
707 return false;
708 } else {
709 if ((int)has_year + (int)has_mon + (int)has_time + num_index) return false;
710 string_skip_until(sp, &p, " -/(");
711 }
712
713 string_skip_separators(sp, &p);
714 }
715
716 if (num_index + (int)has_year + (int)has_mon > 3) return false;
717
718 switch (num_index) {
719 case 0:
720 if (!has_year) return false;
721 break;
722 case 1:
723 if (has_mon) fields[2] = num[0];
724 else fields[1] = num[0];
725 break;
726 case 2:
727 if (has_year) {
728 fields[1] = num[0];
729 fields[2] = num[1];
730 } else if (has_mon) {
731 fields[0] = num[1] + (num[1] < 100) * 1900 + (num[1] < 50) * 100;
732 fields[2] = num[0];
733 } else {
734 fields[1] = num[0];
735 fields[2] = num[1];
736 }
737 break;
738 case 3:
739 fields[0] = num[2] + (num[2] < 100) * 1900 + (num[2] < 50) * 100;
740 fields[1] = num[0];
741 fields[2] = num[1];
742 break;
743 default: return false;
744 }
745
746 if (fields[1] < 1 || fields[2] < 1) return false;
747 fields[1] -= 1;
748
749 return true;
750}
751
752static bool date_parse_string_to_ms(ant_t *js, ant_value_t arg, double *out_ms) {
753 ant_value_t s = coerce_to_str(js, arg);
754 if (is_err(s)) return false;
755
756 ant_offset_t len = 0;
757 ant_offset_t off = vstr(js, s, &len);
758
759 char *buf = (char *)malloc((size_t)len + 1);
760 if (!buf) {
761 *out_ms = JS_NAN;
762 return true;
763 }
764
765 memcpy(buf, (const void *)(uintptr_t)off, (size_t)len);
766 buf[len] = '\0';
767
768 int fields[9];
769 date_fields_t fields1 = {0};
770
771 bool is_local = false;
772 bool ok =
773 parse_isostring((const uint8_t *)buf, fields, &is_local) ||
774 parse_otherstring((const uint8_t *)buf, fields, &is_local);
775
776 free(buf);
777
778 if (!ok) {
779 *out_ms = JS_NAN;
780 return true;
781 }
782
783 static const int field_max[6] = {0, 11, 31, 24, 59, 59};
784 bool valid = true;
785
786 for (int i = 1; i < 6; i++) {
787 if (fields[i] > field_max[i]) valid = false;
788 }
789 if (fields[3] == 24 && (fields[4] | fields[5] | fields[6])) valid = false;
790
791 if (!valid) {
792 *out_ms = JS_NAN;
793 return true;
794 }
795
796 fields1.year = (double)fields[0];
797 fields1.month = (double)fields[1];
798 fields1.day = (double)fields[2];
799 fields1.hour = (double)fields[3];
800 fields1.minute = (double)fields[4];
801 fields1.second = (double)fields[5];
802 fields1.millisecond = (double)fields[6];
803 *out_ms = date_make_fields(&fields1, is_local ? 1 : 0) - (double)fields[8] * 60000.0;
804
805 return true;
806}
807
808static ant_value_t builtin_Date(ant_t *js, ant_value_t *args, int nargs) {
809 double val;
810 static const date_string_spec_t kDateToStringSpec = {
811 DATE_STRING_FMT_LOCAL,
812 DATE_STRING_PART_ALL
813 };
814
815 if (vtype(js->new_target) == T_UNDEF) {
816 val = (double)date_now();
817
818 ant_value_t tmp = js_mkobj(js);
819 ant_value_t date_proto = js_get_ctor_proto(js, "Date", 4);
820 if (is_object_type(date_proto)) js_set_proto_init(tmp, date_proto);
821
822 js_set_slot(tmp, SLOT_DATA, tov(val));
823 js_set_slot(tmp, SLOT_BRAND, js_mknum(BRAND_DATE));
824
825 return get_date_string(js, tmp, kDateToStringSpec);
826 }
827
828 if (nargs == 0) {
829 val = (double)date_now();
830 } else if (nargs == 1) {
831 ant_value_t input = args[0];
832
833 if (is_object_type(input) && is_date_instance(input)) {
834 ant_value_t tv = js_get_slot(js_as_obj(input), SLOT_DATA);
835 val = (vtype(tv) == T_NUM) ? tod(tv) : JS_NAN;
836 val = date_timeclip(val);
837 } else {
838 ant_value_t prim = js_to_primitive(js, input, 0);
839 if (is_err(prim)) return prim;
840 if (vtype(prim) == T_STR) {
841 if (!date_parse_string_to_ms(js, prim, &val)) return js_mkerr_typed(js, JS_ERR_INTERNAL, "Date parse failed");
842 } else val = js_to_number(js, prim);
843 val = date_timeclip(val);
844 }
845 } else {
846 date_fields_t fields = {0};
847 fields.day = 1;
848 int n = nargs;
849 if (n > 7) n = 7;
850
851 int i;
852 for (i = 0; i < n; i++) {
853 double a = js_to_number(js, args[i]);
854 if (!isfinite(a)) break;
855 double t = trunc(a);
856 date_fields_set(&fields, (date_field_index_t)i, t);
857 if (i == 0 && fields.year >= 0 && fields.year < 100) fields.year += 1900;
858 }
859
860 val = (i == n) ? date_make_fields(&fields, 1) : JS_NAN;
861 }
862
863 js_set_slot(js_as_obj(js->this_val), SLOT_DATA, tov(val));
864 js_set_slot(js_as_obj(js->this_val), SLOT_BRAND, js_mknum(BRAND_DATE));
865
866 return js->this_val;
867}
868
869static ant_value_t builtin_Date_UTC(ant_t *js, ant_value_t *args, int nargs) {
870 if (nargs == 0) return tov(JS_NAN);
871
872 date_fields_t fields = {0};
873 fields.day = 1;
874 int n = nargs;
875 if (n > 7) n = 7;
876
877 for (int i = 0; i < n; i++) {
878 double a = js_to_number(js, args[i]);
879 if (!isfinite(a)) return tov(JS_NAN);
880 double t = trunc(a);
881 date_fields_set(&fields, (date_field_index_t)i, t);
882 if (i == 0 && fields.year >= 0 && fields.year < 100) fields.year += 1900;
883 }
884
885 return tov(date_make_fields(&fields, 0));
886}
887
888static ant_value_t builtin_Date_parse(ant_t *js, ant_value_t *args, int nargs) {
889 if (nargs < 1) return tov(JS_NAN);
890 double v;
891 if (!date_parse_string_to_ms(js, args[0], &v)) {
892 return js_mkerr_typed(js, JS_ERR_INTERNAL, "Date parse failed");
893 }
894 return tov(v);
895}
896
897static ant_value_t builtin_Date_now(ant_t *js, ant_value_t *args, int nargs) {
898 return tov((double)date_now());
899}
900
901static ant_value_t date_get_field(ant_t *js, date_get_field_spec_t spec) {
902 date_fields_t fields;
903 ant_value_t err;
904
905 int res = date_extract_fields(js, js->this_val, &fields, spec.local_time, 0, &err);
906 if (res < 0) return err;
907 if (!res) return tov(JS_NAN);
908
909 if (spec.legacy_get_year) fields.year -= 1900;
910 return tov(date_fields_get(&fields, spec.field));
911}
912
913static ant_value_t date_set_field(ant_t *js, ant_value_t *args, int nargs, date_set_field_spec_t spec) {
914 date_fields_t fields;
915 ant_value_t err;
916 double d = JS_NAN;
917
918 int res = date_extract_fields(
919 js, js->this_val,
920 &fields, spec.local_time,
921 spec.first_field == DATE_FIELD_YEAR, &err
922 );
923
924 if (res < 0) return err;
925 int n = date_min_int(nargs, spec.end_field_exclusive - spec.first_field);
926
927 for (int i = 0; i < n; i++) {
928 double a = js_to_number(js, args[i]);
929 if (!isfinite(a)) return date_set_this_time_value(js, js->this_val, JS_NAN);
930 date_fields_set(&fields, (date_field_index_t)(spec.first_field + i), trunc(a));
931 }
932
933 if (!res) return tov(JS_NAN);
934 if (nargs > 0) d = date_make_fields(&fields, spec.local_time);
935
936 return date_set_this_time_value(js, js->this_val, d);
937}
938
939static ant_value_t builtin_Date_valueOf(ant_t *js, ant_value_t *args, int nargs) {
940 double v;
941 ant_value_t err;
942 if (!date_this_time_value(js, js->this_val, &v, &err)) return err;
943 return tov(v);
944}
945
946static ant_value_t builtin_Date_getTime(ant_t *js, ant_value_t *args, int nargs) {
947 return builtin_Date_valueOf(js, args, nargs);
948}
949
950static ant_value_t builtin_Date_toUTCString(ant_t *js, ant_value_t *args, int nargs) {
951 static const date_string_spec_t kSpec = {DATE_STRING_FMT_UTC, DATE_STRING_PART_ALL};
952 return get_date_string(js, js->this_val, kSpec);
953}
954
955static ant_value_t builtin_Date_toString(ant_t *js, ant_value_t *args, int nargs) {
956 static const date_string_spec_t kSpec = {DATE_STRING_FMT_LOCAL, DATE_STRING_PART_ALL};
957 return get_date_string(js, js->this_val, kSpec);
958}
959
960static ant_value_t builtin_Date_toISOString(ant_t *js, ant_value_t *args, int nargs) {
961 static const date_string_spec_t kSpec = {DATE_STRING_FMT_ISO, DATE_STRING_PART_ALL};
962 return get_date_string(js, js->this_val, kSpec);
963}
964
965static ant_value_t builtin_Date_toDateString(ant_t *js, ant_value_t *args, int nargs) {
966 static const date_string_spec_t kSpec = {DATE_STRING_FMT_LOCAL, DATE_STRING_PART_DATE};
967 return get_date_string(js, js->this_val, kSpec);
968}
969
970static ant_value_t builtin_Date_toTimeString(ant_t *js, ant_value_t *args, int nargs) {
971 static const date_string_spec_t kSpec = {DATE_STRING_FMT_LOCAL, DATE_STRING_PART_TIME};
972 return get_date_string(js, js->this_val, kSpec);
973}
974
975static ant_value_t builtin_Date_toLocaleString(ant_t *js, ant_value_t *args, int nargs) {
976 static const date_string_spec_t kSpec = {DATE_STRING_FMT_LOCALE, DATE_STRING_PART_ALL};
977 return get_date_string(js, js->this_val, kSpec);
978}
979
980static ant_value_t builtin_Date_toLocaleDateString(ant_t *js, ant_value_t *args, int nargs) {
981 static const date_string_spec_t kSpec = {DATE_STRING_FMT_LOCALE, DATE_STRING_PART_DATE};
982 return get_date_string(js, js->this_val, kSpec);
983}
984
985static ant_value_t builtin_Date_toLocaleTimeString(ant_t *js, ant_value_t *args, int nargs) {
986 static const date_string_spec_t kSpec = {DATE_STRING_FMT_LOCALE, DATE_STRING_PART_TIME};
987 return get_date_string(js, js->this_val, kSpec);
988}
989
990static ant_value_t builtin_Date_getTimezoneOffset(ant_t *js, ant_value_t *args, int nargs) {
991 double v;
992 ant_value_t err;
993 if (!date_this_time_value(js, js->this_val, &v, &err)) return err;
994 if (isnan(v)) return tov(JS_NAN);
995 return tov((double)get_timezone_offset((int64_t)trunc(v)));
996}
997
998static ant_value_t builtin_Date_setTime(ant_t *js, ant_value_t *args, int nargs) {
999 double cur;
1000 ant_value_t err;
1001 if (!date_this_time_value(js, js->this_val, &cur, &err)) return err;
1002
1003 double v = (nargs > 0) ? js_to_number(js, args[0]) : JS_NAN;
1004 return date_set_this_time_value(js, js->this_val, date_timeclip(v));
1005}
1006
1007static ant_value_t builtin_Date_getYear(ant_t *js, ant_value_t *args, int nargs) {
1008 static const date_get_field_spec_t kSpec = {DATE_FIELD_YEAR, true, true};
1009 return date_get_field(js, kSpec);
1010}
1011
1012static ant_value_t builtin_Date_getFullYear(ant_t *js, ant_value_t *args, int nargs) {
1013 static const date_get_field_spec_t kSpec = {DATE_FIELD_YEAR, true, false};
1014 return date_get_field(js, kSpec);
1015}
1016
1017static ant_value_t builtin_Date_getUTCFullYear(ant_t *js, ant_value_t *args, int nargs) {
1018 static const date_get_field_spec_t kSpec = {DATE_FIELD_YEAR, false, false};
1019 return date_get_field(js, kSpec);
1020}
1021
1022static ant_value_t builtin_Date_getMonth(ant_t *js, ant_value_t *args, int nargs) {
1023 static const date_get_field_spec_t kSpec = {DATE_FIELD_MONTH, true, false};
1024 return date_get_field(js, kSpec);
1025}
1026
1027static ant_value_t builtin_Date_getUTCMonth(ant_t *js, ant_value_t *args, int nargs) {
1028 static const date_get_field_spec_t kSpec = {DATE_FIELD_MONTH, false, false};
1029 return date_get_field(js, kSpec);
1030}
1031
1032static ant_value_t builtin_Date_getDate(ant_t *js, ant_value_t *args, int nargs) {
1033 static const date_get_field_spec_t kSpec = {DATE_FIELD_DAY_OF_MONTH, true, false};
1034 return date_get_field(js, kSpec);
1035}
1036
1037static ant_value_t builtin_Date_getUTCDate(ant_t *js, ant_value_t *args, int nargs) {
1038 static const date_get_field_spec_t kSpec = {DATE_FIELD_DAY_OF_MONTH, false, false};
1039 return date_get_field(js, kSpec);
1040}
1041
1042static ant_value_t builtin_Date_getHours(ant_t *js, ant_value_t *args, int nargs) {
1043 static const date_get_field_spec_t kSpec = {DATE_FIELD_HOUR, true, false};
1044 return date_get_field(js, kSpec);
1045}
1046
1047static ant_value_t builtin_Date_getUTCHours(ant_t *js, ant_value_t *args, int nargs) {
1048 static const date_get_field_spec_t kSpec = {DATE_FIELD_HOUR, false, false};
1049 return date_get_field(js, kSpec);
1050}
1051
1052static ant_value_t builtin_Date_getMinutes(ant_t *js, ant_value_t *args, int nargs) {
1053 static const date_get_field_spec_t kSpec = {DATE_FIELD_MINUTE, true, false};
1054 return date_get_field(js, kSpec);
1055}
1056
1057static ant_value_t builtin_Date_getUTCMinutes(ant_t *js, ant_value_t *args, int nargs) {
1058 static const date_get_field_spec_t kSpec = {DATE_FIELD_MINUTE, false, false};
1059 return date_get_field(js, kSpec);
1060}
1061
1062static ant_value_t builtin_Date_getSeconds(ant_t *js, ant_value_t *args, int nargs) {
1063 static const date_get_field_spec_t kSpec = {DATE_FIELD_SECOND, true, false};
1064 return date_get_field(js, kSpec);
1065}
1066
1067static ant_value_t builtin_Date_getUTCSeconds(ant_t *js, ant_value_t *args, int nargs) {
1068 static const date_get_field_spec_t kSpec = {DATE_FIELD_SECOND, false, false};
1069 return date_get_field(js, kSpec);
1070}
1071
1072static ant_value_t builtin_Date_getMilliseconds(ant_t *js, ant_value_t *args, int nargs) {
1073 static const date_get_field_spec_t kSpec = {DATE_FIELD_MILLISECOND, true, false};
1074 return date_get_field(js, kSpec);
1075}
1076
1077static ant_value_t builtin_Date_getUTCMilliseconds(ant_t *js, ant_value_t *args, int nargs) {
1078 static const date_get_field_spec_t kSpec = {DATE_FIELD_MILLISECOND, false, false};
1079 return date_get_field(js, kSpec);
1080}
1081
1082static ant_value_t builtin_Date_getDay(ant_t *js, ant_value_t *args, int nargs) {
1083 static const date_get_field_spec_t kSpec = {DATE_FIELD_DAY_OF_WEEK, true, false};
1084 return date_get_field(js, kSpec);
1085}
1086
1087static ant_value_t builtin_Date_getUTCDay(ant_t *js, ant_value_t *args, int nargs) {
1088 static const date_get_field_spec_t kSpec = {DATE_FIELD_DAY_OF_WEEK, false, false};
1089 return date_get_field(js, kSpec);
1090}
1091
1092static ant_value_t builtin_Date_setMilliseconds(ant_t *js, ant_value_t *args, int nargs) {
1093 static const date_set_field_spec_t kSpec = {DATE_FIELD_MILLISECOND, DATE_FIELD_DAY_OF_WEEK, true};
1094 return date_set_field(js, args, nargs, kSpec);
1095}
1096
1097static ant_value_t builtin_Date_setUTCMilliseconds(ant_t *js, ant_value_t *args, int nargs) {
1098 static const date_set_field_spec_t kSpec = {DATE_FIELD_MILLISECOND, DATE_FIELD_DAY_OF_WEEK, false};
1099 return date_set_field(js, args, nargs, kSpec);
1100}
1101
1102static ant_value_t builtin_Date_setSeconds(ant_t *js, ant_value_t *args, int nargs) {
1103 static const date_set_field_spec_t kSpec = {DATE_FIELD_SECOND, DATE_FIELD_DAY_OF_WEEK, true};
1104 return date_set_field(js, args, nargs, kSpec);
1105}
1106
1107static ant_value_t builtin_Date_setUTCSeconds(ant_t *js, ant_value_t *args, int nargs) {
1108 static const date_set_field_spec_t kSpec = {DATE_FIELD_SECOND, DATE_FIELD_DAY_OF_WEEK, false};
1109 return date_set_field(js, args, nargs, kSpec);
1110}
1111
1112static ant_value_t builtin_Date_setMinutes(ant_t *js, ant_value_t *args, int nargs) {
1113 static const date_set_field_spec_t kSpec = {DATE_FIELD_MINUTE, DATE_FIELD_DAY_OF_WEEK, true};
1114 return date_set_field(js, args, nargs, kSpec);
1115}
1116
1117static ant_value_t builtin_Date_setUTCMinutes(ant_t *js, ant_value_t *args, int nargs) {
1118 static const date_set_field_spec_t kSpec = {DATE_FIELD_MINUTE, DATE_FIELD_DAY_OF_WEEK, false};
1119 return date_set_field(js, args, nargs, kSpec);
1120}
1121
1122static ant_value_t builtin_Date_setHours(ant_t *js, ant_value_t *args, int nargs) {
1123 static const date_set_field_spec_t kSpec = {DATE_FIELD_HOUR, DATE_FIELD_DAY_OF_WEEK, true};
1124 return date_set_field(js, args, nargs, kSpec);
1125}
1126
1127static ant_value_t builtin_Date_setUTCHours(ant_t *js, ant_value_t *args, int nargs) {
1128 static const date_set_field_spec_t kSpec = {DATE_FIELD_HOUR, DATE_FIELD_DAY_OF_WEEK, false};
1129 return date_set_field(js, args, nargs, kSpec);
1130}
1131
1132static ant_value_t builtin_Date_setDate(ant_t *js, ant_value_t *args, int nargs) {
1133 static const date_set_field_spec_t kSpec = {DATE_FIELD_DAY_OF_MONTH, DATE_FIELD_HOUR, true};
1134 return date_set_field(js, args, nargs, kSpec);
1135}
1136
1137static ant_value_t builtin_Date_setUTCDate(ant_t *js, ant_value_t *args, int nargs) {
1138 static const date_set_field_spec_t kSpec = {DATE_FIELD_DAY_OF_MONTH, DATE_FIELD_HOUR, false};
1139 return date_set_field(js, args, nargs, kSpec);
1140}
1141
1142static ant_value_t builtin_Date_setMonth(ant_t *js, ant_value_t *args, int nargs) {
1143 static const date_set_field_spec_t kSpec = {DATE_FIELD_MONTH, DATE_FIELD_DAY_OF_MONTH, true};
1144 return date_set_field(js, args, nargs, kSpec);
1145}
1146
1147static ant_value_t builtin_Date_setUTCMonth(ant_t *js, ant_value_t *args, int nargs) {
1148 static const date_set_field_spec_t kSpec = {DATE_FIELD_MONTH, DATE_FIELD_DAY_OF_MONTH, false};
1149 return date_set_field(js, args, nargs, kSpec);
1150}
1151
1152static ant_value_t builtin_Date_setFullYear(ant_t *js, ant_value_t *args, int nargs) {
1153 static const date_set_field_spec_t kSpec = {DATE_FIELD_YEAR, DATE_FIELD_HOUR, true};
1154 return date_set_field(js, args, nargs, kSpec);
1155}
1156
1157static ant_value_t builtin_Date_setUTCFullYear(ant_t *js, ant_value_t *args, int nargs) {
1158 static const date_set_field_spec_t kSpec = {DATE_FIELD_YEAR, DATE_FIELD_HOUR, false};
1159 return date_set_field(js, args, nargs, kSpec);
1160}
1161
1162static ant_value_t builtin_Date_setYear(ant_t *js, ant_value_t *args, int nargs) {
1163 double y;
1164 ant_value_t err;
1165 if (!date_this_time_value(js, js->this_val, &y, &err)) return err;
1166
1167 y = (nargs > 0) ? js_to_number(js, args[0]) : JS_NAN;
1168 if (isnan(y)) return date_set_this_time_value(js, js->this_val, y);
1169
1170 if (isfinite(y)) {
1171 y = trunc(y);
1172 if (y >= 0 && y < 100) y += 1900;
1173 }
1174
1175 ant_value_t darg = tov(y);
1176 static const date_set_field_spec_t kSetYearSpec = {
1177 DATE_FIELD_YEAR, DATE_FIELD_MONTH, true
1178 };
1179 return date_set_field(js, &darg, 1, kSetYearSpec);
1180}
1181
1182static ant_value_t date_to_primitive_number(ant_t *js, ant_value_t obj) {
1183 ant_value_t to_primitive_sym = get_toPrimitive_sym();
1184 if (vtype(to_primitive_sym) == T_SYMBOL) {
1185 ant_value_t ex = js_get_sym(js, obj, to_primitive_sym);
1186 uint8_t et = vtype(ex);
1187 if (et == T_FUNC || et == T_CFUNC) {
1188 ant_value_t hint = js_mkstr(js, "number", 6);
1189 ant_value_t result = sv_vm_call(js->vm, js, ex, obj, &hint, 1, NULL, false);
1190 if (is_err(result)) return result;
1191 if (date_is_primitive(result)) return result;
1192 return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot convert object to primitive value");
1193 }
1194 if (et != T_UNDEF) {
1195 return js_mkerr_typed(js, JS_ERR_TYPE, "Symbol.toPrimitive is not a function");
1196 }
1197 }
1198
1199 const char *methods[2] = {"valueOf", "toString"};
1200 for (int i = 0; i < 2; i++) {
1201 ant_value_t method = js_getprop_fallback(js, obj, methods[i]);
1202 uint8_t mt = vtype(method);
1203 if (mt == T_FUNC || mt == T_CFUNC) {
1204 ant_value_t result = sv_vm_call(js->vm, js, method, obj, NULL, 0, NULL, false);
1205 if (is_err(result)) return result;
1206 if (date_is_primitive(result)) return result;
1207 }
1208 }
1209
1210 return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot convert object to primitive value");
1211}
1212
1213static ant_value_t builtin_Date_toJSON(ant_t *js, ant_value_t *args, int nargs) {
1214 ant_value_t obj = js->this_val;
1215 if (!is_object_type(obj)) {
1216 return js_mkerr_typed(js, JS_ERR_TYPE, "Date.prototype.toJSON called on non-object");
1217 }
1218
1219 ant_value_t tv = date_to_primitive_number(js, obj);
1220 if (is_err(tv)) return tv;
1221
1222 if (vtype(tv) == T_NUM && !isfinite(tod(tv))) {
1223 return js_mknull();
1224 }
1225
1226 ant_value_t method = js_getprop_fallback(js, obj, "toISOString");
1227 uint8_t mt = vtype(method);
1228 if (mt != T_FUNC && mt != T_CFUNC) {
1229 return js_mkerr_typed(js, JS_ERR_TYPE, "object needs toISOString method");
1230 }
1231
1232 return sv_vm_call(js->vm, js, method, obj, NULL, 0, NULL, false);
1233}
1234
1235static ant_value_t date_toPrimitive(ant_t *js, ant_value_t *args, int nargs) {
1236 ant_value_t this_val = js_getthis(js);
1237 if (!is_object_type(this_val)) {
1238 return js_mkerr_typed(js, JS_ERR_TYPE, "Not an object");
1239 }
1240
1241 int hint_num;
1242 if (nargs > 0 && vtype(args[0]) == T_STR) {
1243 size_t hint_len = 0;
1244 const char *hint = js_getstr(js, args[0], &hint_len);
1245 if (!hint) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid hint");
1246
1247 if ((hint_len == 6 && memcmp(hint, "number", 6) == 0)
1248 || (hint_len == 7 && memcmp(hint, "integer", 7) == 0)) {
1249 hint_num = 1;
1250 } else if (
1251 (hint_len == 6 && memcmp(hint, "string", 6) == 0)
1252 || (hint_len == 7 && memcmp(hint, "default", 7) == 0)) {
1253 hint_num = 0;
1254 } else {
1255 return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid hint");
1256 }
1257 } else return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid hint");
1258
1259 const char *methods0[2] = {"toString", "valueOf"};
1260 const char *methods1[2] = {"valueOf", "toString"};
1261 const char **methods = hint_num ? methods1 : methods0;
1262
1263 for (int i = 0; i < 2; i++) {
1264 ant_value_t method = js_getprop_fallback(js, this_val, methods[i]);
1265 uint8_t mt = vtype(method);
1266 if (mt == T_FUNC || mt == T_CFUNC) {
1267 ant_value_t result = sv_vm_call(js->vm, js, method, this_val, NULL, 0, NULL, false);
1268 if (is_err(result)) return result;
1269 if (date_is_primitive(result)) return result;
1270 }
1271 }
1272
1273 return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot convert object to primitive value");
1274}
1275
1276static void date_define_methods(
1277 ant_t *js, ant_value_t target,
1278 const date_method_entry_t *methods,
1279 size_t count
1280) {
1281 for (size_t i = 0; i < count; i++) js_setprop(js,target,
1282 js_mkstr(js, methods[i].name, methods[i].len),
1283 js_mkfun_dyn(methods[i].fn)
1284 );
1285}
1286
1287void init_date_module(void) {
1288 ant_t *js = rt->js;
1289 ant_value_t glob = js->global;
1290 ant_value_t object_proto = js->sym.object_proto;
1291
1292 ant_value_t function_proto = js_get_ctor_proto(js, "Function", 8);
1293 ant_value_t date_proto = js_mkobj(js);
1294
1295 js_set_proto_init(date_proto, object_proto);
1296
1297 static const date_method_entry_t kDateProtoMethods[] = {
1298 DATE_METHOD_ENTRY("valueOf", builtin_Date_valueOf),
1299 DATE_METHOD_ENTRY("toString", builtin_Date_toString),
1300 DATE_METHOD_ENTRY("toUTCString", builtin_Date_toUTCString),
1301 DATE_METHOD_ENTRY("toGMTString", builtin_Date_toUTCString),
1302 DATE_METHOD_ENTRY("toISOString", builtin_Date_toISOString),
1303 DATE_METHOD_ENTRY("toDateString", builtin_Date_toDateString),
1304 DATE_METHOD_ENTRY("toTimeString", builtin_Date_toTimeString),
1305 DATE_METHOD_ENTRY("toLocaleString", builtin_Date_toLocaleString),
1306 DATE_METHOD_ENTRY("toLocaleDateString", builtin_Date_toLocaleDateString),
1307 DATE_METHOD_ENTRY("toLocaleTimeString", builtin_Date_toLocaleTimeString),
1308 DATE_METHOD_ENTRY("getTimezoneOffset", builtin_Date_getTimezoneOffset),
1309 DATE_METHOD_ENTRY("getTime", builtin_Date_getTime),
1310 DATE_METHOD_ENTRY("getYear", builtin_Date_getYear),
1311 DATE_METHOD_ENTRY("getFullYear", builtin_Date_getFullYear),
1312 DATE_METHOD_ENTRY("getUTCFullYear", builtin_Date_getUTCFullYear),
1313 DATE_METHOD_ENTRY("getMonth", builtin_Date_getMonth),
1314 DATE_METHOD_ENTRY("getUTCMonth", builtin_Date_getUTCMonth),
1315 DATE_METHOD_ENTRY("getDate", builtin_Date_getDate),
1316 DATE_METHOD_ENTRY("getUTCDate", builtin_Date_getUTCDate),
1317 DATE_METHOD_ENTRY("getHours", builtin_Date_getHours),
1318 DATE_METHOD_ENTRY("getUTCHours", builtin_Date_getUTCHours),
1319 DATE_METHOD_ENTRY("getMinutes", builtin_Date_getMinutes),
1320 DATE_METHOD_ENTRY("getUTCMinutes", builtin_Date_getUTCMinutes),
1321 DATE_METHOD_ENTRY("getSeconds", builtin_Date_getSeconds),
1322 DATE_METHOD_ENTRY("getUTCSeconds", builtin_Date_getUTCSeconds),
1323 DATE_METHOD_ENTRY("getMilliseconds", builtin_Date_getMilliseconds),
1324 DATE_METHOD_ENTRY("getUTCMilliseconds", builtin_Date_getUTCMilliseconds),
1325 DATE_METHOD_ENTRY("getDay", builtin_Date_getDay),
1326 DATE_METHOD_ENTRY("getUTCDay", builtin_Date_getUTCDay),
1327 DATE_METHOD_ENTRY("setTime", builtin_Date_setTime),
1328 DATE_METHOD_ENTRY("setMilliseconds", builtin_Date_setMilliseconds),
1329 DATE_METHOD_ENTRY("setUTCMilliseconds", builtin_Date_setUTCMilliseconds),
1330 DATE_METHOD_ENTRY("setSeconds", builtin_Date_setSeconds),
1331 DATE_METHOD_ENTRY("setUTCSeconds", builtin_Date_setUTCSeconds),
1332 DATE_METHOD_ENTRY("setMinutes", builtin_Date_setMinutes),
1333 DATE_METHOD_ENTRY("setUTCMinutes", builtin_Date_setUTCMinutes),
1334 DATE_METHOD_ENTRY("setHours", builtin_Date_setHours),
1335 DATE_METHOD_ENTRY("setUTCHours", builtin_Date_setUTCHours),
1336 DATE_METHOD_ENTRY("setDate", builtin_Date_setDate),
1337 DATE_METHOD_ENTRY("setUTCDate", builtin_Date_setUTCDate),
1338 DATE_METHOD_ENTRY("setMonth", builtin_Date_setMonth),
1339 DATE_METHOD_ENTRY("setUTCMonth", builtin_Date_setUTCMonth),
1340 DATE_METHOD_ENTRY("setYear", builtin_Date_setYear),
1341 DATE_METHOD_ENTRY("setFullYear", builtin_Date_setFullYear),
1342 DATE_METHOD_ENTRY("setUTCFullYear", builtin_Date_setUTCFullYear),
1343 DATE_METHOD_ENTRY("toJSON", builtin_Date_toJSON),
1344 };
1345 date_define_methods(js, date_proto, kDateProtoMethods, DATE_COUNT_OF(kDateProtoMethods));
1346
1347 ant_value_t to_primitive_sym = get_toPrimitive_sym();
1348 if (vtype(to_primitive_sym) == T_SYMBOL) {
1349 js_set_sym(js, date_proto, to_primitive_sym, js_mkfun(date_toPrimitive));
1350 }
1351
1352 ant_value_t date_ctor_obj = js_mkobj(js);
1353 js_set_proto_init(date_ctor_obj, function_proto);
1354 js_set_slot(date_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_Date));
1355
1356 static const date_method_entry_t kDateCtorMethods[] = {
1357 DATE_METHOD_ENTRY("now", builtin_Date_now),
1358 DATE_METHOD_ENTRY("parse", builtin_Date_parse),
1359 DATE_METHOD_ENTRY("UTC", builtin_Date_UTC),
1360 };
1361
1362 date_define_methods(js, date_ctor_obj, kDateCtorMethods, DATE_COUNT_OF(kDateCtorMethods));
1363 js_setprop_nonconfigurable(js, date_ctor_obj, "prototype", 9, date_proto);
1364 js_setprop(js, date_ctor_obj, ANT_STRING("name"), ANT_STRING("Date"));
1365
1366 ant_value_t date_ctor_func = js_obj_to_func(date_ctor_obj);
1367 js_setprop(js, glob, js_mkstr(js, "Date", 4), date_ctor_func);
1368
1369 js_setprop(js, date_proto, js_mkstr(js, "constructor", 11), date_ctor_func);
1370 js_set_descriptor(js, date_proto, "constructor", 11, JS_DESC_W | JS_DESC_C);
1371}