diff --git a/packages/react-native/Libraries/Core/setUpPerformance.js b/packages/react-native/Libraries/Core/setUpPerformance.js index 2087c74cc1e5c1..edb00ce81e211b 100644 --- a/packages/react-native/Libraries/Core/setUpPerformance.js +++ b/packages/react-native/Libraries/Core/setUpPerformance.js @@ -8,12 +8,13 @@ * @format */ -import Performance from '../../src/private/webapis/performance/Performance'; import NativePerformance from '../../src/private/webapis/performance/specs/NativePerformance'; // 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) { + const Performance = + require('../../src/private/webapis/performance/Performance').default; // $FlowExpectedError[cannot-write] global.performance = new Performance(); } else { diff --git a/packages/react-native/src/private/webapis/performance/EventTiming.js b/packages/react-native/src/private/webapis/performance/EventTiming.js index 5e2b446dcfdaac..8f96f8d6ba8b65 100644 --- a/packages/react-native/src/private/webapis/performance/EventTiming.js +++ b/packages/react-native/src/private/webapis/performance/EventTiming.js @@ -15,9 +15,11 @@ import type { PerformanceEntryJSON, } from './PerformanceEntry'; -import {warnNoNativePerformance} from './internals/Utilities'; 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, @@ -86,14 +88,8 @@ function getCachedEventCounts(): Map { return cachedEventCounts; } - if (!NativePerformance || !NativePerformance?.getEventCounts) { - warnNoNativePerformance(); - cachedEventCounts = new Map(); - return cachedEventCounts; - } - const eventCounts = new Map( - NativePerformance.getEventCounts?.() ?? [], + NativePerformance.getEventCounts() ?? [], ); cachedEventCounts = eventCounts; diff --git a/packages/react-native/src/private/webapis/performance/Performance.js b/packages/react-native/src/private/webapis/performance/Performance.js index 789e901a14ec6b..c652481cd376cb 100644 --- a/packages/react-native/src/private/webapis/performance/Performance.js +++ b/packages/react-native/src/private/webapis/performance/Performance.js @@ -35,8 +35,9 @@ import { } 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<{ @@ -58,11 +59,13 @@ export type PerformanceMeasureOptions = const ENTRY_TYPES_AVAILABLE_FROM_TIMELINE: $ReadOnlyArray = ['mark', 'measure']; -const cachedReportMark = NativePerformance?.reportMark; -const cachedReportMeasure = NativePerformance?.reportMeasure; -const cachedGetMarkTime = NativePerformance?.getMarkTime; -const cachedNativeClearMarks = NativePerformance?.clearMarks; -const cachedNativeClearMeasures = NativePerformance?.clearMeasures; +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 MARK_OPTIONS_REUSABLE_OBJECT: {...PerformanceMarkOptions} = { startTime: 0, @@ -98,53 +101,46 @@ export default class Performance { // Get the current JS memory information. get memory(): MemoryInfo { - if (NativePerformance?.getSimpleMemoryInfo) { - // JSI API implementations may have different variants of names for the JS - // heap information we need here. We will parse the result based on our - // guess of the implementation for now. - const memoryInfo = NativePerformance.getSimpleMemoryInfo(); - if (memoryInfo.hasOwnProperty('hermes_heapSize')) { - // We got memory information from Hermes - const { - hermes_heapSize: totalJSHeapSize, - hermes_allocatedBytes: usedJSHeapSize, - } = memoryInfo; - - return new MemoryInfo({ - jsHeapSizeLimit: null, // We don't know the heap size limit from Hermes. - totalJSHeapSize, - usedJSHeapSize, - }); - } else { - // JSC and V8 has no native implementations for memory information in JSI::Instrumentation - return new MemoryInfo(); - } + // JSI API implementations may have different variants of names for the JS + // heap information we need here. We will parse the result based on our + // guess of the implementation for now. + const memoryInfo = NativePerformance.getSimpleMemoryInfo(); + if (memoryInfo.hasOwnProperty('hermes_heapSize')) { + // We got memory information from Hermes + const { + hermes_heapSize: totalJSHeapSize, + hermes_allocatedBytes: usedJSHeapSize, + } = memoryInfo; + + return new MemoryInfo({ + jsHeapSizeLimit: null, // We don't know the heap size limit from Hermes. + totalJSHeapSize, + usedJSHeapSize, + }); + } else { + // JSC and V8 has no native implementations for memory information in JSI::Instrumentation + return new MemoryInfo(); } - - return new MemoryInfo(); } // Startup metrics is not used in web, but only in React Native. get rnStartupTiming(): ReactNativeStartupTiming { - if (NativePerformance?.getReactNativeStartupTiming) { - const { - startTime, - endTime, - initializeRuntimeStart, - initializeRuntimeEnd, - executeJavaScriptBundleEntryPointStart, - executeJavaScriptBundleEntryPointEnd, - } = NativePerformance.getReactNativeStartupTiming(); - return new ReactNativeStartupTiming({ - startTime, - endTime, - initializeRuntimeStart, - initializeRuntimeEnd, - executeJavaScriptBundleEntryPointStart, - executeJavaScriptBundleEntryPointEnd, - }); - } - return new ReactNativeStartupTiming(); + const { + startTime, + endTime, + initializeRuntimeStart, + initializeRuntimeEnd, + executeJavaScriptBundleEntryPointStart, + executeJavaScriptBundleEntryPointEnd, + } = NativePerformance.getReactNativeStartupTiming(); + return new ReactNativeStartupTiming({ + startTime, + endTime, + initializeRuntimeStart, + initializeRuntimeEnd, + executeJavaScriptBundleEntryPointStart, + executeJavaScriptBundleEntryPointEnd, + }); } mark( @@ -217,11 +213,6 @@ export default class Performance { } clearMarks(markName?: string): void { - if (!cachedNativeClearMarks) { - warnNoNativePerformance(); - return; - } - cachedNativeClearMarks(markName); } @@ -414,11 +405,6 @@ export default class Performance { } clearMeasures(measureName?: string): void { - if (!cachedNativeClearMeasures) { - warnNoNativePerformance(); - return; - } - cachedNativeClearMeasures(measureName); } @@ -434,10 +420,6 @@ export default class Performance { * https://www.w3.org/TR/performance-timeline/#extensions-to-the-performance-interface */ getEntries(): PerformanceEntryList { - if (!NativePerformance?.getEntries) { - warnNoNativePerformance(); - return []; - } return NativePerformance.getEntries().map(rawToPerformanceEntry); } @@ -450,11 +432,6 @@ export default class Performance { return []; } - if (!NativePerformance?.getEntriesByType) { - warnNoNativePerformance(); - return []; - } - return NativePerformance.getEntriesByType( performanceEntryTypeToRaw(entryType), ).map(rawToPerformanceEntry); @@ -472,11 +449,6 @@ export default class Performance { return []; } - if (!NativePerformance?.getEntriesByName) { - warnNoNativePerformance(); - return []; - } - return NativePerformance.getEntriesByName( entryName, entryType != null ? performanceEntryTypeToRaw(entryType) : undefined, diff --git a/packages/react-native/src/private/webapis/performance/PerformanceObserver.js b/packages/react-native/src/private/webapis/performance/PerformanceObserver.js index 6870d4a9f2a4f5..c576a4451f6658 100644 --- a/packages/react-native/src/private/webapis/performance/PerformanceObserver.js +++ b/packages/react-native/src/private/webapis/performance/PerformanceObserver.js @@ -21,12 +21,13 @@ import { rawToPerformanceEntry, rawToPerformanceEntryType, } from './internals/RawPerformanceEntry'; -import {warnNoNativePerformance} from './internals/Utilities'; -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; @@ -75,13 +76,6 @@ export interface PerformanceObserverInit { } function getSupportedPerformanceEntryTypes(): $ReadOnlyArray { - if (!NativePerformance) { - return Object.freeze([]); - } - if (!NativePerformance.getSupportedPerformanceEntryTypes) { - // fallback if getSupportedPerformanceEntryTypes is not defined on native side - return Object.freeze(['mark', 'measure', 'event']); - } return Object.freeze( NativePerformance.getSupportedPerformanceEntryTypes().map( rawToPerformanceEntryType, @@ -120,11 +114,6 @@ export class PerformanceObserver { } observe(options: PerformanceObserverInit): void { - if (!NativePerformance || NativePerformance.observe == null) { - warnNoNativePerformance(); - return; - } - this.#validateObserveOptions(options); if (this.#nativeObserverHandle == null) { @@ -135,12 +124,12 @@ export class PerformanceObserver { if (options.entryTypes) { this.#type = 'multiple'; - NativePerformance.observe?.(observerHandle, { + NativePerformance.observe(observerHandle, { entryTypes: options.entryTypes.map(performanceEntryTypeToRaw), }); } else if (options.type) { this.#type = 'single'; - NativePerformance.observe?.(observerHandle, { + NativePerformance.observe(observerHandle, { type: performanceEntryTypeToRaw(options.type), buffered: options.buffered, durationThreshold: options.durationThreshold, @@ -149,12 +138,7 @@ export class PerformanceObserver { } disconnect(): void { - if (!NativePerformance) { - warnNoNativePerformance(); - return; - } - - if (this.#nativeObserverHandle == null || !NativePerformance.disconnect) { + if (this.#nativeObserverHandle == null) { return; } @@ -162,16 +146,11 @@ export class PerformanceObserver { } #createNativeObserver(): OpaqueNativeObserverHandle | null { - if (!NativePerformance || !NativePerformance.createObserver) { - warnNoNativePerformance(); - return null; - } - this.#calledAtLeastOnce = false; const observerHandle: OpaqueNativeObserverHandle = NativePerformance.createObserver(() => { - const rawEntries = NativePerformance.takeRecords?.( + const rawEntries = NativePerformance.takeRecords( observerHandle, true, // sort records ); @@ -185,7 +164,7 @@ export class PerformanceObserver { let droppedEntriesCount = 0; if (!this.#calledAtLeastOnce) { droppedEntriesCount = - NativePerformance.getDroppedEntriesCount?.(observerHandle) ?? 0; + NativePerformance.getDroppedEntriesCount(observerHandle); this.#calledAtLeastOnce = true; } diff --git a/packages/react-native/src/private/webapis/performance/__tests__/EventTimingAPI-itest.js b/packages/react-native/src/private/webapis/performance/__tests__/EventTimingAPI-itest.js index 9f9e6615ccf6ff..5c46106f95d7fb 100644 --- a/packages/react-native/src/private/webapis/performance/__tests__/EventTimingAPI-itest.js +++ b/packages/react-native/src/private/webapis/performance/__tests__/EventTimingAPI-itest.js @@ -13,7 +13,7 @@ import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment'; import type Performance from 'react-native/src/private/webapis/performance/Performance'; import type {PerformanceObserverEntryList} from 'react-native/src/private/webapis/performance/PerformanceObserver'; -import NativePerformance from '../specs/NativePerformance'; +import MaybeNativePerformance from '../specs/NativePerformance'; import * as Fantom from '@react-native/fantom'; import nullthrows from 'nullthrows'; import {useState} from 'react'; @@ -22,6 +22,8 @@ import setUpPerformanceObserver from 'react-native/src/private/setup/setUpPerfor import {PerformanceEventTiming} from 'react-native/src/private/webapis/performance/EventTiming'; import {PerformanceObserver} from 'react-native/src/private/webapis/performance/PerformanceObserver'; +const NativePerformance = nullthrows(MaybeNativePerformance); + setUpPerformanceObserver(); declare var performance: Performance; @@ -193,7 +195,7 @@ describe('Event Timing API', () => { }); it('reports number of dispatched events via performance.eventCounts', () => { - NativePerformance?.clearEventCountsForTesting?.(); + NativePerformance.clearEventCountsForTesting?.(); const root = Fantom.createRoot(); Fantom.runTask(() => { diff --git a/packages/react-native/src/private/webapis/performance/__tests__/UserTiming-itest.js b/packages/react-native/src/private/webapis/performance/__tests__/UserTiming-itest.js index 85fa6443510613..e1a469740062f8 100644 --- a/packages/react-native/src/private/webapis/performance/__tests__/UserTiming-itest.js +++ b/packages/react-native/src/private/webapis/performance/__tests__/UserTiming-itest.js @@ -18,8 +18,11 @@ import type { import ensureInstance from '../../../__tests__/utilities/ensureInstance'; import DOMException from '../../errors/DOMException'; -import NativePerformance from '../specs/NativePerformance'; +import MaybeNativePerformance from '../specs/NativePerformance'; import {PerformanceMark, PerformanceMeasure} from '../UserTiming'; +import nullthrows from 'nullthrows'; + +const NativePerformance = nullthrows(MaybeNativePerformance); declare var performance: Performance; @@ -44,7 +47,7 @@ describe('User Timing', () => { describe('mark', () => { it('works with default timestamp', () => { - NativePerformance?.setCurrentTimeStampForTesting?.(25); + NativePerformance.setCurrentTimeStampForTesting?.(25); const mark = performance.mark('mark-now'); @@ -144,7 +147,7 @@ describe('User Timing', () => { describe('measure', () => { describe('with measureOptions', () => { it('uses 0 as default start and now as default end', () => { - NativePerformance?.setCurrentTimeStampForTesting?.(25); + NativePerformance.setCurrentTimeStampForTesting?.(25); const measure = performance.measure('measure-with-defaults', {}); @@ -157,7 +160,7 @@ describe('User Timing', () => { }); it('works with a start timestamp', () => { - NativePerformance?.setCurrentTimeStampForTesting?.(25); + NativePerformance.setCurrentTimeStampForTesting?.(25); const measure = performance.measure('measure-with-start-timestamp', { start: 10, @@ -172,7 +175,7 @@ describe('User Timing', () => { }); it('works with start mark', () => { - NativePerformance?.setCurrentTimeStampForTesting?.(25); + NativePerformance.setCurrentTimeStampForTesting?.(25); performance.mark('start-mark', { startTime: 10, @@ -191,7 +194,7 @@ describe('User Timing', () => { }); it('works with end mark', () => { - NativePerformance?.setCurrentTimeStampForTesting?.(25); + NativePerformance.setCurrentTimeStampForTesting?.(25); performance.mark('end-mark', { startTime: 50, @@ -210,7 +213,7 @@ describe('User Timing', () => { }); it('works with start mark and end mark', () => { - NativePerformance?.setCurrentTimeStampForTesting?.(25); + NativePerformance.setCurrentTimeStampForTesting?.(25); performance.mark('start-mark', { startTime: 10, @@ -375,7 +378,7 @@ describe('User Timing', () => { describe('with startMark / endMark', () => { it('uses 0 as default start and now as default end', () => { - NativePerformance?.setCurrentTimeStampForTesting?.(25); + NativePerformance.setCurrentTimeStampForTesting?.(25); const measure = performance.measure('measure-with-defaults'); @@ -388,7 +391,7 @@ describe('User Timing', () => { }); it('works with startMark', () => { - NativePerformance?.setCurrentTimeStampForTesting?.(25); + NativePerformance.setCurrentTimeStampForTesting?.(25); performance.mark('start-mark', { startTime: 10, @@ -408,7 +411,7 @@ describe('User Timing', () => { }); it('works with startMark and endMark', () => { - NativePerformance?.setCurrentTimeStampForTesting?.(25); + NativePerformance.setCurrentTimeStampForTesting?.(25); performance.mark('start-mark', { startTime: 10, 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 eeb08d788e0d49..640138cee0abb3 100644 --- a/packages/react-native/src/private/webapis/performance/specs/NativePerformance.js +++ b/packages/react-native/src/private/webapis/performance/specs/NativePerformance.js @@ -53,7 +53,7 @@ export type PerformanceObserverInit = { }; export interface Spec extends TurboModule { - +now?: () => number; + +now: () => number; +reportMark?: (name: string, startTime: number, entry: mixed) => void; +reportMeasure?: ( name: string, @@ -62,36 +62,36 @@ export interface Spec extends TurboModule { entry: mixed, ) => void; +getMarkTime?: (name: string) => ?number; - +clearMarks?: (entryName?: string) => void; - +clearMeasures?: (entryName?: string) => void; - +getEntries?: () => $ReadOnlyArray; - +getEntriesByName?: ( + +clearMarks: (entryName?: string) => void; + +clearMeasures: (entryName?: string) => void; + +getEntries: () => $ReadOnlyArray; + +getEntriesByName: ( entryName: string, entryType?: ?RawPerformanceEntryType, ) => $ReadOnlyArray; - +getEntriesByType?: ( + +getEntriesByType: ( entryType: RawPerformanceEntryType, ) => $ReadOnlyArray; - +getEventCounts?: () => $ReadOnlyArray<[string, number]>; + +getEventCounts: () => $ReadOnlyArray<[string, number]>; +getSimpleMemoryInfo: () => NativeMemoryInfo; +getReactNativeStartupTiming: () => ReactNativeStartupTiming; - +createObserver?: ( + +createObserver: ( callback: NativeBatchedObserverCallback, ) => OpaqueNativeObserverHandle; - +getDroppedEntriesCount?: (observer: OpaqueNativeObserverHandle) => number; + +getDroppedEntriesCount: (observer: OpaqueNativeObserverHandle) => number; - +observe?: ( + +observe: ( observer: OpaqueNativeObserverHandle, options: PerformanceObserverInit, ) => void; - +disconnect?: (observer: OpaqueNativeObserverHandle) => void; - +takeRecords?: ( + +disconnect: (observer: OpaqueNativeObserverHandle) => void; + +takeRecords: ( observer: OpaqueNativeObserverHandle, sort: boolean, ) => $ReadOnlyArray; - +getSupportedPerformanceEntryTypes?: () => $ReadOnlyArray; + +getSupportedPerformanceEntryTypes: () => $ReadOnlyArray; +setCurrentTimeStampForTesting?: (timeStamp: number) => void; +clearEventCountsForTesting?: () => void;