Bug 1208808 - Move time zone adjustment information out of JSRuntime, into global state protected by a spinlock. r=till

This commit is contained in:
Jeff Walden 2015-10-30 16:14:32 -07:00
parent 5d5db3fe15
commit 9badad2fe5
20 changed files with 481 additions and 341 deletions

View File

@ -6,7 +6,7 @@
#include "DateCacheCleaner.h"
#include "jsapi.h"
#include "js/Date.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Hal.h"
@ -33,7 +33,7 @@ public:
void Notify(const SystemTimezoneChangeInformation& aSystemTimezoneChangeInfo)
{
mozilla::AutoSafeJSContext cx;
JS_ClearDateCaches(cx);
JS::ResetTimeZone();
}
};

View File

@ -7,7 +7,7 @@
/*
* InitializeDateCacheCleaner registers DateCacheCleaner to
* SystemTimeChangeObserver. When time zone is changed, DateCacheCleaner calls
* JS_ClearDateCaches to update the time zone information.
* JS::ResetTimeZone to update the time zone information.
*/
namespace mozilla {

View File

@ -39,6 +39,22 @@ struct JSContext;
namespace JS {
/**
* Re-query the system to determine the current time zone adjustment from UTC,
* including any component due to DST. If the time zone has changed, this will
* cause all Date object non-UTC methods and formatting functions to produce
* appropriately adjusted results.
*
* Left to its own devices, SpiderMonkey itself may occasionally call this
* method to attempt to keep up with system time changes. However, no
* particular frequency of checking is guaranteed. Embedders unable to accept
* occasional inaccuracies should call this method in response to system time
* changes, or immediately before operations requiring instantaneous
* correctness, to guarantee correct behavior.
*/
extern JS_PUBLIC_API(void)
ResetTimeZone();
class ClippedTime;
inline ClippedTime TimeClip(double time);

View File

@ -0,0 +1,84 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
/* SpiderMonkey initialization and shutdown APIs. */
#ifndef js_Initialization_h
#define js_Initialization_h
#include "jstypes.h"
namespace JS {
namespace detail {
enum class InitState { Uninitialized = 0, Running, ShutDown };
/**
* SpiderMonkey's initialization status is tracked here, and it controls things
* that should happen only once across all runtimes. It's an API requirement
* that JS_Init (and JS_ShutDown, if called) be called in a thread-aware
* manner, so this (internal -- embedders, don't use!) variable doesn't need to
* be atomic.
*/
extern JS_PUBLIC_DATA(InitState)
libraryInitState;
} // namespace detail
} // namespace JS
// These are equivalent to ICU's |UMemAllocFn|, |UMemReallocFn|, and
// |UMemFreeFn| types. The first argument (called |context| in the ICU docs)
// will always be nullptr and should be ignored.
typedef void* (*JS_ICUAllocFn)(const void*, size_t size);
typedef void* (*JS_ICUReallocFn)(const void*, void* p, size_t size);
typedef void (*JS_ICUFreeFn)(const void*, void* p);
/**
* This function can be used to track memory used by ICU. If it is called, it
* *must* be called before JS_Init. Don't use it unless you know what you're
* doing!
*/
extern JS_PUBLIC_API(bool)
JS_SetICUMemoryFunctions(JS_ICUAllocFn allocFn,
JS_ICUReallocFn reallocFn,
JS_ICUFreeFn freeFn);
/**
* Initialize SpiderMonkey, returning true only if initialization succeeded.
* Once this method has succeeded, it is safe to call JS_NewRuntime and other
* JSAPI methods.
*
* This method must be called before any other JSAPI method is used on any
* thread. Once it has been used, it is safe to call any JSAPI method, and it
* remains safe to do so until JS_ShutDown is correctly called.
*
* It is currently not possible to initialize SpiderMonkey multiple times (that
* is, calling JS_Init/JSAPI methods/JS_ShutDown in that order, then doing so
* again). This restriction may eventually be lifted.
*/
extern JS_PUBLIC_API(bool)
JS_Init(void);
/**
* Destroy free-standing resources allocated by SpiderMonkey, not associated
* with any runtime, context, or other structure.
*
* This method should be called after all other JSAPI data has been properly
* cleaned up: every new runtime must have been destroyed, every new context
* must have been destroyed, and so on. Calling this method before all other
* resources have been destroyed has undefined behavior.
*
* Failure to call this method, at present, has no adverse effects other than
* leaking memory. This may not always be the case; it's recommended that all
* embedders call this method when all other JSAPI operations have completed.
*
* It is currently not possible to initialize SpiderMonkey multiple times (that
* is, calling JS_Init/JSAPI methods/JS_ShutDown in that order, then doing so
* again). This restriction may eventually be lifted.
*/
extern JS_PUBLIC_API(void)
JS_ShutDown(void);
#endif /* js_Initialization_h */

View File

@ -9,6 +9,7 @@
#include "gdb-tests.h"
#include "jsapi.h"
#include "jsfriendapi.h"
#include "js/Initialization.h"
using namespace JS;

View File

@ -8,6 +8,7 @@
#include <stdio.h>
#include "js/Initialization.h"
#include "js/RootingAPI.h"
JSAPITest* JSAPITest::list;

View File

@ -62,14 +62,10 @@
#include "js/CharacterEncoding.h"
#include "js/Conversions.h"
#include "js/Date.h"
#include "js/Initialization.h"
#include "js/Proxy.h"
#include "js/SliceBudget.h"
#include "js/StructuredClone.h"
#if ENABLE_INTL_API
#include "unicode/timezone.h"
#include "unicode/uclean.h"
#include "unicode/utypes.h"
#endif // ENABLE_INTL_API
#include "vm/DateObject.h"
#include "vm/Debugger.h"
#include "vm/ErrorObject.h"
@ -445,129 +441,6 @@ JS_IsBuiltinFunctionConstructor(JSFunction* fun)
/************************************************************************/
/*
* SpiderMonkey's initialization status is tracked here, and it controls things
* that should happen only once across all runtimes. It's an API requirement
* that JS_Init (and JS_ShutDown, if called) be called in a thread-aware
* manner, so this variable doesn't need to be atomic.
*
* The only reason at present for the restriction that you can't call
* JS_Init/stuff/JS_ShutDown multiple times is the Windows PRMJ NowInit
* initialization code, which uses PR_CallOnce to initialize the PRMJ_Now
* subsystem. (For reinitialization to be permitted, we'd need to "reset" the
* called-once status -- doable, but more trouble than it's worth now.)
* Initializing that subsystem from JS_Init eliminates the problem, but
* initialization can take a comparatively long time (15ms or so), so we
* really don't want to do it in JS_Init, and we really do want to do it only
* when PRMJ_Now is eventually called.
*/
enum InitState { Uninitialized, Running, ShutDown };
static InitState jsInitState = Uninitialized;
#ifdef DEBUG
static unsigned
MessageParameterCount(const char* format)
{
unsigned numfmtspecs = 0;
for (const char* fmt = format; *fmt != '\0'; fmt++) {
if (*fmt == '{' && isdigit(fmt[1]))
++numfmtspecs;
}
return numfmtspecs;
}
static void
CheckMessageParameterCounts()
{
// Assert that each message format has the correct number of braced
// parameters.
# define MSG_DEF(name, count, exception, format) \
MOZ_ASSERT(MessageParameterCount(format) == count);
# include "js.msg"
# undef MSG_DEF
}
#endif /* DEBUG */
JS_PUBLIC_API(bool)
JS_Init(void)
{
MOZ_ASSERT(jsInitState == Uninitialized,
"must call JS_Init once before any JSAPI operation except "
"JS_SetICUMemoryFunctions");
MOZ_ASSERT(!JSRuntime::hasLiveRuntimes(),
"how do we have live runtimes before JS_Init?");
PRMJ_NowInit();
#ifdef DEBUG
CheckMessageParameterCounts();
#endif
using js::TlsPerThreadData;
if (!TlsPerThreadData.initialized() && !TlsPerThreadData.init())
return false;
#if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
if (!js::oom::InitThreadType())
return false;
js::oom::SetThreadType(js::oom::THREAD_TYPE_MAIN);
#endif
jit::ExecutableAllocator::initStatic();
if (!jit::InitializeIon())
return false;
#if EXPOSE_INTL_API
UErrorCode err = U_ZERO_ERROR;
u_init(&err);
if (U_FAILURE(err))
return false;
#endif // EXPOSE_INTL_API
if (!CreateHelperThreadsState())
return false;
if (!FutexRuntime::initialize())
return false;
jsInitState = Running;
return true;
}
JS_PUBLIC_API(void)
JS_ShutDown(void)
{
MOZ_ASSERT(jsInitState == Running,
"JS_ShutDown must only be called after JS_Init and can't race with it");
#ifdef DEBUG
if (JSRuntime::hasLiveRuntimes()) {
// Gecko is too buggy to assert this just yet.
fprintf(stderr,
"WARNING: YOU ARE LEAKING THE WORLD (at least one JSRuntime "
"and everything alive inside it, that is) AT JS_ShutDown "
"TIME. FIX THIS!\n");
}
#endif
FutexRuntime::destroy();
DestroyHelperThreadsState();
#ifdef JS_TRACE_LOGGING
DestroyTraceLoggerThreadState();
DestroyTraceLoggerGraphState();
#endif
PRMJ_NowShutdown();
#if EXPOSE_INTL_API
u_cleanup();
#endif // EXPOSE_INTL_API
jsInitState = ShutDown;
}
#ifdef DEBUG
JS_FRIEND_API(bool)
JS::isGCEnabled()
@ -581,7 +454,7 @@ JS_FRIEND_API(bool) JS::isGCEnabled() { return true; }
JS_PUBLIC_API(JSRuntime*)
JS_NewRuntime(uint32_t maxbytes, uint32_t maxNurseryBytes, JSRuntime* parentRuntime)
{
MOZ_ASSERT(jsInitState == Running,
MOZ_ASSERT(JS::detail::libraryInitState == JS::detail::InitState::Running,
"must call JS_Init prior to creating any JSRuntimes");
// Make sure that all parent runtimes are the topmost parent.
@ -606,22 +479,6 @@ JS_DestroyRuntime(JSRuntime* rt)
js_delete(rt);
}
JS_PUBLIC_API(bool)
JS_SetICUMemoryFunctions(JS_ICUAllocFn allocFn, JS_ICUReallocFn reallocFn, JS_ICUFreeFn freeFn)
{
MOZ_ASSERT(jsInitState == Uninitialized,
"must call JS_SetICUMemoryFunctions before any other JSAPI "
"operation (including JS_Init)");
#if EXPOSE_INTL_API
UErrorCode status = U_ZERO_ERROR;
u_setMemoryFunctions(/* context = */ nullptr, allocFn, reallocFn, freeFn, &status);
return U_SUCCESS(status);
#else
return true;
#endif
}
static JS_CurrentEmbedderTimeFunction currentEmbedderTimeFunction;
JS_PUBLIC_API(void)
@ -5573,14 +5430,6 @@ JS_ObjectIsDate(JSContext* cx, HandleObject obj, bool* isDate)
return true;
}
JS_PUBLIC_API(void)
JS_ClearDateCaches(JSContext* cx)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
cx->runtime()->dateTimeInfo.updateTimeZoneAdjustment();
}
/************************************************************************/
/*
@ -6353,11 +6202,3 @@ JS::GetObjectZone(JSObject* obj)
{
return obj->zone();
}
JS_PUBLIC_API(void)
JS::ResetTimeZone()
{
#if ENABLE_INTL_API && defined(ICU_TZ_HAS_RECREATE_DEFAULT)
icu::TimeZone::recreateDefault();
#endif
}

View File

@ -40,9 +40,6 @@
namespace JS {
extern JS_PUBLIC_API(void)
ResetTimeZone();
class TwoByteChars;
#ifdef JS_DEBUG
@ -953,49 +950,14 @@ JS_IsBuiltinFunctionConstructor(JSFunction* fun);
/************************************************************************/
/*
* Initialization, locking, contexts, and memory allocation.
* Locking, contexts, and memory allocation.
*
* It is important that the first runtime and first context be created in a
* single-threaded fashion, otherwise the behavior of the library is undefined.
* It is important that SpiderMonkey be initialized, and the first runtime and
* first context be created, in a single-threaded fashion. Otherwise the
* behavior of the library is undefined.
* See: http://developer.mozilla.org/en/docs/Category:JSAPI_Reference
*/
/**
* Initialize SpiderMonkey, returning true only if initialization succeeded.
* Once this method has succeeded, it is safe to call JS_NewRuntime and other
* JSAPI methods.
*
* This method must be called before any other JSAPI method is used on any
* thread. Once it has been used, it is safe to call any JSAPI method, and it
* remains safe to do so until JS_ShutDown is correctly called.
*
* It is currently not possible to initialize SpiderMonkey multiple times (that
* is, calling JS_Init/JSAPI methods/JS_ShutDown in that order, then doing so
* again). This restriction may eventually be lifted.
*/
extern JS_PUBLIC_API(bool)
JS_Init(void);
/**
* Destroy free-standing resources allocated by SpiderMonkey, not associated
* with any runtime, context, or other structure.
*
* This method should be called after all other JSAPI data has been properly
* cleaned up: every new runtime must have been destroyed, every new context
* must have been destroyed, and so on. Calling this method before all other
* resources have been destroyed has undefined behavior.
*
* Failure to call this method, at present, has no adverse effects other than
* leaking memory. This may not always be the case; it's recommended that all
* embedders call this method when all other JSAPI operations have completed.
*
* It is currently not possible to initialize SpiderMonkey multiple times (that
* is, calling JS_Init/JSAPI methods/JS_ShutDown in that order, then doing so
* again). This restriction may eventually be lifted.
*/
extern JS_PUBLIC_API(void)
JS_ShutDown(void);
extern JS_PUBLIC_API(JSRuntime*)
JS_NewRuntime(uint32_t maxbytes,
uint32_t maxNurseryBytes = JS::DefaultNurseryBytes,
@ -1004,20 +966,6 @@ JS_NewRuntime(uint32_t maxbytes,
extern JS_PUBLIC_API(void)
JS_DestroyRuntime(JSRuntime* rt);
// These are equivalent to ICU's |UMemAllocFn|, |UMemReallocFn|, and
// |UMemFreeFn| types. The first argument (called |context| in the ICU docs)
// will always be nullptr, and should be ignored.
typedef void* (*JS_ICUAllocFn)(const void*, size_t size);
typedef void* (*JS_ICUReallocFn)(const void*, void* p, size_t size);
typedef void (*JS_ICUFreeFn)(const void*, void* p);
/**
* This function can be used to track memory used by ICU.
* Do not use it unless you know what you are doing!
*/
extern JS_PUBLIC_API(bool)
JS_SetICUMemoryFunctions(JS_ICUAllocFn allocFn, JS_ICUReallocFn reallocFn, JS_ICUFreeFn freeFn);
typedef double (*JS_CurrentEmbedderTimeFunction)();
/**
@ -4992,13 +4940,6 @@ JS_NewDateObject(JSContext* cx, int year, int mon, int mday, int hour, int min,
extern JS_PUBLIC_API(bool)
JS_ObjectIsDate(JSContext* cx, JS::HandleObject obj, bool* isDate);
/**
* Clears the cache of calculated local time from each Date object.
* Call to propagate a system timezone change.
*/
extern JS_PUBLIC_API(void)
JS_ClearDateCaches(JSContext* cx);
/************************************************************************/
/*

View File

@ -18,6 +18,7 @@
#include "gc/Marking.h"
#include "jit/JitCompartment.h"
#include "js/Date.h"
#include "js/Proxy.h"
#include "js/RootingAPI.h"
#include "proxy/DeadObjectProxy.h"
@ -120,11 +121,10 @@ JSCompartment::init(JSContext* maybecx)
*
* As a hack, we clear our timezone cache every time we create a new
* compartment. This ensures that the cache is always relatively fresh, but
* shouldn't interfere with benchmarks which create tons of date objects
* shouldn't interfere with benchmarks that create tons of date objects
* (unless they also create tons of iframes, which seems unlikely).
*/
if (maybecx)
maybecx->runtime()->dateTimeInfo.updateTimeZoneAdjustment();
JS::ResetTimeZone();
if (!crossCompartmentWrappers.init(0)) {
if (maybecx)

View File

@ -407,7 +407,7 @@ EquivalentYearForDST(int year)
/* ES5 15.9.1.8. */
static double
DaylightSavingTA(double t, DateTimeInfo* dtInfo)
DaylightSavingTA(double t)
{
if (!IsFinite(t))
return GenericNaN();
@ -423,29 +423,30 @@ DaylightSavingTA(double t, DateTimeInfo* dtInfo)
}
int64_t utcMilliseconds = static_cast<int64_t>(t);
int64_t offsetMilliseconds = dtInfo->getDSTOffsetMilliseconds(utcMilliseconds);
int64_t offsetMilliseconds = DateTimeInfo::getDSTOffsetMilliseconds(utcMilliseconds);
return static_cast<double>(offsetMilliseconds);
}
static double
AdjustTime(double date, DateTimeInfo* dtInfo)
AdjustTime(double date)
{
double t = DaylightSavingTA(date, dtInfo) + dtInfo->localTZA();
t = (dtInfo->localTZA() >= 0) ? fmod(t, msPerDay) : -fmod(msPerDay - t, msPerDay);
double localTZA = DateTimeInfo::localTZA();
double t = DaylightSavingTA(date) + localTZA;
t = (localTZA >= 0) ? fmod(t, msPerDay) : -fmod(msPerDay - t, msPerDay);
return t;
}
/* ES5 15.9.1.9. */
static double
LocalTime(double t, DateTimeInfo* dtInfo)
LocalTime(double t)
{
return t + AdjustTime(t, dtInfo);
return t + AdjustTime(t);
}
static double
UTC(double t, DateTimeInfo* dtInfo)
UTC(double t)
{
return t - AdjustTime(t - dtInfo->localTZA(), dtInfo);
return t - AdjustTime(t - DateTimeInfo::localTZA());
}
/* ES5 15.9.1.10. */
@ -764,7 +765,7 @@ DaysInMonth(int year, int month)
*/
template <typename CharT>
static bool
ParseISODate(const CharT* s, size_t length, ClippedTime* result, DateTimeInfo* dtInfo)
ParseISODate(const CharT* s, size_t length, ClippedTime* result)
{
size_t i = 0;
int tzMul = 1;
@ -864,7 +865,7 @@ ParseISODate(const CharT* s, size_t length, ClippedTime* result, DateTimeInfo* d
MakeTime(hour, min, sec, frac * 1000.0));
if (isLocalTime)
msec = UTC(msec, dtInfo);
msec = UTC(msec);
else
msec -= tzMul * (tzHour * msPerHour + tzMin * msPerMinute);
@ -879,9 +880,9 @@ ParseISODate(const CharT* s, size_t length, ClippedTime* result, DateTimeInfo* d
template <typename CharT>
static bool
ParseDate(const CharT* s, size_t length, ClippedTime* result, DateTimeInfo* dtInfo)
ParseDate(const CharT* s, size_t length, ClippedTime* result)
{
if (ParseISODate(s, length, result, dtInfo))
if (ParseISODate(s, length, result))
return true;
if (length == 0)
@ -1144,7 +1145,7 @@ ParseDate(const CharT* s, size_t length, ClippedTime* result, DateTimeInfo* dtIn
double msec = MakeDate(MakeDay(year, mon, mday), MakeTime(hour, min, sec, 0));
if (tzOffset == -1) /* no time zone specified, have to use local */
msec = UTC(msec, dtInfo);
msec = UTC(msec);
else
msec += tzOffset * msPerMinute;
@ -1153,12 +1154,12 @@ ParseDate(const CharT* s, size_t length, ClippedTime* result, DateTimeInfo* dtIn
}
static bool
ParseDate(JSLinearString* s, ClippedTime* result, DateTimeInfo* dtInfo)
ParseDate(JSLinearString* s, ClippedTime* result)
{
AutoCheckCannotGC nogc;
return s->hasLatin1Chars()
? ParseDate(s->latin1Chars(nogc), s->length(), result, dtInfo)
: ParseDate(s->twoByteChars(nogc), s->length(), result, dtInfo);
? ParseDate(s->latin1Chars(nogc), s->length(), result)
: ParseDate(s->twoByteChars(nogc), s->length(), result);
}
static bool
@ -1179,7 +1180,7 @@ date_parse(JSContext* cx, unsigned argc, Value* vp)
return false;
ClippedTime result;
if (!ParseDate(linearStr, &result, &cx->runtime()->dateTimeInfo)) {
if (!ParseDate(linearStr, &result)) {
args.rval().setNaN();
return true;
}
@ -1219,17 +1220,17 @@ DateObject::setUTCTime(ClippedTime t, MutableHandleValue vp)
}
void
DateObject::fillLocalTimeSlots(DateTimeInfo* dtInfo)
DateObject::fillLocalTimeSlots()
{
/* Check if the cache is already populated. */
if (!getReservedSlot(LOCAL_TIME_SLOT).isUndefined() &&
getReservedSlot(TZA_SLOT).toDouble() == dtInfo->localTZA())
getReservedSlot(TZA_SLOT).toDouble() == DateTimeInfo::localTZA())
{
return;
}
/* Remember timezone used to generate the local cache. */
setReservedSlot(TZA_SLOT, DoubleValue(dtInfo->localTZA()));
setReservedSlot(TZA_SLOT, DoubleValue(DateTimeInfo::localTZA()));
double utcTime = UTCTime().toNumber();
@ -1239,7 +1240,7 @@ DateObject::fillLocalTimeSlots(DateTimeInfo* dtInfo)
return;
}
double localTime = LocalTime(utcTime, dtInfo);
double localTime = LocalTime(utcTime);
setReservedSlot(LOCAL_TIME_SLOT, DoubleValue(localTime));
@ -1349,9 +1350,9 @@ DateObject::fillLocalTimeSlots(DateTimeInfo* dtInfo)
}
inline double
DateObject::cachedLocalTime(DateTimeInfo* dtInfo)
DateObject::cachedLocalTime()
{
fillLocalTimeSlots(dtInfo);
fillLocalTimeSlots();
return getReservedSlot(LOCAL_TIME_SLOT).toDouble();
}
@ -1382,7 +1383,7 @@ date_getTime(JSContext* cx, unsigned argc, Value* vp)
DateObject::getYear_impl(JSContext* cx, const CallArgs& args)
{
DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
dateObj->fillLocalTimeSlots();
Value yearVal = dateObj->getReservedSlot(LOCAL_YEAR_SLOT);
if (yearVal.isInt32()) {
@ -1407,7 +1408,7 @@ date_getYear(JSContext* cx, unsigned argc, Value* vp)
DateObject::getFullYear_impl(JSContext* cx, const CallArgs& args)
{
DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
dateObj->fillLocalTimeSlots();
args.rval().set(dateObj->getReservedSlot(LOCAL_YEAR_SLOT));
return true;
@ -1442,7 +1443,7 @@ date_getUTCFullYear(JSContext* cx, unsigned argc, Value* vp)
DateObject::getMonth_impl(JSContext* cx, const CallArgs& args)
{
DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
dateObj->fillLocalTimeSlots();
args.rval().set(dateObj->getReservedSlot(LOCAL_MONTH_SLOT));
return true;
@ -1474,7 +1475,7 @@ date_getUTCMonth(JSContext* cx, unsigned argc, Value* vp)
DateObject::getDate_impl(JSContext* cx, const CallArgs& args)
{
DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
dateObj->fillLocalTimeSlots();
args.rval().set(dateObj->getReservedSlot(LOCAL_DATE_SLOT));
return true;
@ -1509,7 +1510,7 @@ date_getUTCDate(JSContext* cx, unsigned argc, Value* vp)
DateObject::getDay_impl(JSContext* cx, const CallArgs& args)
{
DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
dateObj->fillLocalTimeSlots();
args.rval().set(dateObj->getReservedSlot(LOCAL_DAY_SLOT));
return true;
@ -1544,7 +1545,7 @@ date_getUTCDay(JSContext* cx, unsigned argc, Value* vp)
DateObject::getHours_impl(JSContext* cx, const CallArgs& args)
{
DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
dateObj->fillLocalTimeSlots();
args.rval().set(dateObj->getReservedSlot(LOCAL_HOURS_SLOT));
return true;
@ -1579,7 +1580,7 @@ date_getUTCHours(JSContext* cx, unsigned argc, Value* vp)
DateObject::getMinutes_impl(JSContext* cx, const CallArgs& args)
{
DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
dateObj->fillLocalTimeSlots();
args.rval().set(dateObj->getReservedSlot(LOCAL_MINUTES_SLOT));
return true;
@ -1616,7 +1617,7 @@ date_getUTCMinutes(JSContext* cx, unsigned argc, Value* vp)
DateObject::getUTCSeconds_impl(JSContext* cx, const CallArgs& args)
{
DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
dateObj->fillLocalTimeSlots();
args.rval().set(dateObj->getReservedSlot(LOCAL_SECONDS_SLOT));
return true;
@ -1654,7 +1655,7 @@ DateObject::getTimezoneOffset_impl(JSContext* cx, const CallArgs& args)
{
DateObject* dateObj = &args.thisv().toObject().as<DateObject>();
double utctime = dateObj->UTCTime().toNumber();
double localtime = dateObj->cachedLocalTime(&cx->runtime()->dateTimeInfo);
double localtime = dateObj->cachedLocalTime();
/*
* Return the time zone offset in minutes for the current locale that is
@ -1727,25 +1728,27 @@ GetMinsOrDefault(JSContext* cx, const CallArgs& args, unsigned i, double t, doub
return ToNumber(cx, args[i], mins);
}
/* ES5 15.9.5.28. */
/* ES6 20.3.4.23. */
MOZ_ALWAYS_INLINE bool
date_setMilliseconds_impl(JSContext* cx, const CallArgs& args)
{
Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
/* Step 1. */
double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
// Steps 1-2.
double t = LocalTime(dateObj->UTCTime().toNumber());
/* Step 2. */
double milli;
if (!ToNumber(cx, args.get(0), &milli))
// Steps 3-4.
double ms;
if (!ToNumber(cx, args.get(0), &ms))
return false;
double time = MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), milli);
/* Step 3. */
ClippedTime u = TimeClip(UTC(MakeDate(Day(t), time), &cx->runtime()->dateTimeInfo));
// Step 5.
double time = MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms);
/* Steps 4-5. */
// Step 6.
ClippedTime u = TimeClip(UTC(MakeDate(Day(t), time)));
// Steps 7-8.
dateObj->setUTCTime(u, args.rval());
return true;
}
@ -1793,31 +1796,31 @@ date_setSeconds_impl(JSContext* cx, const CallArgs& args)
{
Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
/* Step 1. */
double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
// Steps 1-2.
double t = LocalTime(dateObj->UTCTime().toNumber());
/* Step 2. */
// Steps 3-4.
double s;
if (!ToNumber(cx, args.get(0), &s))
return false;
/* Step 3. */
// Steps 5-6.
double milli;
if (!GetMsecsOrDefault(cx, args, 1, t, &milli))
return false;
/* Step 4. */
// Step 7.
double date = MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli));
/* Step 5. */
ClippedTime u = TimeClip(UTC(date, &cx->runtime()->dateTimeInfo));
// Step 8.
ClippedTime u = TimeClip(UTC(date));
/* Steps 6-7. */
// Step 9.
dateObj->setUTCTime(u, args.rval());
return true;
}
/* ES5 15.9.5.31. */
/* ES6 20.3.4.26. */
static bool
date_setSeconds(JSContext* cx, unsigned argc, Value* vp)
{
@ -1867,39 +1870,40 @@ date_setMinutes_impl(JSContext* cx, const CallArgs& args)
{
Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
/* Step 1. */
double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
// Steps 1-2.
double t = LocalTime(dateObj->UTCTime().toNumber());
/* Step 2. */
// Steps 3-4.
double m;
if (!ToNumber(cx, args.get(0), &m))
return false;
/* Step 3. */
// Steps 5-6.
double s;
if (!GetSecsOrDefault(cx, args, 1, t, &s))
return false;
/* Step 4. */
// Steps 7-8.
double milli;
if (!GetMsecsOrDefault(cx, args, 2, t, &milli))
return false;
/* Step 5. */
// Step 9.
double date = MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli));
/* Step 6. */
ClippedTime u = TimeClip(UTC(date, &cx->runtime()->dateTimeInfo));
// Step 10.
ClippedTime u = TimeClip(UTC(date));
/* Steps 7-8. */
// Steps 11-12.
dateObj->setUTCTime(u, args.rval());
return true;
}
/* ES5 15.9.5.33. */
/* ES6 20.3.4.24. */
static bool
date_setMinutes(JSContext* cx, unsigned argc, Value* vp)
{
// Steps 1-2 (the effectful parts).
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod<IsDate, date_setMinutes_impl>(cx, args);
}
@ -1951,36 +1955,36 @@ date_setHours_impl(JSContext* cx, const CallArgs& args)
{
Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
/* Step 1. */
double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
// Steps 1-2.
double t = LocalTime(dateObj->UTCTime().toNumber());
/* Step 2. */
// Steps 3-4.
double h;
if (!ToNumber(cx, args.get(0), &h))
return false;
/* Step 3. */
// Steps 5-6.
double m;
if (!GetMinsOrDefault(cx, args, 1, t, &m))
return false;
/* Step 4. */
// Steps 7-8.
double s;
if (!GetSecsOrDefault(cx, args, 2, t, &s))
return false;
/* Step 5. */
// Steps 9-10.
double milli;
if (!GetMsecsOrDefault(cx, args, 3, t, &milli))
return false;
/* Step 6. */
// Step 11.
double date = MakeDate(Day(t), MakeTime(h, m, s, milli));
/* Step 6. */
ClippedTime u = TimeClip(UTC(date, &cx->runtime()->dateTimeInfo));
// Step 12.
ClippedTime u = TimeClip(UTC(date));
/* Steps 7-8. */
// Steps 13-14.
dateObj->setUTCTime(u, args.rval());
return true;
}
@ -2046,7 +2050,7 @@ date_setDate_impl(JSContext* cx, const CallArgs& args)
Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
/* Step 1. */
double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
double t = LocalTime(dateObj->UTCTime().toNumber());
/* Step 2. */
double date;
@ -2057,7 +2061,7 @@ date_setDate_impl(JSContext* cx, const CallArgs& args)
double newDate = MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t));
/* Step 4. */
ClippedTime u = TimeClip(UTC(newDate, &cx->runtime()->dateTimeInfo));
ClippedTime u = TimeClip(UTC(newDate));
/* Steps 5-6. */
dateObj->setUTCTime(u, args.rval());
@ -2130,7 +2134,7 @@ date_setMonth_impl(JSContext* cx, const CallArgs& args)
Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
/* Step 1. */
double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
double t = LocalTime(dateObj->UTCTime().toNumber());
/* Step 2. */
double m;
@ -2146,7 +2150,7 @@ date_setMonth_impl(JSContext* cx, const CallArgs& args)
double newDate = MakeDate(MakeDay(YearFromTime(t), m, date), TimeWithinDay(t));
/* Step 5. */
ClippedTime u = TimeClip(UTC(newDate, &cx->runtime()->dateTimeInfo));
ClippedTime u = TimeClip(UTC(newDate));
/* Steps 6-7. */
dateObj->setUTCTime(u, args.rval());
@ -2198,12 +2202,12 @@ date_setUTCMonth(JSContext* cx, unsigned argc, Value* vp)
}
static double
ThisLocalTimeOrZero(Handle<DateObject*> dateObj, DateTimeInfo* dtInfo)
ThisLocalTimeOrZero(Handle<DateObject*> dateObj)
{
double t = dateObj->UTCTime().toNumber();
if (IsNaN(t))
return +0;
return LocalTime(t, dtInfo);
return LocalTime(t);
}
static double
@ -2220,7 +2224,7 @@ date_setFullYear_impl(JSContext* cx, const CallArgs& args)
Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
/* Step 1. */
double t = ThisLocalTimeOrZero(dateObj, &cx->runtime()->dateTimeInfo);
double t = ThisLocalTimeOrZero(dateObj);
/* Step 2. */
double y;
@ -2241,7 +2245,7 @@ date_setFullYear_impl(JSContext* cx, const CallArgs& args)
double newDate = MakeDate(MakeDay(y, m, date), TimeWithinDay(t));
/* Step 6. */
ClippedTime u = TimeClip(UTC(newDate, &cx->runtime()->dateTimeInfo));
ClippedTime u = TimeClip(UTC(newDate));
/* Steps 7-8. */
dateObj->setUTCTime(u, args.rval());
@ -2304,7 +2308,7 @@ date_setYear_impl(JSContext* cx, const CallArgs& args)
Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
/* Step 1. */
double t = ThisLocalTimeOrZero(dateObj, &cx->runtime()->dateTimeInfo);
double t = ThisLocalTimeOrZero(dateObj);
/* Step 2. */
double y;
@ -2326,7 +2330,7 @@ date_setYear_impl(JSContext* cx, const CallArgs& args)
double day = MakeDay(yint, MonthFromTime(t), DateFromTime(t));
/* Step 6. */
double u = UTC(MakeDate(day, TimeWithinDay(t)), &cx->runtime()->dateTimeInfo);
double u = UTC(MakeDate(day, TimeWithinDay(t)));
/* Steps 7-8. */
dateObj->setUTCTime(TimeClip(u), args.rval());
@ -2505,7 +2509,7 @@ date_toJSON(JSContext* cx, unsigned argc, Value* vp)
/* for Date.toLocaleFormat; interface to PRMJTime date struct.
*/
static void
new_explode(double timeval, PRMJTime* split, DateTimeInfo* dtInfo)
new_explode(double timeval, PRMJTime* split)
{
double year = YearFromTime(timeval);
@ -2521,7 +2525,7 @@ new_explode(double timeval, PRMJTime* split, DateTimeInfo* dtInfo)
/* not sure how this affects things, but it doesn't seem
to matter. */
split->tm_isdst = (DaylightSavingTA(timeval, dtInfo) != 0);
split->tm_isdst = (DaylightSavingTA(timeval) != 0);
}
typedef enum formatspec {
@ -2543,11 +2547,11 @@ date_format(JSContext* cx, double date, formatspec format, MutableHandleValue rv
} else {
MOZ_ASSERT(NumbersAreIdentical(TimeClip(date).toDouble(), date));
double local = LocalTime(date, &cx->runtime()->dateTimeInfo);
double local = LocalTime(date);
/* offset from GMT in minutes. The offset includes daylight savings,
if it applies. */
int minutes = (int) floor(AdjustTime(date, &cx->runtime()->dateTimeInfo) / msPerMinute);
int minutes = (int) floor(AdjustTime(date) / msPerMinute);
/* map 510 minutes to 0830 hours */
int offset = (minutes / 60) * 100 + minutes % 60;
@ -2563,7 +2567,7 @@ date_format(JSContext* cx, double date, formatspec format, MutableHandleValue rv
/* get a timezone string from the OS to include as a
comment. */
new_explode(date, &split, &cx->runtime()->dateTimeInfo);
new_explode(date, &split);
if (PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &split) != 0) {
/* Decide whether to use the resulting timezone string.
@ -2653,9 +2657,9 @@ ToLocaleFormatHelper(JSContext* cx, HandleObject obj, const char* format, Mutabl
JS_snprintf(buf, sizeof buf, js_NaN_date_str);
} else {
int result_len;
double local = LocalTime(utctime, &cx->runtime()->dateTimeInfo);
double local = LocalTime(utctime);
PRMJTime split;
new_explode(local, &split, &cx->runtime()->dateTimeInfo);
new_explode(local, &split);
/* Let PRMJTime format it. */
result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split);
@ -2673,7 +2677,7 @@ ToLocaleFormatHelper(JSContext* cx, HandleObject obj, const char* format, Mutabl
/* ...but not if starts with 4-digit year, like 2022/3/11. */
!(isdigit(buf[0]) && isdigit(buf[1]) &&
isdigit(buf[2]) && isdigit(buf[3]))) {
double localtime = obj->as<DateObject>().cachedLocalTime(&cx->runtime()->dateTimeInfo);
double localtime = obj->as<DateObject>().cachedLocalTime();
int year = IsNaN(localtime) ? 0 : (int) YearFromTime(localtime);
JS_snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2),
"%d", year);
@ -3070,7 +3074,7 @@ DateOneArgument(JSContext* cx, const CallArgs& args)
if (!linearStr)
return false;
if (!ParseDate(linearStr, &t, &cx->runtime()->dateTimeInfo))
if (!ParseDate(linearStr, &t))
t = ClippedTime::invalid();
} else {
double d;
@ -3159,7 +3163,7 @@ DateMultipleArguments(JSContext* cx, const CallArgs& args)
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 NewDateObject(cx, args, TimeClip(UTC(finalDate)));
}
return ToDateString(cx, args, NowAsMillis());
@ -3271,7 +3275,7 @@ js::NewDateObject(JSContext* cx, int year, int mon, int mday,
{
MOZ_ASSERT(mon < 12);
double msec_time = MakeDate(MakeDay(year, mon, mday), MakeTime(hour, min, sec, 0.0));
return NewDateObjectMsec(cx, TimeClip(UTC(msec_time, &cx->runtime()->dateTimeInfo)));
return NewDateObjectMsec(cx, TimeClip(UTC(msec_time)));
}
JS_FRIEND_API(bool)

View File

@ -114,6 +114,7 @@ EXPORTS.js += [
'../public/HashTable.h',
'../public/HeapAPI.h',
'../public/Id.h',
'../public/Initialization.h',
'../public/LegacyIntTypes.h',
'../public/MemoryMetrics.h',
'../public/Principals.h',
@ -362,6 +363,7 @@ SOURCES += [
'jsatom.cpp',
'jsmath.cpp',
'jsutil.cpp',
'vm/Initialization.cpp',
]
if CONFIG['JS_POSIX_NSPR']:

View File

@ -67,6 +67,7 @@
#include "jit/OptimizationTracking.h"
#include "js/Debug.h"
#include "js/GCAPI.h"
#include "js/Initialization.h"
#include "js/StructuredClone.h"
#include "js/TrackedOptimizationInfo.h"
#include "perf/jsperf.h"

View File

@ -14,8 +14,6 @@
namespace js {
class DateTimeInfo;
class DateObject : public NativeObject
{
static const uint32_t UTC_TIME_SLOT = 0;
@ -58,12 +56,12 @@ class DateObject : public NativeObject
void setUTCTime(JS::ClippedTime t);
void setUTCTime(JS::ClippedTime t, MutableHandleValue vp);
inline double cachedLocalTime(DateTimeInfo* dtInfo);
inline double cachedLocalTime();
// Cache the local time, year, month, and so forth of the object.
// If UTC time is not finite (e.g., NaN), the local time
// slots will be set to the UTC time without conversion.
void fillLocalTimeSlots(DateTimeInfo* dtInfo);
void fillLocalTimeSlots();
static MOZ_ALWAYS_INLINE bool getTime_impl(JSContext* cx, const CallArgs& args);
static MOZ_ALWAYS_INLINE bool getYear_impl(JSContext* cx, const CallArgs& args);

View File

@ -10,8 +10,19 @@
#include "jsutil.h"
#include "js/Date.h"
#if ENABLE_INTL_API
#include "unicode/timezone.h"
#endif
using mozilla::UnspecifiedNaN;
/* static */ js::DateTimeInfo
js::DateTimeInfo::instance;
/* static */ mozilla::Atomic<bool, mozilla::ReleaseAcquire>
js::DateTimeInfo::AcquireLock::spinLock;
static bool
ComputeLocalTime(time_t local, struct tm* ptm)
{
@ -131,7 +142,7 @@ UTCToLocalStandardOffsetSeconds()
}
void
js::DateTimeInfo::updateTimeZoneAdjustment()
js::DateTimeInfo::internalUpdateTimeZoneAdjustment()
{
/*
* The difference between local standard time and UTC will never change for
@ -164,12 +175,19 @@ js::DateTimeInfo::updateTimeZoneAdjustment()
* negative numbers to ensure the first computation is always a cache miss and
* doesn't return a bogus offset.
*/
js::DateTimeInfo::DateTimeInfo()
/* static */ void
js::DateTimeInfo::init()
{
DateTimeInfo* dtInfo = &DateTimeInfo::instance;
MOZ_ASSERT(dtInfo->localTZA_ == 0,
"we should be initializing only once, and the static instance "
"should have started out zeroed");
// Set to a totally impossible TZA so that the comparison above will fail
// and all fields will be properly initialized.
localTZA_ = UnspecifiedNaN<double>();
updateTimeZoneAdjustment();
dtInfo->localTZA_ = UnspecifiedNaN<double>();
dtInfo->internalUpdateTimeZoneAdjustment();
}
int64_t
@ -201,7 +219,7 @@ js::DateTimeInfo::computeDSTOffsetMilliseconds(int64_t utcSeconds)
}
int64_t
js::DateTimeInfo::getDSTOffsetMilliseconds(int64_t utcMilliseconds)
js::DateTimeInfo::internalGetDSTOffsetMilliseconds(int64_t utcMilliseconds)
{
sanityCheck();
@ -288,3 +306,13 @@ js::DateTimeInfo::sanityCheck()
MOZ_ASSERT_IF(rangeStartSeconds != INT64_MIN,
rangeStartSeconds <= MaxUnixTimeT && rangeEndSeconds <= MaxUnixTimeT);
}
JS_PUBLIC_API(void)
JS::ResetTimeZone()
{
DateTimeInfo::updateTimeZoneAdjustment();
#if ENABLE_INTL_API && defined(ICU_TZ_HAS_RECREATE_DEFAULT)
icu::TimeZone::recreateDefault();
#endif
}

View File

@ -7,12 +7,17 @@
#ifndef vm_DateTime_h
#define vm_DateTime_h
#include "mozilla/Assertions.h"
#include "mozilla/Atomics.h"
#include "mozilla/Attributes.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/MathAlgorithms.h"
#include <stdint.h>
#include "js/Conversions.h"
#include "js/Date.h"
#include "js/Initialization.h"
#include "js/Value.h"
namespace js {
@ -88,23 +93,71 @@ const double EndOfTime = 8.64e15;
*/
class DateTimeInfo
{
public:
DateTimeInfo();
static DateTimeInfo instance;
// Date/time info is shared across all threads in DateTimeInfo::instance,
// for consistency with ICU's handling of its default time zone. Thus we
// need something to protect concurrent accesses.
//
// The spec implicitly assumes DST and time zone adjustment information
// never change in the course of a function -- sometimes even across
// reentrancy. So make critical sections as narrow as possible, and use a
// bog-standard spinlock with busy-waiting in case of contention for
// simplicity.
class MOZ_RAII AcquireLock
{
static mozilla::Atomic<bool, mozilla::ReleaseAcquire> spinLock;
public:
AcquireLock() {
while (!spinLock.compareExchange(false, true))
continue;
}
~AcquireLock() {
MOZ_ASSERT(spinLock, "spinlock should have been acquired");
spinLock = false;
}
};
friend bool ::JS_Init();
// Initialize global date/time tracking state. This operation occurs
// during, and is restricted to, SpiderMonkey initialization.
static void init();
public:
/*
* Get the DST offset in milliseconds at a UTC time. This is usually
* either 0 or |msPerSecond * SecondsPerHour|, but at least one exotic time
* zone (Lord Howe Island, Australia) has a fractional-hour offset, just to
* keep things interesting.
*/
int64_t getDSTOffsetMilliseconds(int64_t utcMilliseconds);
static int64_t getDSTOffsetMilliseconds(int64_t utcMilliseconds) {
AcquireLock lock;
void updateTimeZoneAdjustment();
return DateTimeInfo::instance.internalGetDSTOffsetMilliseconds(utcMilliseconds);
}
/* ES5 15.9.1.7. */
double localTZA() { return localTZA_; }
static double localTZA() {
AcquireLock lock;
return DateTimeInfo::instance.localTZA_;
}
private:
// We don't want anyone accidentally calling *only*
// DateTimeInfo::updateTimeZoneAdjustment() to respond to a system time
// zone change (missing the necessary poking of ICU as well), so ensure
// only JS::ResetTimeZone() can call this via access restrictions.
friend void JS::ResetTimeZone();
static void updateTimeZoneAdjustment() {
AcquireLock lock;
DateTimeInfo::instance.internalUpdateTimeZoneAdjustment();
}
/*
* The current local time zone adjustment, cached because retrieving this
* dynamically is Slow, and a certain venerable benchmark which shall not
@ -113,7 +166,7 @@ class DateTimeInfo
* SpiderMonkey occasionally and arbitrarily updates this value from the
* system time zone to attempt to keep this reasonably up-to-date. If
* temporary inaccuracy can't be tolerated, JSAPI clients may call
* JS_ClearDateCaches to forcibly sync this with the system time zone.
* JS::ResetTimeZone to forcibly sync this with the system time zone.
*/
double localTZA_;
@ -141,6 +194,9 @@ class DateTimeInfo
static const int64_t RangeExpansionAmount = 30 * SecondsPerDay;
int64_t internalGetDSTOffsetMilliseconds(int64_t utcMilliseconds);
void internalUpdateTimeZoneAdjustment();
void sanityCheck();
};

View File

@ -0,0 +1,166 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
/* SpiderMonkey initialization and shutdown code. */
#include "js/Initialization.h"
#include "mozilla/Assertions.h"
#include <ctype.h>
#include "jstypes.h"
#include "builtin/AtomicsObject.h"
#include "jit/ExecutableAllocator.h"
#include "jit/Ion.h"
#include "js/Utility.h"
#if ENABLE_INTL_API
#include "unicode/uclean.h"
#include "unicode/utypes.h"
#endif // ENABLE_INTL_API
#include "vm/DateTime.h"
#include "vm/HelperThreads.h"
#include "vm/Runtime.h"
#include "vm/Time.h"
#include "vm/TraceLogging.h"
using JS::detail::InitState;
using JS::detail::libraryInitState;
using js::FutexRuntime;
InitState JS::detail::libraryInitState;
#ifdef DEBUG
static unsigned
MessageParameterCount(const char* format)
{
unsigned numfmtspecs = 0;
for (const char* fmt = format; *fmt != '\0'; fmt++) {
if (*fmt == '{' && isdigit(fmt[1]))
++numfmtspecs;
}
return numfmtspecs;
}
static void
CheckMessageParameterCounts()
{
// Assert that each message format has the correct number of braced
// parameters.
# define MSG_DEF(name, count, exception, format) \
MOZ_ASSERT(MessageParameterCount(format) == count);
# include "js.msg"
# undef MSG_DEF
}
#endif /* DEBUG */
JS_PUBLIC_API(bool)
JS_Init(void)
{
MOZ_ASSERT(libraryInitState == InitState::Uninitialized,
"must call JS_Init once before any JSAPI operation except "
"JS_SetICUMemoryFunctions");
MOZ_ASSERT(!JSRuntime::hasLiveRuntimes(),
"how do we have live runtimes before JS_Init?");
PRMJ_NowInit();
#ifdef DEBUG
CheckMessageParameterCounts();
#endif
using js::TlsPerThreadData;
if (!TlsPerThreadData.initialized() && !TlsPerThreadData.init())
return false;
#if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
if (!js::oom::InitThreadType())
return false;
js::oom::SetThreadType(js::oom::THREAD_TYPE_MAIN);
#endif
js::jit::ExecutableAllocator::initStatic();
if (!js::jit::InitializeIon())
return false;
js::DateTimeInfo::init();
#if EXPOSE_INTL_API
UErrorCode err = U_ZERO_ERROR;
u_init(&err);
if (U_FAILURE(err))
return false;
#endif // EXPOSE_INTL_API
if (!js::CreateHelperThreadsState())
return false;
if (!FutexRuntime::initialize())
return false;
libraryInitState = InitState::Running;
return true;
}
JS_PUBLIC_API(void)
JS_ShutDown(void)
{
MOZ_ASSERT(libraryInitState == InitState::Running,
"JS_ShutDown must only be called after JS_Init and can't race with it");
#ifdef DEBUG
if (JSRuntime::hasLiveRuntimes()) {
// Gecko is too buggy to assert this just yet.
fprintf(stderr,
"WARNING: YOU ARE LEAKING THE WORLD (at least one JSRuntime "
"and everything alive inside it, that is) AT JS_ShutDown "
"TIME. FIX THIS!\n");
}
#endif
FutexRuntime::destroy();
js::DestroyHelperThreadsState();
#ifdef JS_TRACE_LOGGING
js::DestroyTraceLoggerThreadState();
js::DestroyTraceLoggerGraphState();
#endif
// The only difficult-to-address reason for the restriction that you can't
// call JS_Init/stuff/JS_ShutDown multiple times is the Windows PRMJ
// NowInit initialization code, which uses PR_CallOnce to initialize the
// PRMJ_Now subsystem. (For reinitialization to be permitted, we'd need to
// "reset" the called-once status -- doable, but more trouble than it's
// worth now.) Initializing that subsystem from JS_Init eliminates the
// problem, but initialization can take a comparatively long time (15ms or
// so), so we really don't want to do it in JS_Init, and we really do want
// to do it only when PRMJ_Now is eventually called.
PRMJ_NowShutdown();
#if EXPOSE_INTL_API
u_cleanup();
#endif // EXPOSE_INTL_API
libraryInitState = InitState::ShutDown;
}
JS_PUBLIC_API(bool)
JS_SetICUMemoryFunctions(JS_ICUAllocFn allocFn, JS_ICUReallocFn reallocFn, JS_ICUFreeFn freeFn)
{
MOZ_ASSERT(libraryInitState == InitState::Uninitialized,
"must call JS_SetICUMemoryFunctions before any other JSAPI "
"operation (including JS_Init)");
#if EXPOSE_INTL_API
UErrorCode status = U_ZERO_ERROR;
u_setMemoryFunctions(/* context = */ nullptr, allocFn, reallocFn, freeFn, &status);
return U_SUCCESS(status);
#else
return true;
#endif
}

View File

@ -45,6 +45,7 @@
#include "jit/JitCompartment.h"
#include "jit/mips32/Simulator-mips32.h"
#include "jit/PcScriptCache.h"
#include "js/Date.h"
#include "js/MemoryMetrics.h"
#include "js/SliceBudget.h"
#include "vm/Debugger.h"
@ -333,7 +334,7 @@ JSRuntime::init(uint32_t maxbytes, uint32_t maxNurseryBytes)
if (!InitRuntimeNumberState(this))
return false;
dateTimeInfo.updateTimeZoneAdjustment();
JS::ResetTimeZone();
#ifdef JS_SIMULATOR
simulator_ = js::jit::Simulator::Create();

View File

@ -1205,7 +1205,6 @@ struct JSRuntime : public JS::shadow::Runtime,
js::LazyScriptCache lazyScriptCache;
js::CompressedSourceSet compressedSourceSet;
js::DateTimeInfo dateTimeInfo;
// Pool of maps used during parse/emit. This may be modified by threads
// with an ExclusiveContext and requires a lock. Active compilations

View File

@ -1263,7 +1263,7 @@ intrinsic_LocalTZA(JSContext* cx, unsigned argc, Value* vp)
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 0, "the LocalTZA intrinsic takes no arguments");
args.rval().setDouble(cx->runtime()->dateTimeInfo.localTZA());
args.rval().setDouble(DateTimeInfo::localTZA());
return true;
}

View File

@ -155,6 +155,7 @@ extern nsresult nsStringInputStreamConstructor(nsISupports*, REFNSIID, void**);
#include "GeckoProfiler.h"
#include "jsapi.h"
#include "js/Initialization.h"
#include "gfxPlatform.h"