about summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
authorCharlie Gordon <github@chqrlie.org>2024-02-21 21:22:10 +0100
committerbptato <nincsnevem662@gmail.com>2024-03-02 18:12:24 +0100
commit994ecade79fced9e21e2794f86b0a34858bd027c (patch)
tree7553e37b8a1b236224dbeb8fab8b5aedaa44d591 /lib
parent951fa118f79460c1aaa284d0a9e605a8d5780406 (diff)
downloadchawan-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.c80
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,