Title: [164007] trunk/Source/_javascript_Core
Revision
164007
Author
[email protected]
Date
2014-02-12 19:34:33 -0800 (Wed, 12 Feb 2014)

Log Message

Web Replay: capture and replay nondeterminism of Date.now() and Math.random()
https://bugs.webkit.org/show_bug.cgi?id=128633

Reviewed by Filip Pizlo.

Upstream the only two sources of script-visible nondeterminism in _javascript_Core.

The random seed for WeakRandom is memoized when the owning JSGlobalObject is
constructed. It is deterministically initialized during replay before any
scripts execute with the global object.

The implementations of `Date.now()` and `new Date()` eventually obtain the
current time from jsCurrentTime(). When capturing, we save return values of
jsCurrentTime() into the recording. When replaying, we use memoized values from
the recording instead of obtaining values from the platform-specific currentTime()
implementation. No other code calls jsCurrentTime().

* DerivedSources.make: Add rules to make JSReplayInputs.h from JSInputs.json.
* _javascript_Core.xcodeproj/project.pbxproj:
* replay/JSInputs.json: Added. Includes specifications for replay inputs
"GetCurrentTime" and "SetRandomSeed". Tests will be added for both input
cases once sufficient replay machinery has been added.

* replay/NondeterministicInput.h: NondeterministicInput should not have
been marked 'final'.

* runtime/DateConstructor.cpp:
(JSC::deterministicCurrentTime): Added. Load or store the current time depending
on what kind of InputCursor is attached to the JSGlobalObject.

(JSC::constructDate): Use deterministicCurrentTime().
(JSC::dateNow): Use deterministicCurrentTime().
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::setInputCursor): When setting a non-empty input cursor,
immediately store or load the "SetRandomSeed" input and initialize WeakRandom's
random seed with it. The input cursor (and thus random seed) must be set before
any scripts are evaluated with this JSGlobalObject.

* runtime/WeakRandom.h:
(JSC::WeakRandom::WeakRandom): Add JSGlobalObject as a friend class.
(JSC::WeakRandom::initializeSeed): Extract the seed initialization into a
separate method so it can be called outside of the JSGlobalObject constructor.

Modified Paths

Added Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (164006 => 164007)


--- trunk/Source/_javascript_Core/ChangeLog	2014-02-13 03:02:53 UTC (rev 164006)
+++ trunk/Source/_javascript_Core/ChangeLog	2014-02-13 03:34:33 UTC (rev 164007)
@@ -1,3 +1,48 @@
+2014-02-12  Brian Burg  <[email protected]>
+
+        Web Replay: capture and replay nondeterminism of Date.now() and Math.random()
+        https://bugs.webkit.org/show_bug.cgi?id=128633
+
+        Reviewed by Filip Pizlo.
+
+        Upstream the only two sources of script-visible nondeterminism in _javascript_Core.
+
+        The random seed for WeakRandom is memoized when the owning JSGlobalObject is
+        constructed. It is deterministically initialized during replay before any
+        scripts execute with the global object.
+
+        The implementations of `Date.now()` and `new Date()` eventually obtain the
+        current time from jsCurrentTime(). When capturing, we save return values of
+        jsCurrentTime() into the recording. When replaying, we use memoized values from
+        the recording instead of obtaining values from the platform-specific currentTime()
+        implementation. No other code calls jsCurrentTime().
+
+        * DerivedSources.make: Add rules to make JSReplayInputs.h from JSInputs.json.
+        * _javascript_Core.xcodeproj/project.pbxproj:
+        * replay/JSInputs.json: Added. Includes specifications for replay inputs
+        "GetCurrentTime" and "SetRandomSeed". Tests will be added for both input
+        cases once sufficient replay machinery has been added.
+
+        * replay/NondeterministicInput.h: NondeterministicInput should not have
+        been marked 'final'.
+
+        * runtime/DateConstructor.cpp:
+        (JSC::deterministicCurrentTime): Added. Load or store the current time depending
+        on what kind of InputCursor is attached to the JSGlobalObject.
+
+        (JSC::constructDate): Use deterministicCurrentTime().
+        (JSC::dateNow): Use deterministicCurrentTime().
+        * runtime/JSGlobalObject.cpp:
+        (JSC::JSGlobalObject::setInputCursor): When setting a non-empty input cursor,
+        immediately store or load the "SetRandomSeed" input and initialize WeakRandom's
+        random seed with it. The input cursor (and thus random seed) must be set before
+        any scripts are evaluated with this JSGlobalObject.
+
+        * runtime/WeakRandom.h:
+        (JSC::WeakRandom::WeakRandom): Add JSGlobalObject as a friend class.
+        (JSC::WeakRandom::initializeSeed): Extract the seed initialization into a
+        separate method so it can be called outside of the JSGlobalObject constructor.
+
 2014-02-12  Joseph Pecoraro  <[email protected]>
 
         Web Inspector: Cleanup _javascript_Core/inspector

