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;
};