mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1208808 - Move time zone adjustment information out of JSRuntime, into global state protected by a spinlock. r=till
This commit is contained in:
parent
38628058bb
commit
c810badd97
@ -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();
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
||||
|
84
js/public/Initialization.h
Normal file
84
js/public/Initialization.h
Normal 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 */
|
@ -9,6 +9,7 @@
|
||||
#include "gdb-tests.h"
|
||||
#include "jsapi.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "js/Initialization.h"
|
||||
|
||||
using namespace JS;
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "js/Initialization.h"
|
||||
#include "js/RootingAPI.h"
|
||||
|
||||
JSAPITest* JSAPITest::list;
|
||||
|
163
js/src/jsapi.cpp
163
js/src/jsapi.cpp
@ -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
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
/************************************************************************/
|
||||
|
||||
/*
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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']:
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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();
|
||||
};
|
||||
|
||||
|
166
js/src/vm/Initialization.cpp
Normal file
166
js/src/vm/Initialization.cpp
Normal 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
|
||||
}
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -155,6 +155,7 @@ extern nsresult nsStringInputStreamConstructor(nsISupports*, REFNSIID, void**);
|
||||
#include "GeckoProfiler.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "js/Initialization.h"
|
||||
|
||||
#include "gfxPlatform.h"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user