Bug 1158399 - Expose the [[DateValue]] field in Date objects only through a ClippedTime class that enforces prior TimeClip-ing on the given value. r=evilpie, r=bz, r=dhylands, r=mt, r=froydnj, r=khuey, r=baku, r=smaug

--HG--
extra : rebase_source : 803fe39f338b6b32cb0fe2be6cf4056bab57283a
This commit is contained in:
Jeff Walden 2015-05-01 19:12:52 -07:00
Родитель 0cacb70da5
Коммит 1b62a9d07b
18 изменённых файлов: 175 добавлений и 80 удалений

Просмотреть файл

@ -526,7 +526,7 @@ File::GetLastModifiedDate(ErrorResult& aRv)
return Date(); return Date();
} }
return Date(value); return Date(JS::TimeClip(value));
} }
int64_t int64_t

Просмотреть файл

@ -6,8 +6,9 @@
#include "mozilla/dom/Date.h" #include "mozilla/dom/Date.h"
#include "jsapi.h" // for JS_ObjectIsDate, JS_NewDateObjectMsec #include "jsapi.h" // for JS_ObjectIsDate
#include "jsfriendapi.h" // for DateGetMsecSinceEpoch #include "jsfriendapi.h" // for DateGetMsecSinceEpoch
#include "js/Date.h" // for JS::NewDateObject, JS::ClippedTime, JS::TimeClip
#include "js/RootingAPI.h" // for Rooted, MutableHandle #include "js/RootingAPI.h" // for Rooted, MutableHandle
#include "js/Value.h" // for Value #include "js/Value.h" // for Value
#include "mozilla/FloatingPoint.h" // for IsNaN, UnspecifiedNaN #include "mozilla/FloatingPoint.h" // for IsNaN, UnspecifiedNaN
@ -15,30 +16,22 @@
namespace mozilla { namespace mozilla {
namespace dom { namespace dom {
Date::Date()
: mMsecSinceEpoch(UnspecifiedNaN<double>())
{
}
bool
Date::IsUndefined() const
{
return IsNaN(mMsecSinceEpoch);
}
bool bool
Date::SetTimeStamp(JSContext* aCx, JSObject* aObject) Date::SetTimeStamp(JSContext* aCx, JSObject* aObject)
{ {
JS::Rooted<JSObject*> obj(aCx, aObject); JS::Rooted<JSObject*> obj(aCx, aObject);
MOZ_ASSERT(JS_ObjectIsDate(aCx, obj)); MOZ_ASSERT(JS_ObjectIsDate(aCx, obj));
mMsecSinceEpoch = js::DateGetMsecSinceEpoch(aCx, obj); double msecs = js::DateGetMsecSinceEpoch(aCx, obj);
JS::ClippedTime time = JS::TimeClip(msecs);
MOZ_ASSERT(NumbersAreIdentical(msecs, time.toDouble()));
mMsecSinceEpoch = time;
return true; return true;
} }
bool bool
Date::ToDateObject(JSContext* aCx, JS::MutableHandle<JS::Value> aRval) const Date::ToDateObject(JSContext* aCx, JS::MutableHandle<JS::Value> aRval) const
{ {
JSObject* obj = JS_NewDateObjectMsec(aCx, mMsecSinceEpoch); JSObject* obj = JS::NewDateObject(aCx, mMsecSinceEpoch);
if (!obj) { if (!obj) {
return false; return false;
} }

Просмотреть файл

@ -9,6 +9,7 @@
#ifndef mozilla_dom_Date_h #ifndef mozilla_dom_Date_h
#define mozilla_dom_Date_h #define mozilla_dom_Date_h
#include "js/Date.h"
#include "js/TypeDecls.h" #include "js/TypeDecls.h"
namespace mozilla { namespace mozilla {
@ -17,21 +18,33 @@ namespace dom {
class Date class Date
{ {
public: public:
// Not inlining much here to avoid the includes we'd need. Date() {}
Date(); explicit Date(JS::ClippedTime aMilliseconds)
explicit Date(double aMilliseconds)
: mMsecSinceEpoch(aMilliseconds) : mMsecSinceEpoch(aMilliseconds)
{} {}
bool IsUndefined() const; bool IsUndefined() const
double TimeStamp() const {
return !mMsecSinceEpoch.isValid();
}
JS::ClippedTime TimeStamp() const
{ {
return mMsecSinceEpoch; return mMsecSinceEpoch;
} }
void SetTimeStamp(double aMilliseconds)
// Returns an integer in the range [-8.64e15, +8.64e15] (-0 excluded), *or*
// returns NaN. DO NOT ASSUME THIS IS FINITE!
double ToDouble() const
{
return mMsecSinceEpoch.toDouble();
}
void SetTimeStamp(JS::ClippedTime aMilliseconds)
{ {
mMsecSinceEpoch = aMilliseconds; mMsecSinceEpoch = aMilliseconds;
} }
// Can return false if CheckedUnwrap fails. This will NOT throw; // Can return false if CheckedUnwrap fails. This will NOT throw;
// callers should do it as needed. // callers should do it as needed.
bool SetTimeStamp(JSContext* aCx, JSObject* aObject); bool SetTimeStamp(JSContext* aCx, JSObject* aObject);
@ -39,7 +52,7 @@ public:
bool ToDateObject(JSContext* aCx, JS::MutableHandle<JS::Value> aRval) const; bool ToDateObject(JSContext* aCx, JS::MutableHandle<JS::Value> aRval) const;
private: private:
double mMsecSinceEpoch; JS::ClippedTime mMsecSinceEpoch;
}; };
} // namespace dom } // namespace dom

Просмотреть файл

@ -4319,7 +4319,7 @@ nsDOMDeviceStorage::EnumerateInternal(const nsAString& aPath,
PRTime since = 0; PRTime since = 0;
if (aOptions.mSince.WasPassed() && !aOptions.mSince.Value().IsUndefined()) { if (aOptions.mSince.WasPassed() && !aOptions.mSince.Value().IsUndefined()) {
since = PRTime(aOptions.mSince.Value().TimeStamp()); since = PRTime(aOptions.mSince.Value().TimeStamp().toDouble());
} }
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,

Просмотреть файл

@ -10,6 +10,7 @@
#include "js/Value.h" #include "js/Value.h"
#include "js/RootingAPI.h" #include "js/RootingAPI.h"
#include "jsapi.h" #include "jsapi.h"
#include "js/Date.h"
#include "mozilla/dom/FileModeBinding.h" #include "mozilla/dom/FileModeBinding.h"
#include "nsDebug.h" #include "nsDebug.h"
#include "nsIFileStreams.h" #include "nsIFileStreams.h"
@ -49,7 +50,7 @@ MetadataHelper::GetSuccessResult(JSContext* aCx,
if (mParams->LastModifiedRequested()) { if (mParams->LastModifiedRequested()) {
double msec = mParams->LastModified(); double msec = mParams->LastModified();
JSObject *date = JS_NewDateObjectMsec(aCx, msec); JSObject *date = JS::NewDateObject(aCx, JS::TimeClip(msec));
NS_ENSURE_TRUE(date, NS_ERROR_OUT_OF_MEMORY); NS_ENSURE_TRUE(date, NS_ERROR_OUT_OF_MEMORY);
JS::Rooted<JS::Value> dateRoot(aCx, JS::ObjectValue(*date)); JS::Rooted<JS::Value> dateRoot(aCx, JS::ObjectValue(*date));

Просмотреть файл

@ -1514,12 +1514,12 @@ HTMLInputElement::ConvertStringToNumber(nsAString& aValue,
return false; return false;
} }
double date = JS::MakeDate(year, month - 1, day); JS::ClippedTime time = JS::TimeClip(JS::MakeDate(year, month - 1, day));
if (IsNaN(date)) { if (!time.isValid()) {
return false; return false;
} }
aResultValue = Decimal::fromDouble(date); aResultValue = Decimal::fromDouble(time.toDouble());
return true; return true;
} }
case NS_FORM_INPUT_TIME: case NS_FORM_INPUT_TIME:
@ -1762,7 +1762,8 @@ HTMLInputElement::GetValueAsDate(ErrorResult& aRv)
return Nullable<Date>(); return Nullable<Date>();
} }
return Nullable<Date>(Date(JS::MakeDate(year, month - 1, day))); JS::ClippedTime time = JS::TimeClip(JS::MakeDate(year, month - 1, day));
return Nullable<Date>(Date(time));
} }
case NS_FORM_INPUT_TIME: case NS_FORM_INPUT_TIME:
{ {
@ -1773,7 +1774,11 @@ HTMLInputElement::GetValueAsDate(ErrorResult& aRv)
return Nullable<Date>(); return Nullable<Date>();
} }
return Nullable<Date>(Date(millisecond)); JS::ClippedTime time = JS::TimeClip(millisecond);
MOZ_ASSERT(time.toDouble() == millisecond,
"HTML times are restricted to the day after the epoch and "
"never clip");
return Nullable<Date>(Date(time));
} }
} }
@ -1795,7 +1800,7 @@ HTMLInputElement::SetValueAsDate(Nullable<Date> aDate, ErrorResult& aRv)
return; return;
} }
SetValue(Decimal::fromDouble(aDate.Value().TimeStamp())); SetValue(Decimal::fromDouble(aDate.Value().TimeStamp().toDouble()));
} }
NS_IMETHODIMP NS_IMETHODIMP

