зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
0cacb70da5
Коммит
1b62a9d07b
|
@ -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
|
||||||
|
|
107
js/public/Date.h
107
js/public/Date.h
|
@ -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);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче