From 57fb3ff2c1f713c75da458565a18d98e8ee585dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Mon, 20 Jul 2026 03:52:48 -0700 Subject: [PATCH] Re-apply "Require the NativePerformance module and drop the legacy performance fallback" (#57609) Summary: Pull Request resolved: https://github.com/react/react-native/pull/57609 This re-applies a previously landed change that was reverted. It requires the NativePerformance module and removes the legacy `performance` fallback path. `setUpPerformance` previously branched between the modern Performance API setup and a barebones `performance` stub depending on whether the native Performance module was available. The native module is now always present in supported runtimes, so this consolidates everything on the modern setup and removes the dead fallback path. - `NativePerformance` is now resolved via `getEnforcing` instead of a nullable `get`, and its `timeOrigin` method is no longer optional (the native module always implements it). - Removed the legacy `performance` stub and the `global.nativePerformanceNow` fallbacks, along with the now-redundant null checks and the `getCurrentTimeStamp` indirection (native `now`/`timeOrigin` are cached directly). - `setUpPerformance` is kept as a thin alias of the internal setup module so external callers keep working. Changelog: [Internal] Reviewed By: huntie Differential Revision: D112566045 --- .../Libraries/Core/setUpPerformance.js | 25 ++--------- .../setUpDefaultReactNativeEnvironment.js | 4 +- ...rformanceModern.js => setUpPerformance.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, 33 insertions(+), 90 deletions(-) rename packages/react-native/src/private/setup/{setUpPerformanceModern.js => setUpPerformance.js} (97%) delete 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 3f91689eabbf..1a3eaef5abd1 100644 --- a/packages/react-native/Libraries/Core/setUpPerformance.js +++ b/packages/react-native/Libraries/Core/setUpPerformance.js @@ -8,25 +8,8 @@ * @format */ -import setUpPerformanceModern from '../../src/private/setup/setUpPerformanceModern'; -import NativePerformance from '../../src/private/webapis/performance/specs/NativePerformance'; +// Kept as an alias of the internal `setUpPerformance` module for external +// consumers that import this path directly. +import setUpPerformance from '../../src/private/setup/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(); - }, - }; - } -} +setUpPerformance(); diff --git a/packages/react-native/src/private/setup/setUpDefaultReactNativeEnvironment.js b/packages/react-native/src/private/setup/setUpDefaultReactNativeEnvironment.js index eb61a6644345..6bd27c4cc3d1 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 setUpDefaltReactNativeEnvironment( +export default function setUpDefaultReactNativeEnvironment( enableDeveloperTools: boolean = true, ) { if (initialized) { @@ -21,7 +21,7 @@ export default function setUpDefaltReactNativeEnvironment( require('../../../Libraries/Core/setUpGlobals'); require('./setUpDOM').default(); - require('../../../Libraries/Core/setUpPerformance'); + require('./setUpPerformance').default(); require('../../../Libraries/Core/polyfillPromise'); require('../../../Libraries/Core/setUpTimers'); if (__DEV__ && enableDeveloperTools) { diff --git a/packages/react-native/src/private/setup/setUpPerformanceModern.js b/packages/react-native/src/private/setup/setUpPerformance.js similarity index 97% rename from packages/react-native/src/private/setup/setUpPerformanceModern.js rename to packages/react-native/src/private/setup/setUpPerformance.js index 22a7ca135043..747b92f5f577 100644 --- a/packages/react-native/src/private/setup/setUpPerformanceModern.js +++ b/packages/react-native/src/private/setup/setUpPerformance.js @@ -12,7 +12,7 @@ import {polyfillGlobal} from '../../../Libraries/Utilities/PolyfillFunctions'; let initialized = false; -export default function setUpPerformanceModern() { +export default function setUpPerformance() { 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 c175f8592f18..713964ea4f37 100644 --- a/packages/react-native/src/private/webapis/performance/EventTiming.js +++ b/packages/react-native/src/private/webapis/performance/EventTiming.js @@ -16,10 +16,7 @@ import type { } from './PerformanceEntry'; import {PerformanceEntry} from './PerformanceEntry'; -import MaybeNativePerformance from './specs/NativePerformance'; -import nullthrows from 'nullthrows'; - -const NativePerformance = nullthrows(MaybeNativePerformance); +import NativePerformance from './specs/NativePerformance'; 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 6a492064fb43..e6f544475d3a 100644 --- a/packages/react-native/src/private/webapis/performance/Performance.js +++ b/packages/react-native/src/private/webapis/performance/Performance.js @@ -29,12 +29,10 @@ import { performanceEntryTypeToRaw, rawToPerformanceEntry, } from './internals/RawPerformanceEntry'; -import {getCurrentTimeStamp} from './internals/Utilities'; import MemoryInfo from './MemoryInfo'; import ReactNativeStartupTiming from './ReactNativeStartupTiming'; -import MaybeNativePerformance from './specs/NativePerformance'; +import NativePerformance from './specs/NativePerformance'; import {PerformanceMark, PerformanceMeasure} from './UserTiming'; -import nullthrows from 'nullthrows'; export type PerformanceMeasureOptions = | Readonly<{ @@ -56,13 +54,8 @@ export type PerformanceMeasureOptions = const ENTRY_TYPES_AVAILABLE_FROM_TIMELINE: ReadonlyArray = ['mark', 'measure']; -const NativePerformance = nullthrows(MaybeNativePerformance); - -const cachedReportMark = NativePerformance.reportMark; -const cachedReportMeasure = NativePerformance.reportMeasure; -const cachedGetMarkTime = NativePerformance.getMarkTime; -const cachedNativeClearMarks = NativePerformance.clearMarks; -const cachedNativeClearMeasures = NativePerformance.clearMeasures; +const {now, reportMark, reportMeasure, getMarkTime, clearMarks, clearMeasures} = + NativePerformance; let cachedTimeOrigin: ?DOMHighResTimeStamp; const MARK_OPTIONS_REUSABLE_OBJECT: PerformanceMarkOptions = { @@ -78,7 +71,7 @@ const MEASURE_OPTIONS_REUSABLE_OBJECT: PerformanceMeasureInit = { }; const getMarkTimeForMeasure = (markName: string): number => { - const markTime = cachedGetMarkTime(markName); + const markTime = getMarkTime(markName); if (markTime == null) { throw new DOMException( `Failed to execute 'measure' on 'Performance': The mark '${markName}' does not exist.`, @@ -147,12 +140,7 @@ export default class Performance { */ get timeOrigin(): DOMHighResTimeStamp { if (cachedTimeOrigin == null) { - if (NativePerformance.timeOrigin) { - cachedTimeOrigin = NativePerformance?.timeOrigin(); - } else { - // Very naive polyfill. - cachedTimeOrigin = Date.now() - getCurrentTimeStamp(); - } + cachedTimeOrigin = NativePerformance.timeOrigin(); } return cachedTimeOrigin; @@ -202,7 +190,7 @@ export default class Performance { ); } } else { - resolvedStartTime = getCurrentTimeStamp(); + resolvedStartTime = now(); } if (detail !== undefined) { @@ -219,13 +207,13 @@ export default class Performance { MARK_OPTIONS_REUSABLE_OBJECT, ); - cachedReportMark(resolvedMarkName, resolvedStartTime, entry); + reportMark(resolvedMarkName, resolvedStartTime, entry); return entry; } clearMarks(markName?: string): void { - cachedNativeClearMarks(markName); + clearMarks(markName); } measure( @@ -347,7 +335,7 @@ export default class Performance { ) { resolvedDuration = resolvedEndTime - resolvedStartTime; } else { - resolvedDuration = getCurrentTimeStamp() - resolvedStartTime; + resolvedDuration = now() - resolvedStartTime; } } @@ -364,7 +352,7 @@ export default class Performance { resolvedDuration = getMarkTimeForMeasure(endMark) - resolvedStartTime; } else { - resolvedDuration = getCurrentTimeStamp() - resolvedStartTime; + resolvedDuration = now() - resolvedStartTime; } break; } @@ -375,7 +363,7 @@ export default class Performance { resolvedDuration = getMarkTimeForMeasure(endMark) - resolvedStartTime; } else { - resolvedDuration = getCurrentTimeStamp() - resolvedStartTime; + resolvedDuration = now() - resolvedStartTime; } } } @@ -385,7 +373,7 @@ export default class Performance { if (endMark !== undefined) { resolvedDuration = getMarkTimeForMeasure(endMark) - resolvedStartTime; } else { - resolvedDuration = getCurrentTimeStamp() - resolvedStartTime; + resolvedDuration = now() - resolvedStartTime; } } @@ -400,7 +388,7 @@ export default class Performance { const entry = new PerformanceMeasure(MEASURE_OPTIONS_REUSABLE_OBJECT); - cachedReportMeasure( + reportMeasure( resolvedMeasureName, resolvedStartTime, resolvedDuration, @@ -411,14 +399,14 @@ export default class Performance { } clearMeasures(measureName?: string): void { - cachedNativeClearMeasures(measureName); + clearMeasures(measureName); } /** * Returns a double, measured in milliseconds. * https://developer.mozilla.org/en-US/docs/Web/API/Performance/now */ - now: () => DOMHighResTimeStamp = getCurrentTimeStamp; + now: () => DOMHighResTimeStamp = now; /** * 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 b66e7f406614..e4d899a3cb72 100644 --- a/packages/react-native/src/private/webapis/performance/PerformanceObserver.js +++ b/packages/react-native/src/private/webapis/performance/PerformanceObserver.js @@ -21,13 +21,11 @@ import { rawToPerformanceEntry, rawToPerformanceEntryType, } from './internals/RawPerformanceEntry'; -import MaybeNativePerformance from './specs/NativePerformance'; +import NativePerformance 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 ff8786083194..f07b48c763bb 100644 --- a/packages/react-native/src/private/webapis/performance/UserTiming.js +++ b/packages/react-native/src/private/webapis/performance/UserTiming.js @@ -18,8 +18,10 @@ 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 @@ -47,7 +49,7 @@ class PerformanceMarkTemplate extends PerformanceEntry { constructor(markName: string, markOptions?: PerformanceMarkOptions) { super('mark', { name: markName, - startTime: markOptions?.startTime ?? getCurrentTimeStamp(), + startTime: markOptions?.startTime ?? now(), duration: 0, }); @@ -73,7 +75,7 @@ export const PerformanceMark: typeof PerformanceMarkTemplate = ) { this.__entryType = 'mark'; this.__name = markName; - this.__startTime = markOptions?.startTime ?? getCurrentTimeStamp(); + this.__startTime = markOptions?.startTime ?? now(); 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 deleted file mode 100644 index 929afbe51282..000000000000 --- a/packages/react-native/src/private/webapis/performance/internals/Utilities.js +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 11e73d19d531..a3c4284e6baa 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,4 +107,6 @@ export interface Spec extends TurboModule { readonly clearEventCountsForTesting: () => void; } -export default TurboModuleRegistry.get('NativePerformanceCxx') as ?Spec; +export default TurboModuleRegistry.getEnforcing( + 'NativePerformanceCxx', +) as Spec;