Modified: trunk/Source/_javascript_Core/DerivedSources.make (164006 => 164007)


--- trunk/Source/_javascript_Core/DerivedSources.make	2014-02-13 03:02:53 UTC (rev 164006)
+++ trunk/Source/_javascript_Core/DerivedSources.make	2014-02-13 03:34:33 UTC (rev 164007)
@@ -101,15 +101,25 @@
     $(_javascript_Core)/inspector/protocol/Runtime.json \
 #
 
+INPUT_GENERATOR_SCRIPTS = \
+    $(_javascript_Core)/replay/scripts/CodeGeneratorReplayInputs.py \
+    $(_javascript_Core)/replay/scripts/CodeGeneratorReplayInputsTemplates.py \
+#
+
 INSPECTOR_GENERATOR_SCRIPTS = \
 	$(_javascript_Core)/inspector/scripts/CodeGeneratorInspector.py \
 	$(_javascript_Core)/inspector/scripts/CodeGeneratorInspectorStrings.py \
 #
 
+INPUT_GENERATOR_SPECIFICATIONS = \
+    $(_javascript_Core)/replay/JSInputs.json \
+#
+
 all : \
     InspectorJS.json \
     InspectorJSFrontendDispatchers.h \
     InjectedScriptSource.h \
+	JSReplayInputs.h \
 #
 
 InspectorJS.json : inspector/scripts/generate-combined-inspector-json.py $(INSPECTOR_DOMAINS)
@@ -123,3 +133,9 @@
 	python $(_javascript_Core)/inspector/scripts/jsmin.py < $(_javascript_Core)/inspector/InjectedScriptSource.js > ./InjectedScriptSource.min.js
 	perl $(_javascript_Core)/inspector/scripts/xxd.pl InjectedScriptSource_js ./InjectedScriptSource.min.js InjectedScriptSource.h
 	rm -f ./InjectedScriptSource.min.js
+
+# Web Replay inputs generator
+
+JSReplayInputs.h : $(INPUT_GENERATOR_SPECIFICATIONS) $(INPUT_GENERATOR_SCRIPTS)
+	python $(_javascript_Core)/replay/scripts/CodeGeneratorReplayInputs.py --outputDir . --framework _javascript_Core $(INPUT_GENERATOR_SPECIFICATIONS)
+

Modified: trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj (164006 => 164007)


--- trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj	2014-02-13 03:02:53 UTC (rev 164006)
+++ trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj	2014-02-13 03:34:33 UTC (rev 164007)
@@ -930,6 +930,8 @@
 		969A079B0ED1D3AE00F1F681 /* Opcode.h in Headers */ = {isa = PBXBuildFile; fileRef = 969A07950ED1D3AE00F1F681 /* Opcode.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		978801401471AD920041B016 /* JSDateMath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9788FC221471AD0C0068CE2D /* JSDateMath.cpp */; };
 		978801411471AD920041B016 /* JSDateMath.h in Headers */ = {isa = PBXBuildFile; fileRef = 9788FC231471AD0C0068CE2D /* JSDateMath.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		9928FF3B18AC4AEC00B8CF12 /* JSReplayInputs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9928FF3918AC4AEC00B8CF12 /* JSReplayInputs.cpp */; };
