Bug 1346211 - Part 2: Split JS::ResetTimeZone into an external and internal implementation. r=Waldo
authorAndré Bargull <andre.bargull@gmail.com>
Thu, 16 Aug 2018 09:13:18 -0700
changeset 487323 62d58886e8d759f52c871a82dbe4d9c2a266c0c7
parent 487322 b5c5bbed871f65fea01fbc25047c1865a23d6553
child 487324 7cad19d2d2d9368d130b78f2d8cf18ecf39b3c9b
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersWaldo
bugs1346211
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1346211 - Part 2: Split JS::ResetTimeZone into an external and internal implementation. r=Waldo
js/public/Date.h
js/src/builtin/intl/DateTimeFormat.cpp
js/src/jsdate.cpp
js/src/jsdate.h
js/src/tests/non262/Date/reset-time-zone-cache-same-offset.js
js/src/vm/DateTime.cpp
js/src/vm/DateTime.h
js/src/vm/Realm.cpp
js/src/vm/Runtime.cpp
js/src/vm/Runtime.h
toolkit/components/resistfingerprinting/nsRFPService.cpp
--- a/js/public/Date.h
+++ b/js/public/Date.h
@@ -38,22 +38,22 @@
 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.
+ * Left to its own devices, SpiderMonkey itself may occasionally try to detect
+ * 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);
 
 /*
--- a/js/src/builtin/intl/DateTimeFormat.cpp
+++ b/js/src/builtin/intl/DateTimeFormat.cpp
@@ -14,16 +14,17 @@
 #include "jsfriendapi.h"
 
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "builtin/intl/SharedIntlData.h"
 #include "builtin/intl/TimeZoneDataGenerated.h"
 #include "gc/FreeOp.h"
+#include "vm/DateTime.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 #include "vm/Runtime.h"
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -3254,18 +3254,18 @@ DateMultipleArguments(JSContext* cx, con
 
         // Steps 3q-t.
         return NewDateObject(cx, args, TimeClip(UTC(finalDate)));
     }
 
     return ToDateString(cx, args, NowAsMillis(cx));
 }
 
-bool
-js::DateConstructor(JSContext* cx, unsigned argc, Value* vp)
+static bool
+DateConstructor(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() == 0)
         return DateNoArguments(cx, args);
 
     if (args.length() == 1)
         return DateOneArgument(cx, args);
--- a/js/src/jsdate.h
+++ b/js/src/jsdate.h
@@ -12,18 +12,16 @@
 #define jsdate_h
 
 #include "jstypes.h"
 
 #include "js/Date.h"
 #include "js/RootingAPI.h"
 #include "js/TypeDecls.h"
 
-#include "vm/DateTime.h"
-
 namespace js {
 
 /*
  * These functions provide a C interface to the date/time object
  */
 
 /*
  * Construct a new Date Object from a time value given in milliseconds UTC
@@ -35,22 +33,17 @@ NewDateObjectMsec(JSContext* cx, JS::Cli
 /*
  * Construct a new Date Object from an exploded local time value.
  *
  * Assert that mon < 12 to help catch off-by-one user errors, which are common
  * due to the 0-based month numbering copied into JS from Java (java.util.Date
  * in 1995).
  */
 extern JS_FRIEND_API(JSObject*)
-NewDateObject(JSContext* cx, int year, int mon, int mday,
-              int hour, int min, int sec);
-
-/* Date constructor native. Exposed only so the JIT can know its address. */
-bool
-DateConstructor(JSContext* cx, unsigned argc, JS::Value* vp);
+NewDateObject(JSContext* cx, int year, int mon, int mday, int hour, int min, int sec);
 
 /* Date methods exposed so they can be installed in the self-hosting global. */
 bool
 date_now(JSContext* cx, unsigned argc, JS::Value* vp);
 
 bool
 date_valueOf(JSContext* cx, unsigned argc, JS::Value* vp);
 
