From ba316aae8108ee36b2670979aa72fa98a987c38f Mon Sep 17 00:00:00 2001 From: Ruslan Lesiutin Date: Fri, 17 Jul 2026 03:14:42 -0700 Subject: [PATCH] Revert D112116429 Summary: Changelog: [Internal] Reviewed By: thegreatercurve, lenaic, cortinico Differential Revision: D112522540 --- .../Libraries/Core/setUpPerformance.js | 25 +++++++++-- .../setUpDefaultReactNativeEnvironment.js | 4 +- ...rformance.js => setUpPerformanceModern.js} | 2 +- .../webapis/performance/EventTiming.js | 5 ++- .../webapis/performance/Performance.js | 42 ++++++++++++------- .../performance/PerformanceObserver.js | 4 +- .../private/webapis/performance/UserTiming.js | 8 ++-- .../performance/internals/Utilities.js | 27 ++++++++++++ .../performance/specs/NativePerformance.js | 6 +-- 9 files changed, 90 insertions(+), 33 deletions(-) rename packages/react-native/src/private/setup/{setUpPerformance.js => setUpPerformanceModern.js} (97%) create mode 100644 packages/react-native/src/private/webapis/performance/internals/Utilities.js diff --git a/packages/react-native/Libraries/Core/setUpPerformance.js b/packages/react-native/Libraries/Core/setUpPerformance.js index 1a3eaef5abd1..3f91689eabbf 100644 --- a/packages/react-native/Libraries/Core/setUpPerformance.js +++ b/packages/react-native/Libraries/Core/setUpPerformance.js @@ -8,8 +8,25 @@ * @format */ -// Kept as an alias of the internal `setUpPerformance` module for external -// consumers that import this path directly. -import setUpPerformance from '../../src/private/setup/setUpPerformance'; +import setUpPerformanceModern from '../../src/private/setup/setUpPerformanceModern'; +import NativePerformance from '../../src/private/webapis/performance/specs/NativePerformance'; -setUpPerformance(); +// In case if the native implementation of the Performance API is available, use it, +// otherwise fall back to the legacy/default one, which only defines 'Performance.now()' +if (NativePerformance) { + setUpPerformanceModern(); +} else { + if (!global.performance) { + // $FlowExpectedError[cannot-write] + global.performance = { + mark: () => {}, + clearMarks: () => {}, + measure: () => {}, + clearMeasures: () => {}, + now: () => { + const performanceNow = global.nativePerformanceNow || Date.now; + return performanceNow(); + }, + }; + } +} diff --git a/packages/react-native/src/private/setup/setUpDefaultReactNativeEnvironment.js b/packages/react-native/src/private/setup/setUpDefaultReactNativeEnvironment.js index 6bd27c4cc3d1..eb61a6644345 100644 --- a/packages/react-native/src/private/setup/setUpDefaultReactNativeEnvironment.js +++ b/packages/react-native/src/private/setup/setUpDefaultReactNativeEnvironment.js @@ -10,7 +10,7 @@ let initialized = false; -export default function setUpDefaultReactNativeEnvironment( +export default function setUpDefaltReactNativeEnvironment( enableDeveloperTools: boolean = true, ) { if (initialized) { @@ -21,7 +21,7 @@ export default function setUpDefaultReactNativeEnvironment( require('../../../Libraries/Core/setUpGlobals'); require('./setUpDOM').default(); - require('./setUpPerformance').default(); + require('../../../Libraries/Core/setUpPerformance'); require('../../../Libraries/Core/polyfillPromise'); require('../../../Libraries/Core/setUpTimers'); if (__DEV__ && enableDeveloperTools) { diff --git a/packages/react-native/src/private/setup/setUpPerformance.js b/packages/react-native/src/private/setup/setUpPerformanceModern.js similarity index 97% rename from packages/react-native/src/private/setup/setUpPerformance.js rename to packages/react-native/src/private/setup/setUpPerformanceModern.js index 747b92f5f577..22a7ca135043 100644 --- a/packages/react-native/src/private/setup/setUpPerformance.js +++ b/packages/react-native/src/private/setup/setUpPerformanceModern.js @@ -12,7 +12,7 @@ import {polyfillGlobal} from '../../../Libraries/Utilities/PolyfillFunctions'; let initialized = false; -export default function setUpPerformance() { +export default function setUpPerformanceModern() { if (initialized) { return; } diff --git a/packages/react-native/src/private/webapis/performance/EventTiming.js b/packages/react-native/src/private/webapis/performance/EventTiming.js index 713964ea4f37..c175f8592f18 100644 --- a/packages/react-native/src/private/webapis/performance/EventTiming.js +++ b/packages/react-native/src/private/webapis/performance/EventTiming.js @@ -16,7 +16,10 @@ import type { } from './PerformanceEntry'; import {PerformanceEntry} from './PerformanceEntry'; -import NativePerformance from './specs/NativePerformance'; +import MaybeNativePerformance from './specs/NativePerformance'; +import nullthrows from 'nullthrows'; + +const NativePerformance = nullthrows(MaybeNativePerformance); export type PerformanceEventTimingJSON = { ...PerformanceEntryJSON, diff --git a/packages/react-native/src/private/webapis/performance/Performance.js b/packages/react-native/src/private/webapis/performance/Performance.js index e6f544475d3a..6a492064fb43 100644 --- a/packages/react-native/src/private/webapis/performance/Performance.js +++ b/packages/react-native/src/private/webapis/performance/Performance.js @@ -29,10 +29,12 @@ import { performanceEntryTypeToRaw, rawToPerformanceEntry, } from './internals/RawPerformanceEntry'; +import {getCurrentTimeStamp} from './internals/Utilities'; import MemoryInfo from './MemoryInfo'; import ReactNativeStartupTiming from './ReactNativeStartupTiming'; -import NativePerformance from './specs/NativePerformance'; +import MaybeNativePerformance from './specs/NativePerformance'; import {PerformanceMark, PerformanceMeasure} from './UserTiming'; +import nullthrows from 'nullthrows'; export type PerformanceMeasureOptions = | Readonly<{ @@ -54,8 +56,13 @@ export type PerformanceMeasureOptions = const ENTRY_TYPES_AVAILABLE_FROM_TIMELINE: ReadonlyArray = ['mark', 'measure']; -const {now, reportMark, reportMeasure, getMarkTime, clearMarks, clearMeasures} = - NativePerformance; +const NativePerformance = nullthrows(MaybeNativePerformance); + +const cachedReportMark = NativePerformance.reportMark; +const cachedReportMeasure = NativePerformance.reportMeasure; +const cachedGetMarkTime = NativePerformance.getMarkTime; +const cachedNativeClearMarks = NativePerformance.clearMarks; +const cachedNativeClearMeasures = NativePerformance.clearMeasures; let cachedTimeOrigin: ?DOMHighResTimeStamp; const MARK_OPTIONS_REUSABLE_OBJECT: PerformanceMarkOptions = { @@ -71,7 +78,7 @@ const MEASURE_OPTIONS_REUSABLE_OBJECT: PerformanceMeasureInit = { }; const getMarkTimeForMeasure = (markName: string): number => { - const markTime = getMarkTime(markName); + const markTime = cachedGetMarkTime(markName); if (markTime == null) { throw new DOMException( `Failed to execute 'measure' on 'Performance': The mark '${markName}' does not exist.`, @@ -140,7 +147,12 @@ export default class Performance { */ get timeOrigin(): DOMHighResTimeStamp { if (cachedTimeOrigin == null) { - cachedTimeOrigin = NativePerformance.timeOrigin(); + if (NativePerformance.timeOrigin) { + cachedTimeOrigin = NativePerformance?.timeOrigin(); + } else { + // Very naive polyfill. + cachedTimeOrigin = Date.now() - getCurrentTimeStamp(); + } } return cachedTimeOrigin; @@ -190,7 +202,7 @@ export default class Performance { ); } } else { - resolvedStartTime = now(); + resolvedStartTime = getCurrentTimeStamp(); } if (detail !== undefined) { @@ -207,13 +219,13 @@ export default class Performance { MARK_OPTIONS_REUSABLE_OBJECT, ); - reportMark(resolvedMarkName, resolvedStartTime, entry); + cachedReportMark(resolvedMarkName, resolvedStartTime, entry); return entry; } clearMarks(markName?: string): void { - clearMarks(markName); + cachedNativeClearMarks(markName); } measure( @@ -335,7 +347,7 @@ export default class Performance { ) { resolvedDuration = resolvedEndTime - resolvedStartTime; } else { - resolvedDuration = now() - resolvedStartTime; + resolvedDuration = getCurrentTimeStamp() - resolvedStartTime; } } @@ -352,7 +364,7 @@ export default class Performance { resolvedDuration = getMarkTimeForMeasure(endMark) - resolvedStartTime; } else { - resolvedDuration = now() - resolvedStartTime; + resolvedDuration = getCurrentTimeStamp() - resolvedStartTime; } break; } @@ -363,7 +375,7 @@ export default class Performance { resolvedDuration = getMarkTimeForMeasure(endMark) - resolvedStartTime; } else { - resolvedDuration = now() - resolvedStartTime; + resolvedDuration = getCurrentTimeStamp() - resolvedStartTime; } } } @@ -373,7 +385,7 @@ export default class Performance { if (endMark !== undefined) { resolvedDuration = getMarkTimeForMeasure(endMark) - resolvedStartTime; } else { - resolvedDuration = now() - resolvedStartTime; + resolvedDuration = getCurrentTimeStamp() - resolvedStartTime; } } @@ -388,7 +400,7 @@ export default class Performance { const entry = new PerformanceMeasure(MEASURE_OPTIONS_REUSABLE_OBJECT); - reportMeasure( + cachedReportMeasure( resolvedMeasureName, resolvedStartTime, resolvedDuration, @@ -399,14 +411,14 @@ export default class Performance { } clearMeasures(measureName?: string): void { - clearMeasures(measureName); + cachedNativeClearMeasures(measureName); } /** * Returns a double, measured in milliseconds. * https://developer.mozilla.org/en-US/docs/Web/API/Performance/now */ - now: () => DOMHighResTimeStamp = now; + now: () => DOMHighResTimeStamp = getCurrentTimeStamp; /** * An extension that allows to get back to JS all currently logged marks/measures diff --git a/packages/react-native/src/private/webapis/performance/PerformanceObserver.js b/packages/react-native/src/private/webapis/performance/PerformanceObserver.js index e4d899a3cb72..b66e7f406614 100644 --- a/packages/react-native/src/private/webapis/performance/PerformanceObserver.js +++ b/packages/react-native/src/private/webapis/performance/PerformanceObserver.js @@ -21,11 +21,13 @@ import { rawToPerformanceEntry, rawToPerformanceEntryType, } from './internals/RawPerformanceEntry'; -import NativePerformance from './specs/NativePerformance'; +import MaybeNativePerformance from './specs/NativePerformance'; import nullthrows from 'nullthrows'; export {PerformanceEntry} from './PerformanceEntry'; +const NativePerformance = nullthrows(MaybeNativePerformance); + export class PerformanceObserverEntryList { #entries: PerformanceEntryList; diff --git a/packages/react-native/src/private/webapis/performance/UserTiming.js b/packages/react-native/src/private/webapis/performance/UserTiming.js index f07b48c763bb..ff8786083194 100644 --- a/packages/react-native/src/private/webapis/performance/UserTiming.js +++ b/packages/react-native/src/private/webapis/performance/UserTiming.js @@ -18,10 +18,8 @@ import type { ExtensionTrackEntryPayload, } from './UserTimingExtensibility'; +import {getCurrentTimeStamp} from './internals/Utilities'; import {PerformanceEntry} from './PerformanceEntry'; -import NativePerformance from './specs/NativePerformance'; - -const {now} = NativePerformance; export type DetailType = | unknown @@ -49,7 +47,7 @@ class PerformanceMarkTemplate extends PerformanceEntry { constructor(markName: string, markOptions?: PerformanceMarkOptions) { super('mark', { name: markName, - startTime: markOptions?.startTime ?? now(), + startTime: markOptions?.startTime ?? getCurrentTimeStamp(), duration: 0, }); @@ -75,7 +73,7 @@ export const PerformanceMark: typeof PerformanceMarkTemplate = ) { this.__entryType = 'mark'; this.__name = markName; - this.__startTime = markOptions?.startTime ?? now(); + this.__startTime = markOptions?.startTime ?? getCurrentTimeStamp(); this.__duration = 0; this.__detail = markOptions?.detail ?? null; diff --git a/packages/react-native/src/private/webapis/performance/internals/Utilities.js b/packages/react-native/src/private/webapis/performance/internals/Utilities.js new file mode 100644 index 000000000000..929afbe51282 --- /dev/null +++ b/packages/react-native/src/private/webapis/performance/internals/Utilities.js @@ -0,0 +1,27 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict + * @format + */ + +import warnOnce from '../../../../../Libraries/Utilities/warnOnce'; +import NativePerformance from '../specs/NativePerformance'; + +export function warnNoNativePerformance() { + warnOnce( + 'missing-native-performance', + 'Missing native implementation of Performance', + ); +} + +declare var global: { + // This value is defined directly via JSI, if available. + readonly nativePerformanceNow?: ?() => number, +}; + +export const getCurrentTimeStamp: () => DOMHighResTimeStamp = + NativePerformance?.now ?? global.nativePerformanceNow ?? (() => Date.now()); diff --git a/packages/react-native/src/private/webapis/performance/specs/NativePerformance.js b/packages/react-native/src/private/webapis/performance/specs/NativePerformance.js index a3c4284e6baa..11e73d19d531 100644 --- a/packages/react-native/src/private/webapis/performance/specs/NativePerformance.js +++ b/packages/react-native/src/private/webapis/performance/specs/NativePerformance.js @@ -57,7 +57,7 @@ export type PerformanceObserverInit = { export interface Spec extends TurboModule { readonly now: () => number; - readonly timeOrigin: () => number; + readonly timeOrigin?: () => number; readonly reportMark: ( name: string, @@ -107,6 +107,4 @@ export interface Spec extends TurboModule { readonly clearEventCountsForTesting: () => void; } -export default TurboModuleRegistry.getEnforcing( - 'NativePerformanceCxx', -) as Spec; +export default TurboModuleRegistry.get('NativePerformanceCxx') as ?Spec;