Skip to content

Commit

Permalink
Trace date getter values
Browse files Browse the repository at this point in the history
Summary:
Capture and replay the return values from Date getters that return
localized date numbers, ensuring they return the same numbers in
different timezones.

Reviewed By: neildhar

Differential Revision: D62754273

fbshipit-source-id: a687eafd7e8c1e0c9158def75ed3b0e14973ad68
  • Loading branch information
Matt Blagden authored and facebook-github-bot committed Oct 2, 2024
1 parent ded62d3 commit 1802663
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 15 deletions.
67 changes: 57 additions & 10 deletions API/hermes/TracingRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ void TracingRuntime::replaceNondeterministicFuncs() {
auto fun = args[0].getObject(*runtime_).getFunction(*runtime_);
jsi::Value result;
if (count > 1 && args[1].isObject()) {
result = fun.callWithThis(*runtime_, args[1].asObject(*runtime_));
result = fun.callWithThis(
*runtime_, args[1].asObject(*runtime_), &args[2], count - 2);
} else {
result = fun.call(*runtime_);
}
Expand Down Expand Up @@ -181,25 +182,71 @@ void TracingRuntime::replaceNondeterministicFuncs() {
globalThis.WeakRef = WeakRef;
}
// Trace date getters and conversion functions as they can produce values
// that depend on the execution environment's timezone. Setters can set
// values that use the timezone, but the effect will not be observable
// because of the overridden getters.
var DateReal = globalThis.Date;
var dateNowReal = DateReal.now;
var nativeDateNow = function now() { return callUntraced(dateNowReal); };
[
"getDate",
"getDay",
"getFullYear",
"getHours",
"getMilliseconds",
"getMinutes",
"getMonth",
"getSeconds",
"getTime",
"getTimezoneOffset",
"getUTCDate",
"getUTCDay",
"getUTCFullYear",
"getUTCHours",
"getUTCMilliseconds",
"getUTCMinutes",
"getUTCMonth",
"getUTCSeconds",
"getYear",
"toDateString",
"toGMTString",
"toISOString",
"toJSON",
"toLocaleDateString",
"toLocaleString",
"toLocaleTimeString",
"toString",
"toTimeString",
"toUTCString",
"valueOf",
Symbol.toPrimitive
].forEach((property) => {
const original = DateReal.prototype[property];
const replacement = function(...args) {
return callUntraced(original, this, ...args);
};
Object.defineProperty(replacement, "name", {value: original.name});
Object.defineProperty(DateReal.prototype, property, {
value: replacement,
writable: true,
configurable: true
});
});
function Date(...args){
// Convert non-deterministic calls like `Date()` and `new Date()` into the
// deterministic form `new Date(Date.now())`, so they can be traced.
// Trace calls to `Date()`.
if(!new.target){
return new DateReal(nativeDateNow()).toString();
}
if (arguments.length == 0){
return new DateReal(nativeDateNow());
return callUntraced(DateReal);
}
// While `new Date()` technically records the current time, there is no way
// to observe it since we override all the getters.
return new DateReal(...args);
}
// Cannot use Object.assign because Date methods are not enumerable
for (p of Object.getOwnPropertyNames(DateReal)){
Date[p] = DateReal[p];
}
Date.now = nativeDateNow;
var dateNowReal = DateReal.now;
Date.now = function now() { return callUntraced(dateNowReal); };
globalThis.Date = Date;
const defineProperty = Object.defineProperty;
Expand Down
11 changes: 6 additions & 5 deletions unittests/API/SynthTraceTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2092,12 +2092,12 @@ TEST_F(NonDeterminismReplayTest, DateFuncTest) {
}

TEST_F(NonDeterminismReplayTest, DateNewTest) {
eval(*traceRt, "var x = new Date();");
auto dateTime = eval(*traceRt, "x.getTime()").asNumber();
eval(*traceRt, "var x = new Date(); var y = x.getTime();");
auto dateTime = eval(*traceRt, "y").asNumber();

replay();

auto replayedTime = eval(*replayRt, "x.getTime()").asNumber();
auto replayedTime = eval(*replayRt, "y").asNumber();
EXPECT_EQ(dateTime, replayedTime);
}

Expand All @@ -2116,12 +2116,13 @@ TEST_F(NonDeterminismReplayTest, DateReplacedTest) {
var oldDate = Date;
Date = undefined;
var x = new oldDate();
var y = x.getTime();
)");
auto traceTime = eval(*traceRt, "x.getTime()").asNumber();
auto traceTime = eval(*traceRt, "y").asNumber();

replay();

auto replayedTime = eval(*replayRt, "x.getTime()").asNumber();
auto replayedTime = eval(*replayRt, "y").asNumber();
EXPECT_EQ(traceTime, replayedTime);
}

Expand Down

0 comments on commit 1802663

Please sign in to comment.