new file mode 100644
--- /dev/null
+++ b/js/src/tests/non262/Date/reset-time-zone-cache-same-offset.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(xulRuntime.OS=="WINNT") -- Windows doesn't accept IANA names for the TZ env variable
+
+const testCases = [
+    {
+        timeZone: "Europe/London",
+        string: "Tue Aug 14 2018 00:00:00 GMT+0100 (BST)",
+        localeString: "8/14/2018, 12:00:00 AM GMT+1",
+    },
+    {
+        timeZone: "UTC",
+        string: "Tue Aug 14 2018 00:00:00 GMT+0000 (UTC)",
+        localeString: "8/14/2018, 12:00:00 AM UTC",
+    },
+];
+
+// Repeat twice to test both transitions (Europe/London -> UTC and UTC -> Europe/London).
+for (let i = 0; i < 2; ++i) {
+    for (let {timeZone, string, localeString} of testCases) {
+        setTimeZone(timeZone);
+
+        let dt = new Date(2018, 8 - 1, 14);
+        assertEq(dt.toString(), string);
+        assertEq(dt.toLocaleString("en-US", {timeZoneName: "short"}), localeString);
+    }
+}
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
--- a/js/src/vm/DateTime.cpp
+++ b/js/src/vm/DateTime.cpp
@@ -23,18 +23,16 @@
 #include "unicode/timezone.h"
 #if defined(XP_WIN)
 #include "unicode/unistr.h"
 #endif
 #endif /* ENABLE_INTL_API */
 
 #include "vm/MutexIDs.h"
 
-using mozilla::UnspecifiedNaN;
-
 static bool
 ComputeLocalTime(time_t local, struct tm* ptm)
 {
 #if defined(_WIN32)
     return localtime_s(ptm, &local) == 0;
 #elif defined(HAVE_LOCALTIME_R)
     return localtime_r(&local, ptm);
 #else
@@ -140,26 +138,26 @@ UTCToLocalStandardOffsetSeconds()
         return (SecondsPerDay + local_secs) - utc_secs;
 
     // Otherwise we have more local seconds, so move the UTC seconds into the
     // local seconds' frame of reference and then subtract.
     return local_secs - (utc_secs + SecondsPerDay);
 }
 
 void
