Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/jsc/bindings/ZigGlobalObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3378,8 +3378,9 @@ RefPtr<Performance> GlobalObject::performance()
{
if (!m_performance) {
auto* context = this->scriptExecutionContext();
double nanoTimeOrigin = Bun__readOriginTimerStart(this->bunVM());
auto timeOrigin = MonotonicTime::fromRawSeconds(nanoTimeOrigin / 1000.0);
// WTF MonotonicTime value at the instant bunVM()->origin_timer was captured.
auto elapsed = Seconds { static_cast<double>(Bun__readOriginTimer(this->bunVM())) / 1000000000.0 };
auto timeOrigin = MonotonicTime::now() - elapsed;
m_performance = Performance::create(context, timeOrigin);
Comment thread
robobun marked this conversation as resolved.
Outdated
}

Expand Down
17 changes: 4 additions & 13 deletions src/jsc/bindings/webcore/Event.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@
#include "EventPath.h"
#include "EventTarget.h"
// #include "InspectorInstrumentation.h"
// #include "Performance.h"
#include "Performance.h"
// #include "UserGestureIndicator.h"
// #include "WorkerGlobalScope.h"
#include "ZigGlobalObject.h"
#include <wtf/HexNumber.h>
#include <wtf/TZoneMallocInlines.h>
#include <wtf/text/StringBuilder.h>
Expand Down Expand Up @@ -162,18 +163,8 @@ void Event::setUnderlyingEvent(Event* underlyingEvent)

DOMHighResTimeStamp Event::timeStampForBindings(ScriptExecutionContext& context) const
{
// TODO:
return 0.0;
// Performance* performance = nullptr;
// if (is<WorkerGlobalScope>(context))
// performance = &downcast<WorkerGlobalScope>(context).performance();
// else if (auto* window = downcast<Document>(context).domWindow())
// performance = &window->performance();

// if (!performance)
// return 0;

// return std::max(performance->relativeTimeFromTimeOriginInReducedResolution(m_createTime), 0.);
auto performance = defaultGlobalObject(context.jsGlobalObject())->performance();
return std::max((m_createTime - performance->monotonicTimeOrigin()).milliseconds(), 0.0);
}

void Event::resetBeforeDispatch()
Expand Down
3 changes: 1 addition & 2 deletions src/jsc/bindings/webcore/Performance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,7 @@ DOMHighResTimeStamp Performance::now() const

DOMHighResTimeStamp Performance::timeOrigin() const
{
// return reduceTimeResolution(m_timeOrigin.approximateWallTime().secondsSinceEpoch()).milliseconds();
return m_timeOrigin.secondsSinceEpoch().milliseconds();
return Bun__readOriginTimerStart(bunVM(scriptExecutionContext()->vm()));
}

// ReducedResolutionSeconds Performance::nowInReducedResolutionSeconds() const
Expand Down
1 change: 1 addition & 0 deletions src/jsc/bindings/webcore/Performance.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ class Performance final : public RefCounted<Performance>, public ContextDestruct

DOMHighResTimeStamp relativeTimeFromTimeOriginInReducedResolution(MonotonicTime) const;
MonotonicTime monotonicTimeFromRelativeTime(DOMHighResTimeStamp) const;
MonotonicTime monotonicTimeOrigin() const { return m_timeOrigin; }

ScriptExecutionContext* scriptExecutionContext() const final { return ContextDestructionObserver::scriptExecutionContext(); }

Expand Down
43 changes: 43 additions & 0 deletions test/js/web/web-globals.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,49 @@
expect(called).toBe(true);
});

test("Event.prototype.timeStamp", async () => {
await using proc = spawn({
cmd: [
bunExe(),
"-e",
`
while (performance.now() < 10) {}
const before = performance.now();
const samples = [];
samples.push(new Event("x").timeStamp);
samples.push(new CustomEvent("x").timeStamp);
samples.push(new MessageEvent("x").timeStamp);
samples.push(new ErrorEvent("x").timeStamp);
const target = new EventTarget();
target.addEventListener("go", e => samples.push(e.timeStamp));
target.dispatchEvent(new Event("go"));
const ac = new AbortController();
ac.signal.addEventListener("abort", e => samples.push(e.timeStamp));
ac.abort();
const after = performance.now();
const ev = new Event("stable");
const first = ev.timeStamp;
while (performance.now() < after + 5) {}
console.log(JSON.stringify({ before, after, samples, first, second: ev.timeStamp }));
`,
],
env: bunEnv,
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
expect(stderr).toBe("");

Check warning on line 145 in test/js/web/web-globals.test.js

View check run for this annotation

Claude / Claude Code Review

Test asserts stderr is exactly empty — flaky under ASAN/debug

`expect(stderr).toBe("")` will flake on ASAN/debug CI lanes, which emit benign warnings on stderr — REVIEW.md's subprocess-test rule explicitly says to assert a combined `{ stdout, stderr, exitCode }` object instead. It also runs before `JSON.parse(stdout)`, so a spurious stderr failure hides the actual output; the `navigator.%s is a getter-only accessor` test just below already shows the preferred pattern (`stderr: expect.any(String)` inside a combined `toEqual`).
Comment thread
robobun marked this conversation as resolved.
Outdated
const { before, after, samples, first, second } = JSON.parse(stdout);
expect(samples.length).toBe(6);
for (const ts of samples) {
expect(typeof ts).toBe("number");
expect(ts).toBeGreaterThanOrEqual(before);
expect(ts).toBeLessThanOrEqual(after);
}
expect(first).toBeGreaterThanOrEqual(after);

Check warning on line 153 in test/js/web/web-globals.test.js

View check run for this annotation

Claude / Claude Code Review

timeStamp and performance.now() are on different time bases — exact >= bounds can flake

`event.timeStamp` and `performance.now()` are on subtly different time bases: `GlobalObject::performance()` samples `Bun__readOriginTimer` first and `MonotonicTime::now()` second, so `m_timeOrigin` overshoots the true origin by δ ≥ 0 and every `timeStamp` reads δ ms lower than `performance.now()` would at the same instant. The zero-tolerance `toBeGreaterThanOrEqual(before)` / `first >= after` assertions here therefore rely on JS overhead exceeding δ — normally trivial, but a preemption between t
Comment thread
robobun marked this conversation as resolved.
Outdated
expect(second).toBe(first);
expect(exitCode).toBe(0);
});

it("crypto.getRandomValues", () => {
var foo = new Uint8Array(32);

Expand Down
Loading