+		9928FF3C18AC4AEC00B8CF12 /* JSReplayInputs.h in Headers */ = {isa = PBXBuildFile; fileRef = 9928FF3A18AC4AEC00B8CF12 /* JSReplayInputs.h */; };
 		99E45A2418A1B2590026D88F /* EmptyInputCursor.h in Headers */ = {isa = PBXBuildFile; fileRef = 99E45A1F18A1B2590026D88F /* EmptyInputCursor.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		99E45A2518A1B2590026D88F /* EncodedValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 99E45A2018A1B2590026D88F /* EncodedValue.cpp */; };
 		99E45A2618A1B2590026D88F /* EncodedValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 99E45A2118A1B2590026D88F /* EncodedValue.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -2409,6 +2411,9 @@
 		969A09220ED1E09C00F1F681 /* Completion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Completion.cpp; sourceTree = "<group>"; };
 		9788FC221471AD0C0068CE2D /* JSDateMath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSDateMath.cpp; sourceTree = "<group>"; };
 		9788FC231471AD0C0068CE2D /* JSDateMath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSDateMath.h; sourceTree = "<group>"; };
+		9928FF3918AC4AEC00B8CF12 /* JSReplayInputs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSReplayInputs.cpp; sourceTree = "<group>"; };
+		9928FF3A18AC4AEC00B8CF12 /* JSReplayInputs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSReplayInputs.h; sourceTree = "<group>"; };
+		9928FF3D18AC4B1C00B8CF12 /* JSInputs.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = JSInputs.json; sourceTree = "<group>"; };
 		99E45A1D18A1B1E70026D88F /* CodeGeneratorReplayInputs.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = CodeGeneratorReplayInputs.py; sourceTree = "<group>"; };
 		99E45A1E18A1B1E70026D88F /* CodeGeneratorReplayInputsTemplates.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = CodeGeneratorReplayInputsTemplates.py; sourceTree = "<group>"; };
 		99E45A1F18A1B2590026D88F /* EmptyInputCursor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EmptyInputCursor.h; sourceTree = "<group>"; };
@@ -3615,6 +3620,8 @@
 		650FDF8D09D0FCA700769E54 /* Derived Sources */ = {
 			isa = PBXGroup;
 			children = (
+				9928FF3918AC4AEC00B8CF12 /* JSReplayInputs.cpp */,
+				9928FF3A18AC4AEC00B8CF12 /* JSReplayInputs.h */,
 				A7D801A61880D6A80026C39B /* JSCBuiltins.cpp */,
 				A7D801A71880D6A80026C39B /* JSCBuiltins.h */,
 				BC18C5230E16FC8A00B34460 /* ArrayPrototype.lut.h */,
@@ -4576,6 +4583,7 @@
 				99E45A1F18A1B2590026D88F /* EmptyInputCursor.h */,
 				99E45A2218A1B2590026D88F /* InputCursor.h */,
 				99E45A2318A1B2590026D88F /* NondeterministicInput.h */,
+				9928FF3D18AC4B1C00B8CF12 /* JSInputs.json */,
 			);
 			path = replay;
 			sourceTree = "<group>";
@@ -4965,6 +4973,7 @@
 				A78A9779179738B8009DF744 /* DFGJITFinalizer.h in Headers */,
 				0FC97F4018202119002C9B26 /* DFGJumpReplacement.h in Headers */,
 				A73A535B1799CD5D00170C19 /* DFGLazyJSValue.h in Headers */,
+				9928FF3C18AC4AEC00B8CF12 /* JSReplayInputs.h in Headers */,
 				A7D9A29817A0BC7400EE2618 /* DFGLICMPhase.h in Headers */,
 				A7D89CFC17A0B8CC00773AD8 /* DFGLivenessAnalysisPhase.h in Headers */,
 				0FF0F19B16B729FA005DF95B /* DFGLongLivedState.h in Headers */,
@@ -5993,6 +6002,7 @@
 				1428082D107EC0570013E7B2 /* CallData.cpp in Sources */,
 				1429D8DD0ED2205B00B89619 /* CallFrame.cpp in Sources */,
 				0F0B83B014BCF71600885B4F /* CallLinkInfo.cpp in Sources */,
+				9928FF3B18AC4AEC00B8CF12 /* JSReplayInputs.cpp in Sources */,
 				A513E5B7185B8BD3007E95AD /* InjectedScript.cpp in Sources */,
 				0F5A6283188C98D40072C9DF /* FTLValueRange.cpp in Sources */,
 				0F93329D14CA7DC30085F3C6 /* CallLinkStatus.cpp in Sources */,

Added: trunk/Source/_javascript_Core/replay/JSInputs.json (0 => 164007)


--- trunk/Source/_javascript_Core/replay/JSInputs.json	                        (rev 0)
+++ trunk/Source/_javascript_Core/replay/JSInputs.json	2014-02-13 03:34:33 UTC (rev 164007)
@@ -0,0 +1,27 @@
+{
+    "types": {
+        "Global": [
+            { "name": "double", "mode": "SCALAR" },
+            { "name": "uint64_t", "mode": "SCALAR" }
+        ]
+    },
+
+    "inputs": [
+        {
+            "name": "GetCurrentTime",
+            "description": "Supplies the system time to Date.now() and new Date().",
+            "queue": "SCRIPT_MEMOIZED",
+            "members": [
+                { "name": "currentTime", "type": "double" }
+            ]
+        },
+        {
+            "name": "SetRandomSeed",
+            "description": "Sets the PRNG seed used by Math.random().",
+            "queue": "SCRIPT_MEMOIZED",
+            "members": [
+                { "name": "randomSeed", "type": "uint64_t" }
+            ]
+        }
+    ]
+}

Modified: trunk/Source/_javascript_Core/replay/NondeterministicInput.h (164006 => 164007)


--- trunk/Source/_javascript_Core/replay/NondeterministicInput.h	2014-02-13 03:02:53 UTC (rev 164006)
+++ trunk/Source/_javascript_Core/replay/NondeterministicInput.h	2014-02-13 03:34:33 UTC (rev 164007)
@@ -64,7 +64,7 @@
 };
 
 template<typename InputType>