-js::DateTimeInfo::internalUpdateTimeZoneAdjustment()
+js::DateTimeInfo::internalUpdateTimeZoneAdjustment(ResetTimeZoneMode mode)
 {
     /*
      * The difference between local standard time and UTC will never change for
      * a given time zone.
      */
     utcToLocalStandardOffsetSeconds = UTCToLocalStandardOffsetSeconds();
 
     double newTZA = utcToLocalStandardOffsetSeconds * msPerSecond;
-    if (newTZA == localTZA_)
+    if (mode == ResetTimeZoneMode::DontResetIfOffsetUnchanged && newTZA == localTZA_)
         return;
 
     localTZA_ = newTZA;
 
     /*
      * The initial range values are carefully chosen to result in a cache miss
      * on first use given the range of possible values.  Be careful to keep
      * these values and the caching algorithm in sync!
@@ -169,21 +167,17 @@ js::DateTimeInfo::internalUpdateTimeZone
     oldOffsetMilliseconds = 0;
     oldRangeStartSeconds = oldRangeEndSeconds = INT64_MIN;
 
     sanityCheck();
 }
 
 js::DateTimeInfo::DateTimeInfo()
 {
-    // Set to an impossible TZA so that the comparison in
-    // |internalUpdateTimeZoneAdjustment()| initially fails, causing the
-    // remaining fields to be properly initialized at first adjustment.
-    localTZA_ = UnspecifiedNaN<double>();
-    internalUpdateTimeZoneAdjustment();
+    internalUpdateTimeZoneAdjustment(ResetTimeZoneMode::ResetEvenIfOffsetUnchaged);
 }
 
 int64_t
 js::DateTimeInfo::computeDSTOffsetMilliseconds(int64_t utcSeconds)
 {
     MOZ_ASSERT(utcSeconds >= 0);
     MOZ_ASSERT(utcSeconds <= MaxUnixTimeT);
 
@@ -330,24 +324,31 @@ js::FinishDateTimeState()
 {
     js_delete(IcuTimeZoneState);
     IcuTimeZoneState = nullptr;
 
     js_delete(DateTimeInfo::instance);
     DateTimeInfo::instance = nullptr;
 }
 
+void
+js::ResetTimeZoneInternal(ResetTimeZoneMode mode)
+{
+    js::DateTimeInfo::updateTimeZoneAdjustment(mode);
+
+#if ENABLE_INTL_API && defined(ICU_TZ_HAS_RECREATE_DEFAULT)
+    auto guard = js::IcuTimeZoneState->lock();
+    guard.get() = js::IcuTimeZoneStatus::NeedsUpdate;
+#endif
+}
+
 JS_PUBLIC_API(void)
 JS::ResetTimeZone()
 {
-    js::DateTimeInfo::updateTimeZoneAdjustment();
-
-#if ENABLE_INTL_API && defined(ICU_TZ_HAS_RECREATE_DEFAULT)
-    js::IcuTimeZoneState->lock().get() = js::IcuTimeZoneStatus::NeedsUpdate;
-#endif
+    js::ResetTimeZoneInternal(js::ResetTimeZoneMode::ResetEvenIfOffsetUnchaged);
 }
 
 #if defined(XP_WIN)
 static bool
 IsOlsonCompatibleWindowsTimeZoneId(const char* tz)
 {
     // ICU ignores the TZ environment variable on Windows and instead directly
     // invokes Win API functions to retrieve the current time zone. But since
--- a/js/src/vm/DateTime.h
+++ b/js/src/vm/DateTime.h
@@ -4,25 +4,19 @@
  * 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/. */
 
 #ifndef vm_DateTime_h
 #define vm_DateTime_h
 
 #include "mozilla/Assertions.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"
 #include "threading/ExclusiveData.h"
 
 namespace js {
 
 /* Constants defined by ES5 15.9.1.10. */
 const double HoursPerDay = 24;
 const double MinutesPerHour = 60;
 const double SecondsPerMinute = 60;
@@ -46,16 +40,30 @@ const double StartOfTime = -8.64e15;
 const double EndOfTime = 8.64e15;
 
 extern bool
 InitDateTimeState();
 
 extern void
 FinishDateTimeState();
 
+enum class ResetTimeZoneMode : bool {
+  DontResetIfOffsetUnchanged,
+  ResetEvenIfOffsetUnchaged,
+};
+
+/**
+ * Engine-internal variant of JS::ResetTimeZone with an additional flag to
+ * control whether to forcibly reset all time zone data (this is the default
+ * behavior when calling JS::ResetTimeZone) or to try to reuse the previous
+ * time zone data.
+ */
+extern void
+ResetTimeZoneInternal(ResetTimeZoneMode mode);
+
 /*
  * Stores date/time information, particularly concerning the current local
  * time zone, and implements a small cache for daylight saving time offset
  * computation.
  *
  * The basic idea is premised upon this fact: the DST offset never changes more
  * than once in any thirty-day period.  If we know the offset at t_0 is o_0,
  * the offset at [t_1, t_2] is also o_0, where t_1 + 3_0 days == t_2,
@@ -128,22 +136,22 @@ class DateTimeInfo
         auto guard = instance->lock();
         return guard->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();
+    // only js::ResetTimeZoneInternal() can call this via access restrictions.
+    friend void js::ResetTimeZoneInternal(ResetTimeZoneMode);
 
-    static void updateTimeZoneAdjustment() {
+    static void updateTimeZoneAdjustment(ResetTimeZoneMode mode) {
         auto guard = instance->lock();
-        guard->internalUpdateTimeZoneAdjustment();
+        guard->internalUpdateTimeZoneAdjustment(mode);
     }
 
     /*
      * The current local time zone adjustment, cached because retrieving this
      * dynamically is Slow, and a certain venerable benchmark which shall not
      * be named depends on it being fast.
      *
      * SpiderMonkey occasionally and arbitrarily updates this value from the
@@ -173,17 +181,17 @@ class DateTimeInfo
      */
     int32_t utcToLocalStandardOffsetSeconds;
 
     static const int64_t MaxUnixTimeT = 2145859200; /* time_t 12/31/2037 */
 
     static const int64_t RangeExpansionAmount = 30 * SecondsPerDay;
 
     int64_t internalGetDSTOffsetMilliseconds(int64_t utcMilliseconds);
-    void internalUpdateTimeZoneAdjustment();
+    void internalUpdateTimeZoneAdjustment(ResetTimeZoneMode mode);
 
     void sanityCheck();
 };
 
 enum class IcuTimeZoneStatus { Valid, NeedsUpdate };
 
 extern ExclusiveData<IcuTimeZoneStatus>*
 IcuTimeZoneState;
--- a/js/src/vm/Realm.cpp
+++ b/js/src/vm/Realm.cpp
@@ -16,16 +16,17 @@
 #include "gc/PublicIterators.h"
 #include "jit/JitOptions.h"
 #include "jit/JitRealm.h"
 #include "js/Date.h"
 #include "js/Proxy.h"
 #include "js/RootingAPI.h"
 #include "js/Wrapper.h"
 #include "proxy/DeadObjectProxy.h"
+#include "vm/DateTime.h"
 #include "vm/Debugger.h"
 #include "vm/Iteration.h"
 #include "vm/JSContext.h"
 #include "vm/WrapperObject.h"
 
 #include "gc/GC-inl.h"
 #include "gc/Marking-inl.h"
 #include "vm/JSAtom-inl.h"
@@ -102,17 +103,17 @@ bool
 Realm::init(JSContext* cx, JSPrincipals* principals)
 {
     /*
      * As a hack, we clear our timezone cache every time we create a new realm.
      * This ensures that the cache is always relatively fresh, but shouldn't
      * interfere with benchmarks that create tons of date objects (unless they
      * also create tons of iframes, which seems unlikely).
      */
-    JS::ResetTimeZone();
+    js::ResetTimeZoneInternal(ResetTimeZoneMode::DontResetIfOffsetUnchanged);
 
     if (!objects_.init(cx))
         return false;
 
     if (principals) {
         // Any realm with the trusted principals -- and there can be
         // multiple -- is a system realm.
         isSystem_ = (principals == cx->runtime()->trustedPrincipals());
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -35,16 +35,17 @@
 #include "jit/JitRealm.h"
 #include "jit/mips32/Simulator-mips32.h"
 #include "jit/mips64/Simulator-mips64.h"
 #include "js/Date.h"
 #include "js/MemoryMetrics.h"
 #include "js/SliceBudget.h"
 #include "js/Wrapper.h"
 #include "util/Windows.h"
+#include "vm/DateTime.h"
 #include "vm/Debugger.h"
 #include "vm/JSAtom.h"
 #include "vm/JSObject.h"
 #include "vm/JSScript.h"
 #include "vm/TraceLogging.h"
 #include "vm/TraceLoggingGraph.h"
 
 #include "gc/GC-inl.h"
@@ -223,17 +224,17 @@ JSRuntime::init(JSContext* cx, uint32_t 
     gc.atomsZone = atomsZone.release();
 
     /* The garbage collector depends on everything before this point being initialized. */
     gcInitialized = true;
 
     if (!InitRuntimeNumberState(this))
         return false;
 
-    JS::ResetTimeZone();
+    js::ResetTimeZoneInternal(ResetTimeZoneMode::DontResetIfOffsetUnchanged);
 
     jitSupportsFloatingPoint = js::jit::JitSupportsFloatingPoint();
     jitSupportsUnalignedAccesses = js::jit::JitSupportsUnalignedAccesses();
     jitSupportsSimd = js::jit::JitSupportsSimd();
 
     if (!parentRuntime) {
         sharedImmutableStrings_ = js::SharedImmutableStringsCache::Create();
         if (!sharedImmutableStrings_)
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -38,17 +38,16 @@
 #endif
 #include "js/UniquePtr.h"
 #include "js/Utility.h"
 #include "js/Vector.h"
 #include "threading/Thread.h"
 #include "vm/Caches.h"
 #include "vm/CodeCoverage.h"
 #include "vm/CommonPropertyNames.h"
-#include "vm/DateTime.h"
 #include "vm/GeckoProfiler.h"
 #include "vm/JSAtom.h"
 #include "vm/JSScript.h"
 #include "vm/Scope.h"
 #include "vm/SharedImmutableStringsCache.h"
 #include "vm/Stack.h"
 #include "vm/Stopwatch.h"
 #include "vm/SymbolType.h"
--- a/toolkit/components/resistfingerprinting/nsRFPService.cpp
+++ b/toolkit/components/resistfingerprinting/nsRFPService.cpp
@@ -829,16 +829,24 @@ nsRFPService::UpdateRFPPref()
 #else
       // For POSIX like system, we reset the TZ to the /etc/localtime, which is the
       // system timezone.
       PR_SetEnv("TZ=:/etc/localtime");
 #endif
     }
   }
 
+  // localtime_r (and other functions) may not call tzset, so do this here after
+  // changing TZ to ensure all <time.h> functions use the new time zone.
+#if defined(XP_WIN)
+  _tzset();
+#else
+  tzset();
+#endif
+
   nsJSUtils::ResetTimeZone();
 }
 
 void
 nsRFPService::StartShutdown()
 {
   MOZ_ASSERT(NS_IsMainThread());