From bb508a4d942e31d2bcb39073ba338a9770d9e04a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Mon, 1 Sep 2025 09:18:19 -0700 Subject: [PATCH] Refactor PerformanceEntry and subclasses to use interfaces for initialization (#53429) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/53429 Changelog: [internal] This is a refactor of the types in `PerformanceEntry` and subclasses to accept interfaces instead of objects. This allows us to pass down the init object from subclasses to the superclass without having to create intermediate objects. Additionally, this is also more semantically correct, as existing APIs don't need those options to be own properties of the init object. Existing benchmark for Performance doesn't show any significant impact. Reviewed By: rshest Differential Revision: D80800075 fbshipit-source-id: ab439d70f4db9ce60e3089d89ccb105a91e7ef48 --- .../webapis/performance/EventTiming.js | 25 ++++------ .../private/webapis/performance/LongTasks.js | 12 ++++- .../webapis/performance/Performance.js | 17 ++++--- .../webapis/performance/PerformanceEntry.js | 17 ++++--- .../webapis/performance/ResourceTiming.js | 34 ++++++------- .../private/webapis/performance/UserTiming.js | 49 ++++++++----------- .../internals/RawPerformanceEntry.js | 7 ++- 7 files changed, 80 insertions(+), 81 deletions(-) diff --git a/packages/react-native/src/private/webapis/performance/EventTiming.js b/packages/react-native/src/private/webapis/performance/EventTiming.js index 3f99141e223..95657a3b042 100644 --- a/packages/react-native/src/private/webapis/performance/EventTiming.js +++ b/packages/react-native/src/private/webapis/performance/EventTiming.js @@ -9,9 +9,9 @@ */ // flowlint unsafe-getters-setters:off - import type { DOMHighResTimeStamp, + PerformanceEntryInit, PerformanceEntryJSON, } from './PerformanceEntry'; @@ -29,25 +29,20 @@ export type PerformanceEventTimingJSON = { ... }; +export interface PerformanceEventTimingInit extends PerformanceEntryInit { + +processingStart?: DOMHighResTimeStamp; + +processingEnd?: DOMHighResTimeStamp; + +interactionId?: number; +} + export class PerformanceEventTiming extends PerformanceEntry { #processingStart: DOMHighResTimeStamp; #processingEnd: DOMHighResTimeStamp; #interactionId: number; - constructor(init: { - name: string, - startTime?: DOMHighResTimeStamp, - duration?: DOMHighResTimeStamp, - processingStart?: DOMHighResTimeStamp, - processingEnd?: DOMHighResTimeStamp, - interactionId?: number, - }) { - super({ - name: init.name, - entryType: 'event', - startTime: init.startTime ?? 0, - duration: init.duration ?? 0, - }); + constructor(init: PerformanceEventTimingInit) { + super('event', init); + this.#processingStart = init.processingStart ?? 0; this.#processingEnd = init.processingEnd ?? 0; this.#interactionId = init.interactionId ?? 0; diff --git a/packages/react-native/src/private/webapis/performance/LongTasks.js b/packages/react-native/src/private/webapis/performance/LongTasks.js index 4c745db150e..9c27419dd07 100644 --- a/packages/react-native/src/private/webapis/performance/LongTasks.js +++ b/packages/react-native/src/private/webapis/performance/LongTasks.js @@ -9,8 +9,10 @@ */ // flowlint unsafe-getters-setters:off - -import type {PerformanceEntryJSON} from './PerformanceEntry'; +import type { + PerformanceEntryInit, + PerformanceEntryJSON, +} from './PerformanceEntry'; import {PerformanceEntry} from './PerformanceEntry'; @@ -25,7 +27,13 @@ export class TaskAttributionTiming extends PerformanceEntry {} const EMPTY_ATTRIBUTION: $ReadOnlyArray = Object.preventExtensions([]); +export interface PerformanceLongTaskTimingInit extends PerformanceEntryInit {} + export class PerformanceLongTaskTiming extends PerformanceEntry { + constructor(init: PerformanceEntryInit) { + super('longtask', init); + } + get attribution(): $ReadOnlyArray { return EMPTY_ATTRIBUTION; } diff --git a/packages/react-native/src/private/webapis/performance/Performance.js b/packages/react-native/src/private/webapis/performance/Performance.js index 65c66dd2359..6cd2fd5b0a7 100644 --- a/packages/react-native/src/private/webapis/performance/Performance.js +++ b/packages/react-native/src/private/webapis/performance/Performance.js @@ -64,12 +64,13 @@ const cachedGetMarkTime = NativePerformance.getMarkTime; const cachedNativeClearMarks = NativePerformance.clearMarks; const cachedNativeClearMeasures = NativePerformance.clearMeasures; -const MARK_OPTIONS_REUSABLE_OBJECT: {...PerformanceMarkOptions} = { +const MARK_OPTIONS_REUSABLE_OBJECT: PerformanceMarkOptions = { startTime: 0, detail: undefined, }; -const MEASURE_OPTIONS_REUSABLE_OBJECT: {...PerformanceMeasureInit} = { +const MEASURE_OPTIONS_REUSABLE_OBJECT: PerformanceMeasureInit = { + name: '', startTime: 0, duration: 0, detail: undefined, @@ -189,7 +190,9 @@ export default class Performance { resolvedDetail = structuredClone(detail); } + // $FlowExpectedError[cannot-write] MARK_OPTIONS_REUSABLE_OBJECT.startTime = resolvedStartTime; + // $FlowExpectedError[cannot-write] MARK_OPTIONS_REUSABLE_OBJECT.detail = resolvedDetail; const entry = new PerformanceMark( @@ -367,14 +370,16 @@ export default class Performance { } } + // $FlowExpectedError[cannot-write] + MEASURE_OPTIONS_REUSABLE_OBJECT.name = resolvedMeasureName; + // $FlowExpectedError[cannot-write] MEASURE_OPTIONS_REUSABLE_OBJECT.startTime = resolvedStartTime; + // $FlowExpectedError[cannot-write] MEASURE_OPTIONS_REUSABLE_OBJECT.duration = resolvedDuration; + // $FlowExpectedError[cannot-write] MEASURE_OPTIONS_REUSABLE_OBJECT.detail = resolvedDetail; - const entry = new PerformanceMeasure( - resolvedMeasureName, - MEASURE_OPTIONS_REUSABLE_OBJECT, - ); + const entry = new PerformanceMeasure(MEASURE_OPTIONS_REUSABLE_OBJECT); cachedReportMeasure( resolvedMeasureName, diff --git a/packages/react-native/src/private/webapis/performance/PerformanceEntry.js b/packages/react-native/src/private/webapis/performance/PerformanceEntry.js index f2ddfceda55..e5184c28399 100644 --- a/packages/react-native/src/private/webapis/performance/PerformanceEntry.js +++ b/packages/react-native/src/private/webapis/performance/PerformanceEntry.js @@ -28,24 +28,25 @@ export type PerformanceEntryJSON = { ... }; +export interface PerformanceEntryInit { + +name: string; + +startTime: DOMHighResTimeStamp; + +duration: DOMHighResTimeStamp; +} + export class PerformanceEntry { // We don't use private fields because they're significantly slower to // initialize on construction and to access. // We also need these to be protected so they can be initialized in subclasses // where we avoid calling `super()` for performance reasons. - __name: string; __entryType: PerformanceEntryType; + __name: string; __startTime: DOMHighResTimeStamp; __duration: DOMHighResTimeStamp; - constructor(init: { - name: string, - entryType: PerformanceEntryType, - startTime: DOMHighResTimeStamp, - duration: DOMHighResTimeStamp, - }) { + constructor(entryType: PerformanceEntryType, init: PerformanceEntryInit) { + this.__entryType = entryType; this.__name = init.name; - this.__entryType = init.entryType; this.__startTime = init.startTime; this.__duration = init.duration; } diff --git a/packages/react-native/src/private/webapis/performance/ResourceTiming.js b/packages/react-native/src/private/webapis/performance/ResourceTiming.js index 0f02887250b..65440e00a8a 100644 --- a/packages/react-native/src/private/webapis/performance/ResourceTiming.js +++ b/packages/react-native/src/private/webapis/performance/ResourceTiming.js @@ -29,6 +29,19 @@ export type PerformanceResourceTimingJSON = { ... }; +export interface PerformanceResourceTimingInit { + +name: string; + +startTime: DOMHighResTimeStamp; + +duration: DOMHighResTimeStamp; + +fetchStart: DOMHighResTimeStamp; + +requestStart: DOMHighResTimeStamp; + +connectStart: DOMHighResTimeStamp; + +connectEnd: DOMHighResTimeStamp; + +responseStart: DOMHighResTimeStamp; + +responseEnd: DOMHighResTimeStamp; + +responseStatus?: number; +} + export class PerformanceResourceTiming extends PerformanceEntry { #fetchStart: DOMHighResTimeStamp; #requestStart: DOMHighResTimeStamp; @@ -38,24 +51,9 @@ export class PerformanceResourceTiming extends PerformanceEntry { #responseEnd: DOMHighResTimeStamp; #responseStatus: ?number; - constructor(init: { - name: string, - startTime: DOMHighResTimeStamp, - duration: DOMHighResTimeStamp, - fetchStart: DOMHighResTimeStamp, - requestStart: DOMHighResTimeStamp, - connectStart: DOMHighResTimeStamp, - connectEnd: DOMHighResTimeStamp, - responseStart: DOMHighResTimeStamp, - responseEnd: DOMHighResTimeStamp, - responseStatus?: number, - }) { - super({ - name: init.name, - entryType: 'resource', - startTime: init.startTime, - duration: init.duration, - }); + constructor(init: PerformanceResourceTimingInit) { + super('resource', init); + this.#fetchStart = init.fetchStart; this.#requestStart = init.requestStart; this.#connectStart = init.connectStart; diff --git a/packages/react-native/src/private/webapis/performance/UserTiming.js b/packages/react-native/src/private/webapis/performance/UserTiming.js index c52df24e92b..910f6128d5d 100644 --- a/packages/react-native/src/private/webapis/performance/UserTiming.js +++ b/packages/react-native/src/private/webapis/performance/UserTiming.js @@ -9,8 +9,10 @@ */ // flowlint unsafe-getters-setters:off - -import type {DOMHighResTimeStamp} from './PerformanceEntry'; +import type { + DOMHighResTimeStamp, + PerformanceEntryInit, +} from './PerformanceEntry'; import type { ExtensionMarkerPayload, ExtensionTrackEntryPayload, @@ -25,18 +27,16 @@ export type DetailType = // but we'll use it as documentation for how to use the extensibility API. | {devtools?: ExtensionMarkerPayload | ExtensionTrackEntryPayload, ...}; -export type PerformanceMarkOptions = $ReadOnly<{ - detail?: DetailType, - startTime?: DOMHighResTimeStamp, -}>; +export interface PerformanceMarkOptions { + +detail?: DetailType; + +startTime?: DOMHighResTimeStamp; +} export type TimeStampOrName = DOMHighResTimeStamp | string; -export type PerformanceMeasureInit = $ReadOnly<{ - detail?: DetailType, - startTime: DOMHighResTimeStamp, - duration: DOMHighResTimeStamp, -}>; +export interface PerformanceMeasureInit extends PerformanceEntryInit { + +detail?: DetailType; +} class PerformanceMarkTemplate extends PerformanceEntry { // We don't use private fields because they're significantly slower to @@ -45,9 +45,8 @@ class PerformanceMarkTemplate extends PerformanceEntry { // This constructor isn't really used. See `PerformanceMark` below. constructor(markName: string, markOptions?: PerformanceMarkOptions) { - super({ + super('mark', { name: markName, - entryType: 'mark', startTime: markOptions?.startTime ?? getCurrentTimeStamp(), duration: 0, }); @@ -72,8 +71,8 @@ export const PerformanceMark: typeof PerformanceMarkTemplate = markName: string, markOptions?: PerformanceMarkOptions, ) { - this.__name = markName; this.__entryType = 'mark'; + this.__name = markName; this.__startTime = markOptions?.startTime ?? getCurrentTimeStamp(); this.__duration = 0; @@ -89,15 +88,10 @@ class PerformanceMeasureTemplate extends PerformanceEntry { __detail: DetailType; // This constructor isn't really used. See `PerformanceMeasure` below. - constructor(measureName: string, measureOptions: PerformanceMeasureInit) { - super({ - name: measureName, - entryType: 'measure', - startTime: measureOptions.startTime, - duration: measureOptions.duration, - }); + constructor(init: PerformanceMeasureInit) { + super('measure', init); - this.__detail = measureOptions?.detail ?? null; + this.__detail = init?.detail ?? null; } get detail(): DetailType { @@ -110,15 +104,14 @@ export const PerformanceMeasure: typeof PerformanceMeasureTemplate = // $FlowExpectedError[incompatible-type] function PerformanceMeasure( this: PerformanceMeasureTemplate, - measureName: string, - measureOptions: PerformanceMeasureInit, + init: PerformanceMeasureInit, ) { - this.__name = measureName; this.__entryType = 'measure'; - this.__startTime = measureOptions.startTime; - this.__duration = measureOptions.duration; + this.__name = init.name; + this.__startTime = init.startTime; + this.__duration = init.duration; - this.__detail = measureOptions.detail ?? null; + this.__detail = init.detail ?? null; }; // $FlowExpectedError[prop-missing] diff --git a/packages/react-native/src/private/webapis/performance/internals/RawPerformanceEntry.js b/packages/react-native/src/private/webapis/performance/internals/RawPerformanceEntry.js index 900541b4bba..40854b834ba 100644 --- a/packages/react-native/src/private/webapis/performance/internals/RawPerformanceEntry.js +++ b/packages/react-native/src/private/webapis/performance/internals/RawPerformanceEntry.js @@ -44,7 +44,6 @@ export function rawToPerformanceEntry( case RawPerformanceEntryTypeValues.LONGTASK: return new PerformanceLongTaskTiming({ name: entry.name, - entryType: rawToPerformanceEntryType(entry.entryType), startTime: entry.startTime, duration: entry.duration, }); @@ -53,7 +52,8 @@ export function rawToPerformanceEntry( startTime: entry.startTime, }); case RawPerformanceEntryTypeValues.MEASURE: - return new PerformanceMeasure(entry.name, { + return new PerformanceMeasure({ + name: entry.name, startTime: entry.startTime, duration: entry.duration, }); @@ -71,9 +71,8 @@ export function rawToPerformanceEntry( responseStatus: entry.responseStatus, }); default: - return new PerformanceEntry({ + return new PerformanceEntry(rawToPerformanceEntryType(entry.entryType), { name: entry.name, - entryType: rawToPerformanceEntryType(entry.entryType), startTime: entry.startTime, duration: entry.duration, });