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
This commit is contained in:
Rubén Norte
2025-09-01 09:18:19 -07:00
committed by Facebook GitHub Bot
parent 81f8b0a6bf
commit bb508a4d94
7 changed files with 80 additions and 81 deletions
@@ -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;
@@ -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<TaskAttributionTiming> =
Object.preventExtensions([]);
export interface PerformanceLongTaskTimingInit extends PerformanceEntryInit {}
export class PerformanceLongTaskTiming extends PerformanceEntry {
constructor(init: PerformanceEntryInit) {
super('longtask', init);
}
get attribution(): $ReadOnlyArray<TaskAttributionTiming> {
return EMPTY_ATTRIBUTION;
}
@@ -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,
@@ -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;
}
@@ -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;
@@ -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]
@@ -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,
});