-class NondeterministicInput final : public NondeterministicInputBase {
+class NondeterministicInput : public NondeterministicInputBase {
     virtual const AtomicString& type() const override
     {
         return InputTraits<InputType>::type();

Modified: trunk/Source/_javascript_Core/runtime/DateConstructor.cpp (164006 => 164007)


--- trunk/Source/_javascript_Core/runtime/DateConstructor.cpp	2014-02-13 03:02:53 UTC (rev 164006)
+++ trunk/Source/_javascript_Core/runtime/DateConstructor.cpp	2014-02-13 03:34:33 UTC (rev 164007)
@@ -36,6 +36,11 @@
 #include <time.h>
 #include <wtf/MathExtras.h>
 
+#if ENABLE(WEB_REPLAY)
+#include "InputCursor.h"
+#include "JSReplayInputs.h"
+#endif
+
 #if OS(WINCE)
 extern "C" time_t time(time_t* timer); // Provided by libce.
 #endif
@@ -72,6 +77,28 @@
 @end
 */
 
+#if ENABLE(WEB_REPLAY)
+static double deterministicCurrentTime(JSGlobalObject* globalObject)
+{
+    double currentTime = jsCurrentTime();
+    InputCursor& cursor = globalObject->inputCursor();
+    if (cursor.isCapturing())
+        cursor.appendInput<GetCurrentTime>(currentTime);
+
+    if (cursor.isReplaying()) {
+        if (GetCurrentTime* input = cursor.fetchInput<GetCurrentTime>())
+            currentTime = input->currentTime();
+    }
+    return currentTime;
+}
+#endif
+
+#if ENABLE(WEB_REPLAY)
+#define NORMAL_OR_DETERMINISTIC_FUNCTION(a, b) (b)
+#else
+#define NORMAL_OR_DETERMINISTIC_FUNCTION(a, b) (a)
+#endif
+
 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(DateConstructor);
 
 DateConstructor::DateConstructor(VM& vm, Structure* structure)
@@ -100,7 +127,7 @@
     double value;
 
     if (numArgs == 0) // new Date() ECMA 15.9.3.3
-        value = jsCurrentTime();
+        value = NORMAL_OR_DETERMINISTIC_FUNCTION(jsCurrentTime(), deterministicCurrentTime(globalObject));
     else if (numArgs == 1) {
         if (args.at(0).inherits(DateInstance::info()))
             value = asDateInstance(args.at(0))->internalNumber();
@@ -179,9 +206,13 @@
     return JSValue::encode(jsNumber(parseDate(exec->vm(), exec->argument(0).toString(exec)->value(exec))));
 }
 
-static EncodedJSValue JSC_HOST_CALL dateNow(ExecState*)
+static EncodedJSValue JSC_HOST_CALL dateNow(ExecState* exec)
 {
-    return JSValue::encode(jsNumber(jsCurrentTime()));
+#if !ENABLE(WEB_REPLAY)
+    UNUSED_PARAM(exec);
+#endif
+
+    return JSValue::encode(jsNumber(NORMAL_OR_DETERMINISTIC_FUNCTION(jsCurrentTime(), deterministicCurrentTime(exec->lexicalGlobalObject()))));
 }
 
 static EncodedJSValue JSC_HOST_CALL dateUTC(ExecState* exec) 

Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp (164006 => 164007)


--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp	2014-02-13 03:02:53 UTC (rev 164006)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp	2014-02-13 03:34:33 UTC (rev 164007)
@@ -129,6 +129,7 @@
 
 #if ENABLE(WEB_REPLAY)
 #include "EmptyInputCursor.h"
+#include "JSReplayInputs.h"
 #endif
 
 #include "JSGlobalObject.lut.h"
@@ -786,6 +787,17 @@
 void JSGlobalObject::setInputCursor(PassRefPtr<InputCursor> prpCursor)
 {
     m_inputCursor = prpCursor;
+    ASSERT(m_inputCursor);
+
+    InputCursor& cursor = inputCursor();
+    // Save or set the random seed. This performed here rather than the constructor
+    // to avoid threading the input cursor through all the abstraction layers.
+    if (cursor.isCapturing())
+        cursor.appendInput<SetRandomSeed>(m_weakRandom.seedUnsafe());
+    else if (cursor.isReplaying()) {
+        if (SetRandomSeed* input = cursor.fetchInput<SetRandomSeed>())
+            m_weakRandom.initializeSeed(input->randomSeed());
+    }
 }
 #endif
 

Modified: trunk/Source/_javascript_Core/runtime/WeakRandom.h (164006 => 164007)


--- trunk/Source/_javascript_Core/runtime/WeakRandom.h	2014-02-13 03:02:53 UTC (rev 164006)
+++ trunk/Source/_javascript_Core/runtime/WeakRandom.h	2014-02-13 03:34:33 UTC (rev 164007)
@@ -56,11 +56,11 @@
 namespace JSC {
 
 class WeakRandom {
+friend class JSGlobalObject; // For access to initializeSeed() during replay.
 public:
     WeakRandom(unsigned seed)
-        : m_low(seed ^ 0x49616E42)
-        , m_high(seed)
     {
+        initializeSeed(seed);
     }
     
     // Returns the seed provided that you've never called get() or getUint32().
@@ -85,6 +85,12 @@
         return m_high;
     }
 
+    void initializeSeed(unsigned seed)
+    {
+        m_low = seed ^ 0x49616E42;
+        m_high = seed;
+    }
+
     unsigned m_low;
     unsigned m_high;
 };
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to