Bug 1160356 - Make new Date(arg1, arg2, ...) conform to ES3-6 in converting *all* arguments to number before computing the return value. r=evilpie
authorJeff Walden <jwalden@mit.edu>
Thu, 30 Apr 2015 09:58:58 -0700
changeset 242121 f35d190d8e2fd0de55a351a804739c2ae6fb8808
parent 242120 a411bd3307dac2f43d9feadd9653fc0a4917e94a
child 242122 f703c5483fa01bb6251c826cb2f7417d655ae611
push id28679
push userphilringnalda@gmail.com
push dateSat, 02 May 2015 17:02:29 +0000
treeherdermozilla-central@0f5eacc986e8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersevilpie
bugs1160356
milestone40.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1160356 - Make new Date(arg1, arg2, ...) conform to ES3-6 in converting *all* arguments to number before computing the return value. r=evilpie
js/src/jsdate.cpp
js/src/tests/ecma_5/Date/constructor-convert-all-arguments.js
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -569,56 +569,16 @@ RegionMatches(const char* s1, int s1off,
 /* find UTC time from given date... no 1900 correction! */
 static double
 date_msecFromDate(double year, double mon, double mday, double hour,
                   double min, double sec, double msec)
 {
     return MakeDate(MakeDay(year, mon, mday), MakeTime(hour, min, sec, msec));
 }
 
-/* compute the time in msec (unclipped) from the given args */
-#define MAXARGS        7
-
-static bool
-date_msecFromArgs(JSContext* cx, CallArgs args, double* rval)
-{
-    unsigned loop;
-    double array[MAXARGS];
-    double msec_time;
-
-    for (loop = 0; loop < MAXARGS; loop++) {
-        if (loop < args.length()) {
-            double d;
-            if (!ToNumber(cx, args[loop], &d))
-                return false;
-            /* return NaN if any arg is not finite */
-            if (!IsFinite(d)) {
-                *rval = GenericNaN();
-                return true;
-            }
-            array[loop] = ToInteger(d);
-        } else {
-            if (loop == 2) {
-                array[loop] = 1; /* Default the date argument to 1. */
-            } else {
-                array[loop] = 0;
-            }
-        }
-    }
-
-    /* adjust 2-digit years into the 20th century */
-    if (array[0] >= 0 && array[0] <= 99)
-        array[0] += 1900;
-
-    msec_time = date_msecFromDate(array[0], array[1], array[2],
-                                  array[3], array[4], array[5], array[6]);
-    *rval = msec_time;
-    return true;
-}
-
 /* ES6 20.3.3.4. */
 static bool
 date_UTC(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // Steps 1-2.
     double y;
@@ -2942,17 +2902,17 @@ date_valueOf_impl(JSContext* cx, CallArg
 bool
 js::date_valueOf(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsDate, date_valueOf_impl>(cx, args);
 }
 
 static const JSFunctionSpec date_static_methods[] = {
-    JS_FN("UTC",                 date_UTC,                MAXARGS,0),
+    JS_FN("UTC",                 date_UTC,                7,0),
     JS_FN("parse",               date_parse,              1,0),
     JS_FN("now",                 date_now,                0,0),
     JS_FS_END
 };
 
 static const JSFunctionSpec date_methods[] = {
     JS_FN("getTime",             date_getTime,            0,0),
     JS_FN("getTimezoneOffset",   date_getTimezoneOffset,  0,0),
@@ -3074,27 +3034,86 @@ DateOneArgument(JSContext* cx, const Cal
     return ToDateString(cx, args, NowAsMillis());
 }
 
 static bool
 DateMultipleArguments(JSContext* cx, const CallArgs& args)
 {
     MOZ_ASSERT(args.length() >= 2);
 
+    // Step 3.
     if (args.isConstructing()) {
-        double millis;
-        if (!date_msecFromArgs(cx, args, &millis))
+        // Steps 3a-b.
+        double y;
+        if (!ToNumber(cx, args[0], &y))
+            return false;
+
+        // Steps 3c-d.
+        double m;
+        if (!ToNumber(cx, args[1], &m))
             return false;
 
-        if (IsFinite(millis)) {
-            millis = UTC(millis, &cx->runtime()->dateTimeInfo);
-            millis = TimeClip(millis);
+        // Steps 3e-f.
+        double dt;
+        if (args.length() >= 3) {
+            if (!ToNumber(cx, args[2], &dt))
+                return false;
+        } else {
+            dt = 1;
+        }
+
+        // Steps 3g-h.
+        double h;
+        if (args.length() >= 4) {
+            if (!ToNumber(cx, args[3], &h))
+                return false;
+        } else {
+            h = 0;
+        }
+
+        // Steps 3i-j.
+        double min;
+        if (args.length() >= 5) {
+            if (!ToNumber(cx, args[4], &min))
+                return false;
+        } else {
+            min = 0;
         }
 
-        return NewDateObject(cx, args, millis);
+        // Steps 3k-l.
+        double s;
+        if (args.length() >= 6) {
+            if (!ToNumber(cx, args[5], &s))
+                return false;
+        } else {
+            s = 0;
+        }
+
+        // Steps 3m-n.
+        double milli;
+        if (args.length() >= 7) {
+            if (!ToNumber(cx, args[6], &milli))
+                return false;
+        } else {
+            milli = 0;
+        }
+
+        // Step 3o.
+        double yr = y;
+        if (!IsNaN(y)) {
+            double yint = ToInteger(y);
+            if (0 <= yint && yint <= 99)
+                yr = 1900 + yint;
+        }
+
+        // Step 3p.
+        double finalDate = MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli));
+
+        // Steps 3q-t.
+        return NewDateObject(cx, args, TimeClip(UTC(finalDate, &cx->runtime()->dateTimeInfo)));
     }
 
     return ToDateString(cx, args, NowAsMillis());
 }
 
 bool
 js::DateConstructor(JSContext* cx, unsigned argc, Value* vp)
 {
@@ -3139,17 +3158,17 @@ const Class DateObject::class_ = {
     nullptr, /* mayResolve */
     date_convert,
     nullptr, /* finalize */
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     nullptr, /* trace */
     {
-        GenericCreateConstructor<DateConstructor, MAXARGS, JSFunction::FinalizeKind>,
+        GenericCreateConstructor<DateConstructor, 7, JSFunction::FinalizeKind>,
         GenericCreatePrototype,
         date_static_methods,
         nullptr,
         date_methods,
         nullptr,
         FinishDateClassInit
     }
 };
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/Date/constructor-convert-all-arguments.js
@@ -0,0 +1,70 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommonn.org/licenses/publicdomain/
+ */
+
+var BUGNUMBER = 1160356;
+var summary =
+  "new Date(...) must convert *all* arguments to number, not return NaN " +
+  "early if a non-finite argument is encountered";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+function expectThrowTypeError(f, i)
+{
+  try
+  {
+    f();
+    throw new Error("didn't throw");
+  }
+  catch (e)
+  {
+    assertEq(e, 42, "index " + i + ": expected 42, got " + e);
+  }
+}
+
+var bad =
+  { toString: function() { throw 17; }, valueOf: function() { throw 42; } };
+
+var funcs =
+ [
+  function() { new Date(bad); },
+
+  function() { new Date(NaN, bad); },
+  function() { new Date(Infinity, bad); },
+  function() { new Date(1970, bad); },
+
+  function() { new Date(1970, NaN, bad); },
+  function() { new Date(1970, Infinity, bad); },
+  function() { new Date(1970, 4, bad); },
+
+  function() { new Date(1970, 4, NaN, bad); },
+  function() { new Date(1970, 4, Infinity, bad); },
+  function() { new Date(1970, 4, 17, bad); },
+
+  function() { new Date(1970, 4, 17, NaN, bad); },
+  function() { new Date(1970, 4, 17, Infinity, bad); },
+  function() { new Date(1970, 4, 17, 13, bad); },
+
+  function() { new Date(1970, 4, 17, 13, NaN, bad); },
+  function() { new Date(1970, 4, 17, 13, Infinity, bad); },
+  function() { new Date(1970, 4, 17, 13, 37, bad); },
+
+  function() { new Date(1970, 4, 17, 13, 37, NaN, bad); },
+  function() { new Date(1970, 4, 17, 13, 37, Infinity, bad); },
+  function() { new Date(1970, 4, 17, 13, 37, 23, bad); },
+ ];
+
+for (var i = 0, len = funcs.length; i < len; i++)
+  expectThrowTypeError(funcs[i]);
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("Tests complete");