Skip to content

Assume NativePerformance will be available if the Performance module is loaded #52671

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
3 changes: 2 additions & 1 deletion packages/react-native/Libraries/Core/setUpPerformance.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -86,14 +88,8 @@ function getCachedEventCounts(): Map<string, number> {
return cachedEventCounts;
}

if (!NativePerformance || !NativePerformance?.getEventCounts) {
warnNoNativePerformance();
cachedEventCounts = new Map();
return cachedEventCounts;
}

const eventCounts = new Map<string, number>(
NativePerformance.getEventCounts?.() ?? [],
NativePerformance.getEventCounts() ?? [],
);
cachedEventCounts = eventCounts;

Expand Down
116 changes: 44 additions & 72 deletions packages/react-native/src/private/webapis/performance/Performance.js
Original file line number Diff line number Diff line change
Expand Up @@ -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<{
Expand All @@ -58,11 +59,13 @@ export type PerformanceMeasureOptions =
const ENTRY_TYPES_AVAILABLE_FROM_TIMELINE: $ReadOnlyArray<PerformanceEntryType> =
['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,
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -217,11 +213,6 @@ export default class Performance {
}

clearMarks(markName?: string): void {
if (!cachedNativeClearMarks) {
warnNoNativePerformance();
return;
}

cachedNativeClearMarks(markName);
}

Expand Down Expand Up @@ -414,11 +405,6 @@ export default class Performance {
}

clearMeasures(measureName?: string): void {
if (!cachedNativeClearMeasures) {
warnNoNativePerformance();
return;
}

cachedNativeClearMeasures(measureName);
}

Expand All @@ -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);
}

Expand All @@ -450,11 +432,6 @@ export default class Performance {
return [];
}

if (!NativePerformance?.getEntriesByType) {
warnNoNativePerformance();
return [];
}

return NativePerformance.getEntriesByType(
performanceEntryTypeToRaw(entryType),
).map(rawToPerformanceEntry);
Expand All @@ -472,11 +449,6 @@ export default class Performance {
return [];
}

if (!NativePerformance?.getEntriesByName) {
warnNoNativePerformance();
return [];
}

return NativePerformance.getEntriesByName(
entryName,
entryType != null ? performanceEntryTypeToRaw(entryType) : undefined,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -75,13 +76,6 @@ export interface PerformanceObserverInit {
}

function getSupportedPerformanceEntryTypes(): $ReadOnlyArray<PerformanceEntryType> {
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,
Expand Down Expand Up @@ -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) {
Expand All @@ -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,
Expand All @@ -149,29 +138,19 @@ export class PerformanceObserver {
}

disconnect(): void {
if (!NativePerformance) {
warnNoNativePerformance();
return;
}

if (this.#nativeObserverHandle == null || !NativePerformance.disconnect) {
if (this.#nativeObserverHandle == null) {
return;
}

NativePerformance.disconnect(this.#nativeObserverHandle);
}

#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
);
Expand All @@ -185,7 +164,7 @@ export class PerformanceObserver {
let droppedEntriesCount = 0;
if (!this.#calledAtLeastOnce) {
droppedEntriesCount =
NativePerformance.getDroppedEntriesCount?.(observerHandle) ?? 0;
NativePerformance.getDroppedEntriesCount(observerHandle);
this.#calledAtLeastOnce = true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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;
Expand Down Expand Up @@ -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(() => {
Expand Down
Loading
Loading