The runtime implements the WHATWG/WinterTC-standard Performance surface: High
Resolution Time, User Timing Level
3 and the Performance
Timeline with
PerformanceObserver.
Globals (own, writable, enumerable, configurable properties of globalThis,
in main and worker isolates alike): performance, Performance,
PerformanceEntry, PerformanceMark, PerformanceMeasure,
PerformanceObserver, PerformanceObserverEntryList.
performance.now()— double milliseconds since the isolate's time origin, monotonic (V8 platform clock,mach_absolute_time-based: it does not tick while the device is asleep), full double precision with no coarsening.performance.timeOrigin— readonly accessor; wall-clock milliseconds since the Unix epoch, sampled once when the isolate's runtime is created. Each worker gets its own time origin at worker-thread start, sotimeOrigin + now()approximatesDate.now()per isolate while the device stays awake; it drifts behind after device sleep (the monotonic clock does not tick then) and diverges under wall-clock adjustments.performance.toJSON(),Symbol.toStringTag, andPerformance extends EventTargetper spec;performance,PerformanceEntry,PerformanceMeasureandPerformanceObserverEntryListare not user-constructible (newthrowsTypeError);new PerformanceMark(name, options)is constructible per spec but does not buffer the entry.- User timing:
mark(name, {startTime, detail}),measure(name, startOrOptions, endMark)with the full Level 3 options algebra ({start, end, duration, detail}, mark names or timestamps, over-/under-constraint errors),clearMarks(name?),clearMeasures(name?). - Timeline:
getEntries(),getEntriesByType(type),getEntriesByName(name, type?)return copies sorted chronologically bystartTime(stable for ties). - Observers:
new PerformanceObserver(cb),observe({entryTypes})orobserve({type, buffered}),disconnect(),takeRecords(), static frozenPerformanceObserver.supportedEntryTypes === ["mark", "measure"].
All spec logic lives in the internal/performance.js builtin
(NativeScript/runtime/js/performance.js), which is deliberately portable:
the native side hands it only { now(), timeOrigin }, so the same file is
meant to be reused unchanged by the Android runtime against an equivalent
binding bag.
The native clock is owned by Runtime (Runtime::PerformanceNowMillis(),
Runtime::TimeOriginMillis(), captured in Runtime::CreateIsolate) and
exposed to native callers through tns::Performance::NowMillis(isolate)
(NativeScript/runtime/Performance.h). Any future native producer of
JS-visible timestamps — requestAnimationFrame in particular — must read the
clock through that hook rather than sampling its own, so every timestamp
shares performance.timeOrigin as its base.
- Buffers are unbounded. Per spec for user timing.
detailis structured-cloned at entry creation (per spec — an uncloneabledetailthrows theDataCloneError-named error), so entries hold snapshots, but a long-lived app marking in a loop should still clear entries periodically. - Observer callbacks run from a microtask, not a queued task: delivery is
asynchronous relative to
mark()/measure()but precedes timer callbacks scheduled in the same turn. Callback exceptions are routed toreportError, so one throwing observer does not starve the others. - No
DOMException. Errors the specs express asDOMException— theSyntaxErrorfor a missing mark name, theInvalidModificationErrorfor switching an observer between theentryTypesandtypeforms — areErrorinstances withnamepatched.err.namechecks work;instanceof DOMExceptiondoes not. - Browser-only surface is absent: no resource/navigation timing, no
eventCounts, and noPerformanceTiming-attribute resolution inmeasure().