Просмотреть файл

@ -20,6 +20,7 @@
#include "IndexedDatabaseInlines.h" #include "IndexedDatabaseInlines.h"
#include "IndexedDatabaseManager.h" #include "IndexedDatabaseManager.h"
#include "js/Class.h" #include "js/Class.h"
#include "js/Date.h"
#include "js/StructuredClone.h" #include "js/StructuredClone.h"
#include "KeyPath.h" #include "KeyPath.h"
#include "mozilla/Endian.h" #include "mozilla/Endian.h"
@ -749,8 +750,8 @@ public:
return false; return false;
} }
JS::Rooted<JSObject*> date(aCx, JS::ClippedTime time = JS::TimeClip(aData.lastModifiedDate);
JS_NewDateObjectMsec(aCx, aData.lastModifiedDate)); JS::Rooted<JSObject*> date(aCx, JS::NewDateObject(aCx, time));
if (NS_WARN_IF(!date)) { if (NS_WARN_IF(!date)) {
return false; return false;
} }

Просмотреть файл

@ -8,6 +8,7 @@
#include "Key.h" #include "Key.h"
#include <algorithm> #include <algorithm>
#include "js/Date.h"
#include "js/Value.h" #include "js/Value.h"
#include "jsfriendapi.h" #include "jsfriendapi.h"
#include "mozilla/Endian.h" #include "mozilla/Endian.h"
@ -237,7 +238,11 @@ Key::DecodeJSValInternal(const unsigned char*& aPos, const unsigned char* aEnd,
} }
else if (*aPos - aTypeOffset == eDate) { else if (*aPos - aTypeOffset == eDate) {
double msec = static_cast<double>(DecodeNumber(aPos, aEnd)); double msec = static_cast<double>(DecodeNumber(aPos, aEnd));
JSObject* date = JS_NewDateObjectMsec(aCx, msec); JS::ClippedTime time = JS::TimeClip(msec);
MOZ_ASSERT(msec == time.toDouble(),
"encoding from a Date object not containing an invalid date "
"means we should always have clipped values");
JSObject* date = JS::NewDateObject(aCx, time);
if (!date) { if (!date) {
IDB_WARNING("Failed to make date!"); IDB_WARNING("Failed to make date!");
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;

Просмотреть файл

@ -21,6 +21,7 @@
#include "mozilla/dom/Date.h" #include "mozilla/dom/Date.h"
#include "mozilla/dom/CryptoKey.h" #include "mozilla/dom/CryptoKey.h"
#include "mtransport/dtlsidentity.h" #include "mtransport/dtlsidentity.h"
#include "js/Date.h"
#include "js/StructuredClone.h" #include "js/StructuredClone.h"
#include "js/TypeDecls.h" #include "js/TypeDecls.h"
@ -54,7 +55,10 @@ public:
// WebIDL expires attribute. Note: JS dates are milliseconds since epoch; // WebIDL expires attribute. Note: JS dates are milliseconds since epoch;
// NSPR PRTime is in microseconds since the same epoch. // NSPR PRTime is in microseconds since the same epoch.
int64_t Expires() const { return mExpires / PR_USEC_PER_MSEC; } JS::ClippedTime Expires() const
{
return JS::TimeClip(mExpires / PR_USEC_PER_MSEC);
}
// Accessors for use by PeerConnectionImpl. // Accessors for use by PeerConnectionImpl.
RefPtr<DtlsIdentity> CreateDtlsIdentity() const; RefPtr<DtlsIdentity> CreateDtlsIdentity() const;

Просмотреть файл

@ -34,7 +34,7 @@ TimeManager::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
void void
TimeManager::Set(Date& aDate) TimeManager::Set(Date& aDate)
{ {
Set(aDate.TimeStamp()); Set(aDate.ToDouble());
} }
void void

Просмотреть файл

@ -3,51 +3,118 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* JavaScript date/time computation and creation functions. */
#ifndef js_Date_h #ifndef js_Date_h
#define js_Date_h #define js_Date_h
/*
* Dates in JavaScript are defined by IEEE-754 double precision numbers from
* the set:
*
* { t : -8.64e15 t +8.64e15 } { NaN }
*
* The single NaN value represents any invalid-date value. All other values
* represent idealized durations in milliseconds since the UTC epoch. (Leap
* seconds are ignored; leap days are not.) +0 is the only zero in this set.
* The limit represented by 8.64e15 milliseconds is 100 million days either
* side of 00:00 January 1, 1970 UTC.
*
* Dates in the above set are represented by the |ClippedTime| class. The
* double type is a superset of the above set, so it *may* (but need not)
* represent a date. Use ECMAScript's |TimeClip| method to produce a date from
* a double.
*
* Date *objects* are simply wrappers around |TimeClip|'d numbers, with a bunch
* of accessor methods to the various aspects of the represented date.
*/
#include "mozilla/FloatingPoint.h" #include "mozilla/FloatingPoint.h"
#include "mozilla/MathAlgorithms.h" #include "mozilla/MathAlgorithms.h"
#include "jstypes.h"
#include "js/Conversions.h" #include "js/Conversions.h"
#include "js/Value.h" #include "js/Value.h"
struct JSContext;
namespace JS { namespace JS {
class ClippedTime;
inline ClippedTime TimeClip(double time);
/*
* |ClippedTime| represents the limited subset of dates/times described above.
*
* An invalid date/time may be created through the |ClippedTime::invalid|
* method. Otherwise, a |ClippedTime| may be created using the |TimeClip|
* method.
*
* In typical use, the user might wish to manipulate a timestamp. The user
* performs a series of operations on it, but the final value might not be a
* date as defined above -- it could have overflowed, acquired a fractional
* component, &c. So as a *final* step, the user passes that value through
* |TimeClip| to produce a number restricted to JavaScript's date range.
*
* APIs that accept a JavaScript date value thus accept a |ClippedTime|, not a
* double. This ensures that date/time APIs will only ever receive acceptable
* JavaScript dates. This also forces users to perform any desired clipping,
* as only the user knows what behavior is desired when clipping occurs.
*/
class ClippedTime class ClippedTime
{ {
double t; double t;
/* ES5 15.9.1.14. */ explicit ClippedTime(double time) : t(time) {}
double timeClip(double time) { friend ClippedTime TimeClip(double time);
/* Steps 1-2. */
const double MaxTimeMagnitude = 8.64e15;
if (!mozilla::IsFinite(time) || mozilla::Abs(time) > MaxTimeMagnitude)
return JS::GenericNaN();
/* Step 3. */
return JS::ToInteger(time) + (+0.0);
}
public: public:
ClippedTime() : t(JS::GenericNaN()) {} // Create an invalid date.
explicit ClippedTime(double time) : t(timeClip(time)) {} ClippedTime() : t(mozilla::UnspecifiedNaN<double>()) {}
static ClippedTime NaN() { return ClippedTime(); } // Create an invalid date/time, more explicitly; prefer this to the default
// constructor.
static ClippedTime invalid() { return ClippedTime(); }
double value() const { return t; } double toDouble() const { return t; }
bool isValid() const { return !mozilla::IsNaN(t); }
}; };
// ES6 20.3.1.15.
//
// Clip a double to JavaScript's date range (or to an invalid date) using the
// ECMAScript TimeClip algorithm.
inline ClippedTime inline ClippedTime
TimeClip(double d) TimeClip(double time)
{ {
return ClippedTime(d); // Steps 1-2.
const double MaxTimeMagnitude = 8.64e15;
if (!mozilla::IsFinite(time) || mozilla::Abs(time) > MaxTimeMagnitude)
return ClippedTime(mozilla::UnspecifiedNaN<double>());
// Step 3.
return ClippedTime(ToInteger(time) + (+0.0));
} }
// Year is a year, month is 0-11, day is 1-based. The return value is // Produce a double Value from the given time. Because times may be NaN,
// a number of milliseconds since the epoch. Can return NaN. // prefer using this to manual canonicalization.
inline Value
TimeValue(ClippedTime time)
{
return DoubleValue(JS::CanonicalizeNaN(time.toDouble()));
}
// Create a new Date object whose [[DateValue]] internal slot contains the
// clipped |time|. (Users who must represent times outside that range must use
// another representation.)
extern JS_PUBLIC_API(JSObject*)
NewDateObject(JSContext* cx, ClippedTime time);
// Year is a year, month is 0-11, day is 1-based. The return value is a number
// of milliseconds since the epoch.
//
// Consistent with the MakeDate algorithm defined in ECMAScript, this value is
// *not* clipped! Use JS::TimeClip if you need a clipped date.
JS_PUBLIC_API(double) JS_PUBLIC_API(double)
MakeDate(double year, unsigned month, unsigned day); MakeDate(double year, unsigned month, unsigned day);

Просмотреть файл

@ -5500,11 +5500,11 @@ JS_NewDateObject(JSContext* cx, int year, int mon, int mday, int hour, int min,
} }
JS_PUBLIC_API(JSObject*) JS_PUBLIC_API(JSObject*)
JS_NewDateObjectMsec(JSContext* cx, double msec) JS::NewDateObject(JSContext* cx, JS::ClippedTime time)
{ {
AssertHeapIsIdle(cx); AssertHeapIsIdle(cx);
CHECK_REQUEST(cx); CHECK_REQUEST(cx);
return NewDateObjectMsec(cx, JS::TimeClip(msec)); return NewDateObjectMsec(cx, time);
} }
JS_PUBLIC_API(bool) JS_PUBLIC_API(bool)

Просмотреть файл

@ -4761,9 +4761,6 @@ SetForEach(JSContext *cx, HandleObject obj, HandleValue callbackFn, HandleValue
extern JS_PUBLIC_API(JSObject*) extern JS_PUBLIC_API(JSObject*)
JS_NewDateObject(JSContext* cx, int year, int mon, int mday, int hour, int min, int sec); JS_NewDateObject(JSContext* cx, int year, int mon, int mday, int hour, int min, int sec);
extern JS_PUBLIC_API(JSObject*)
JS_NewDateObjectMsec(JSContext* cx, double msec);
/* /*
* Infallible predicate to test whether obj is a date object. * Infallible predicate to test whether obj is a date object.
*/ */

Просмотреть файл

@ -353,7 +353,7 @@ MakeDate(double day, double time)
JS_PUBLIC_API(double) JS_PUBLIC_API(double)
JS::MakeDate(double year, unsigned month, unsigned day) JS::MakeDate(double year, unsigned month, unsigned day)
{ {
return TimeClip(::MakeDate(MakeDay(year, month, day), 0)).value(); return ::MakeDate(MakeDay(year, month, day), 0);
} }
JS_PUBLIC_API(double) JS_PUBLIC_API(double)
@ -640,7 +640,7 @@ date_UTC(JSContext* cx, unsigned argc, Value* vp)
// Step 16. // Step 16.
ClippedTime time = TimeClip(MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli))); ClippedTime time = TimeClip(MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli)));
args.rval().setDouble(time.value()); args.rval().set(TimeValue(time));
return true; return true;
} }
@ -878,7 +878,7 @@ ParseISODate(const CharT* s, size_t length, ClippedTime* result, DateTimeInfo* d
msec -= tzMul * (tzHour * msPerHour + tzMin * msPerMinute); msec -= tzMul * (tzHour * msPerHour + tzMin * msPerMinute);
*result = TimeClip(msec); *result = TimeClip(msec);
return NumbersAreIdentical(msec, result->value()); return NumbersAreIdentical(msec, result->toDouble());
#undef PEEK #undef PEEK
#undef NEED #undef NEED
@ -1193,21 +1193,21 @@ date_parse(JSContext* cx, unsigned argc, Value* vp)
return true; return true;
} }
args.rval().setDouble(result.value()); args.rval().set(TimeValue(result));
return true; return true;
} }
static ClippedTime static ClippedTime
NowAsMillis() NowAsMillis()
{ {
return ClippedTime(static_cast<double>(PRMJ_Now()) / PRMJ_USEC_PER_MSEC); return TimeClip(static_cast<double>(PRMJ_Now()) / PRMJ_USEC_PER_MSEC);
} }
bool bool
js::date_now(JSContext* cx, unsigned argc, Value* vp) js::date_now(JSContext* cx, unsigned argc, Value* vp)
{ {
CallArgs args = CallArgsFromVp(argc, vp); CallArgs args = CallArgsFromVp(argc, vp);
args.rval().setDouble(NowAsMillis().value()); args.rval().set(TimeValue(NowAsMillis()));
return true; return true;
} }
@ -1217,14 +1217,14 @@ DateObject::setUTCTime(ClippedTime t)
for (size_t ind = COMPONENTS_START_SLOT; ind < RESERVED_SLOTS; ind++) for (size_t ind = COMPONENTS_START_SLOT; ind < RESERVED_SLOTS; ind++)
setReservedSlot(ind, UndefinedValue()); setReservedSlot(ind, UndefinedValue());
setFixedSlot(UTC_TIME_SLOT, DoubleValue(t.value())); setFixedSlot(UTC_TIME_SLOT, TimeValue(t));
} }
void void
DateObject::setUTCTime(ClippedTime t, MutableHandleValue vp) DateObject::setUTCTime(ClippedTime t, MutableHandleValue vp)
{ {
setUTCTime(t); setUTCTime(t);
vp.setDouble(t.value()); vp.set(TimeValue(t));
} }
void void
@ -1687,7 +1687,7 @@ date_setTime_impl(JSContext* cx, CallArgs args)
{ {
Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>()); Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
if (args.length() == 0) { if (args.length() == 0) {
dateObj->setUTCTime(ClippedTime::NaN(), args.rval()); dateObj->setUTCTime(ClippedTime::invalid(), args.rval());
return true; return true;
} }
@ -2322,7 +2322,7 @@ date_setYear_impl(JSContext* cx, CallArgs args)
/* Step 3. */ /* Step 3. */
if (IsNaN(y)) { if (IsNaN(y)) {
dateObj->setUTCTime(ClippedTime::NaN(), args.rval()); dateObj->setUTCTime(ClippedTime::invalid(), args.rval());
return true; return true;
} }
@ -2366,7 +2366,7 @@ static const char * const months[] =
static void static void
print_gmt_string(char* buf, size_t size, double utctime) print_gmt_string(char* buf, size_t size, double utctime)
{ {
MOZ_ASSERT(NumbersAreIdentical(TimeClip(utctime).value(), utctime)); MOZ_ASSERT(NumbersAreIdentical(TimeClip(utctime).toDouble(), utctime));
JS_snprintf(buf, size, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT", JS_snprintf(buf, size, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
days[int(WeekDay(utctime))], days[int(WeekDay(utctime))],
int(DateFromTime(utctime)), int(DateFromTime(utctime)),
@ -2380,7 +2380,7 @@ print_gmt_string(char* buf, size_t size, double utctime)
static void static void
print_iso_string(char* buf, size_t size, double utctime) print_iso_string(char* buf, size_t size, double utctime)
{ {
MOZ_ASSERT(NumbersAreIdentical(TimeClip(utctime).value(), utctime)); MOZ_ASSERT(NumbersAreIdentical(TimeClip(utctime).toDouble(), utctime));
JS_snprintf(buf, size, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ", JS_snprintf(buf, size, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ",
int(YearFromTime(utctime)), int(YearFromTime(utctime)),
int(MonthFromTime(utctime)) + 1, int(MonthFromTime(utctime)) + 1,
@ -2394,7 +2394,7 @@ print_iso_string(char* buf, size_t size, double utctime)
static void static void
print_iso_extended_string(char* buf, size_t size, double utctime) print_iso_extended_string(char* buf, size_t size, double utctime)
{ {
MOZ_ASSERT(NumbersAreIdentical(TimeClip(utctime).value(), utctime)); MOZ_ASSERT(NumbersAreIdentical(TimeClip(utctime).toDouble(), utctime));
JS_snprintf(buf, size, "%+.6d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ", JS_snprintf(buf, size, "%+.6d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ",
int(YearFromTime(utctime)), int(YearFromTime(utctime)),
int(MonthFromTime(utctime)) + 1, int(MonthFromTime(utctime)) + 1,
@ -2550,7 +2550,7 @@ date_format(JSContext* cx, double date, formatspec format, MutableHandleValue rv
if (!IsFinite(date)) { if (!IsFinite(date)) {
JS_snprintf(buf, sizeof buf, js_NaN_date_str); JS_snprintf(buf, sizeof buf, js_NaN_date_str);
} else { } else {
MOZ_ASSERT(NumbersAreIdentical(TimeClip(date).value(), date)); MOZ_ASSERT(NumbersAreIdentical(TimeClip(date).toDouble(), date));
double local = LocalTime(date, &cx->runtime()->dateTimeInfo); double local = LocalTime(date, &cx->runtime()->dateTimeInfo);
@ -2990,7 +2990,7 @@ NewDateObject(JSContext* cx, const CallArgs& args, ClippedTime t)
static bool static bool
ToDateString(JSContext* cx, const CallArgs& args, ClippedTime t) ToDateString(JSContext* cx, const CallArgs& args, ClippedTime t)
{ {
return date_format(cx, t.value(), FORMATSPEC_FULL, args.rval()); return date_format(cx, t.toDouble(), FORMATSPEC_FULL, args.rval());
} }
static bool static bool
@ -3023,7 +3023,7 @@ DateOneArgument(JSContext* cx, const CallArgs& args)
return false; return false;
if (!ParseDate(linearStr, &t, &cx->runtime()->dateTimeInfo)) if (!ParseDate(linearStr, &t, &cx->runtime()->dateTimeInfo))
t = ClippedTime::NaN(); t = ClippedTime::invalid();
} else { } else {
double d; double d;
if (!ToNumber(cx, args[0], &d)) if (!ToNumber(cx, args[0], &d))

Просмотреть файл

@ -43,7 +43,14 @@ class DateObject : public NativeObject
static const Class class_; static const Class class_;
static const Class protoClass_; static const Class protoClass_;
inline const js::Value& UTCTime() const { JS::ClippedTime clippedTime() const {
double t = getFixedSlot(UTC_TIME_SLOT).toDouble();
JS::ClippedTime clipped = JS::TimeClip(t);
MOZ_ASSERT(mozilla::NumbersAreIdentical(clipped.toDouble(), t));
return clipped;
}
const js::Value& UTCTime() const {
return getFixedSlot(UTC_TIME_SLOT); return getFixedSlot(UTC_TIME_SLOT);
} }

Просмотреть файл

@ -28,6 +28,7 @@
#include "builtin/TypedObject.h" #include "builtin/TypedObject.h"
#include "builtin/WeakSetObject.h" #include "builtin/WeakSetObject.h"
#include "gc/Marking.h" #include "gc/Marking.h"
#include "js/Date.h"
#include "vm/Compression.h" #include "vm/Compression.h"
#include "vm/GeneratorObject.h" #include "vm/GeneratorObject.h"
#include "vm/Interpreter.h" #include "vm/Interpreter.h"
@ -1777,7 +1778,7 @@ CloneObject(JSContext* cx, HandleNativeObject selfHostedObject)
MOZ_ASSERT(source->isPermanentAtom()); MOZ_ASSERT(source->isPermanentAtom());
clone = RegExpObject::createNoStatics(cx, source, reobj.getFlags(), nullptr, cx->tempLifoAlloc()); clone = RegExpObject::createNoStatics(cx, source, reobj.getFlags(), nullptr, cx->tempLifoAlloc());
} else if (selfHostedObject->is<DateObject>()) { } else if (selfHostedObject->is<DateObject>()) {
clone = JS_NewDateObjectMsec(cx, selfHostedObject->as<DateObject>().UTCTime().toNumber()); clone = JS::NewDateObject(cx, selfHostedObject->as<DateObject>().clippedTime());
} else if (selfHostedObject->is<BooleanObject>()) { } else if (selfHostedObject->is<BooleanObject>()) {
clone = BooleanObject::create(cx, selfHostedObject->as<BooleanObject>().unbox()); clone = BooleanObject::create(cx, selfHostedObject->as<BooleanObject>().unbox());
} else if (selfHostedObject->is<NumberObject>()) { } else if (selfHostedObject->is<NumberObject>()) {

Просмотреть файл

@ -1592,7 +1592,7 @@ JSStructuredCloneReader::startRead(MutableHandleValue vp)
if (!in.readDouble(&d) || !checkDouble(d)) if (!in.readDouble(&d) || !checkDouble(d))
return false; return false;
JS::ClippedTime t = JS::TimeClip(d); JS::ClippedTime t = JS::TimeClip(d);
if (!NumbersAreIdentical(d, t.value())) { if (!NumbersAreIdentical(d, t.toDouble())) {
JS_ReportErrorNumber(context(), GetErrorMessage, nullptr, JS_ReportErrorNumber(context(), GetErrorMessage, nullptr,
JSMSG_SC_BAD_SERIALIZED_DATA, "date"); JSMSG_SC_BAD_SERIALIZED_DATA, "date");
return false; return false;

Просмотреть файл

@ -35,6 +35,7 @@
#include "mozilla/Services.h" #include "mozilla/Services.h"
#include "nsIXPConnect.h" #include "nsIXPConnect.h"
#include "jsapi.h" #include "jsapi.h"
#include "js/Date.h"
#include "prenv.h" #include "prenv.h"
#include "nsAppDirectoryServiceDefs.h" #include "nsAppDirectoryServiceDefs.h"
@ -780,7 +781,7 @@ nsAppStartup::GetStartupInfo(JSContext* aCx, JS::MutableHandle<JS::Value> aRetva
if (stamp >= procTime) { if (stamp >= procTime) {
PRTime prStamp = ComputeAbsoluteTimestamp(absNow, now, stamp) PRTime prStamp = ComputeAbsoluteTimestamp(absNow, now, stamp)
/ PR_USEC_PER_MSEC; / PR_USEC_PER_MSEC;
JS::Rooted<JSObject*> date(aCx, JS_NewDateObjectMsec(aCx, prStamp)); JS::Rooted<JSObject*> date(aCx, JS::NewDateObject(aCx, JS::TimeClip(prStamp)));
JS_DefineProperty(aCx, obj, StartupTimeline::Describe(ev), date, JSPROP_ENUMERATE); JS_DefineProperty(aCx, obj, StartupTimeline::Describe(ev), date, JSPROP_ENUMERATE);
} else { } else {
Telemetry::Accumulate(Telemetry::STARTUP_MEASUREMENT_ERRORS, ev); Telemetry::Accumulate(Telemetry::STARTUP_MEASUREMENT_ERRORS, ev);