diff options
author | Charlie Gordon <github@chqrlie.org> | 2024-02-21 21:22:10 +0100 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2024-03-02 18:12:24 +0100 |
commit | 994ecade79fced9e21e2794f86b0a34858bd027c (patch) | |
tree | 7553e37b8a1b236224dbeb8fab8b5aedaa44d591 /lib | |
parent | 951fa118f79460c1aaa284d0a9e605a8d5780406 (diff) | |
download | chawan-994ecade79fced9e21e2794f86b0a34858bd027c.tar.gz |
Rewrite `set_date_fields` to match the ECMA specification
- use `double` arithmetic where necessary to match the spec - use `volatile` to ensure correct order of evaluation and prevent FMA code generation - reject some border cases. - avoid undefined behavior in `double` -> `int64_t` conversions - improved tests/test_builtin.js `assert` function to compare values more reliably. - added some tests in `test_date()` - disable some of these tests on win32 and cygwin targets
Diffstat (limited to 'lib')
-rw-r--r-- | lib/quickjs/quickjs.c | 80 |
1 files changed, 52 insertions, 28 deletions
diff --git a/lib/quickjs/quickjs.c b/lib/quickjs/quickjs.c index ae882138..4afbf78b 100644 --- a/lib/quickjs/quickjs.c +++ b/lib/quickjs/quickjs.c @@ -49560,39 +49560,63 @@ static double time_clip(double t) { return NAN; } -/* The spec mandates the use of 'double' and it fixes the order +/* The spec mandates the use of 'double' and it specifies the order of the operations */ static double set_date_fields(double fields[], int is_local) { - int64_t y; - double days, h, m1; - volatile double d; /* enforce evaluation order */ - int i, m, md; - - m1 = fields[1]; - m = fmod(m1, 12); - if (m < 0) - m += 12; - y = (int64_t)(fields[0] + floor(m1 / 12)); - days = days_from_year(y); - - for(i = 0; i < m; i++) { - md = month_days[i]; + double y, m, dt, ym, mn, day, h, s, milli, time, tv; + int yi, mi, i; + int64_t days; + volatile double temp; /* enforce evaluation order */ + + /* emulate 21.4.1.15 MakeDay ( year, month, date ) */ + y = fields[0]; + m = fields[1]; + dt = fields[2]; + ym = y + floor(m / 12); + mn = fmod(m, 12); + if (mn < 0) + mn += 12; + if (ym < -271821 || ym > 275760) + return NAN; + + yi = ym; + mi = mn; + days = days_from_year(yi); + for(i = 0; i < mi; i++) { + days += month_days[i]; if (i == 1) - md += days_in_year(y) - 365; - days += md; + days += days_in_year(yi) - 365; } - days += fields[2] - 1; - /* made d volatile to ensure order of evaluation as specified in ECMA. - * this fixes a test262 error on - * test262/test/built-ins/Date/UTC/fp-evaluation-order.js + day = days + dt - 1; + + /* emulate 21.4.1.14 MakeTime ( hour, min, sec, ms ) */ + h = fields[3]; + m = fields[4]; + s = fields[5]; + milli = fields[6]; + /* Use a volatile intermediary variable to ensure order of evaluation + * as specified in ECMA. This fixes a test262 error on + * test262/test/built-ins/Date/UTC/fp-evaluation-order.js. + * Without the volatile qualifier, the compile can generate code + * that performs the computation in a different order or with instructions + * that produce a different result such as FMA (float multiply and add). */ - h = fields[3] * 3600000 + fields[4] * 60000 + - fields[5] * 1000 + fields[6]; - d = days * 86400000; - d = d + h; - if (is_local) - d += getTimezoneOffset(d) * 60000; - return time_clip(d); + time = h * 3600000; + time += (temp = m * 60000); + time += (temp = s * 1000); + time += milli; + + /* emulate 21.4.1.16 MakeDate ( day, time ) */ + tv = (temp = day * 86400000) + time; /* prevent generation of FMA */ + if (!isfinite(tv)) + return NAN; + + /* adjust for local time and clip */ + if (is_local) { + int64_t ti = tv < INT64_MIN ? INT64_MIN : tv >= 0x1p63 ? INT64_MAX : (int64_t)tv; + tv += getTimezoneOffset(ti) * 60000; + } + return time_clip(tv); } static JSValue get_date_field(JSContext *ctx, JSValueConst this_val, |