Handle casting to string in performance.mark and performance.measure correctly (#52559)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52559

Changelog: [internal]

Making `performance.mark` and `performance.measure` slightly more spec-compliant.

Reviewed By: hoxyq

Differential Revision: D78013571

fbshipit-source-id: 4881b674f7cded5045b9f464ca7442a186c39048
This commit is contained in:
Rubén Norte
2025-07-11 05:37:31 -07:00
committed by Facebook GitHub Bot
parent ff38d59cff
commit acd07c7e1c
2 changed files with 38 additions and 6 deletions
@@ -123,6 +123,8 @@ export default class Performance {
);
}
const resolvedMarkName = String(markName);
let resolvedDetail;
if (markOptions?.detail != null) {
resolvedDetail = structuredClone(markOptions.detail);
@@ -137,7 +139,7 @@ export default class Performance {
resolvedStartTime = Number(startTime);
if (resolvedStartTime < 0) {
throw new TypeError(
`Failed to execute 'mark' on 'Performance': '${markName}' cannot have a negative start time.`,
`Failed to execute 'mark' on 'Performance': '${resolvedMarkName}' cannot have a negative start time.`,
);
} else if (!Number.isFinite(resolvedStartTime)) {
throw new TypeError(
@@ -148,7 +150,7 @@ export default class Performance {
// $FlowExpectedError[not-a-function]
computedStartTime = NativePerformance.markWithResult(
markName,
resolvedMarkName,
resolvedStartTime,
);
} else {
@@ -156,7 +158,7 @@ export default class Performance {
computedStartTime = performance.now();
}
return new PerformanceMark(markName, {
return new PerformanceMark(resolvedMarkName, {
startTime: computedStartTime,
detail: resolvedDetail,
});
@@ -176,6 +178,13 @@ export default class Performance {
startMarkOrOptions?: string | PerformanceMeasureOptions,
endMark?: string,
): PerformanceMeasure {
if (measureName == null) {
throw new TypeError(
`Failed to execute 'measure' on 'Performance': 1 argument required, but only 0 present.`,
);
}
let resolvedMeasureName = String(measureName);
let resolvedStartTime: number | void;
let resolvedStartMark: string | void;
let resolvedEndTime: number | void;
@@ -282,7 +291,7 @@ export default class Performance {
if (NativePerformance?.measure) {
try {
[computedStartTime, computedDuration] = NativePerformance.measure(
measureName,
resolvedMeasureName,
resolvedStartTime,
resolvedEndTime,
resolvedDuration,
@@ -299,7 +308,7 @@ export default class Performance {
try {
[computedStartTime, computedDuration] =
NativePerformance.measureWithResult(
measureName,
resolvedMeasureName,
resolvedStartTime ?? 0,
resolvedEndTime ?? 0,
resolvedDuration,
@@ -316,7 +325,7 @@ export default class Performance {
warnNoNativePerformance();
}
const measure = new PerformanceMeasure(measureName, {
const measure = new PerformanceMeasure(resolvedMeasureName, {
startTime: computedStartTime,
duration: computedDuration ?? 0,
detail: resolvedDetail,
@@ -87,6 +87,13 @@ describe('Performance', () => {
);
});
it('casts mark name to a string', () => {
// $FlowExpectedError[incompatible-call]
const mark = performance.mark(10);
expect(mark.name).toBe('10');
});
it('casts startTime to a number', () => {
const mark = performance.mark('some-mark', {
// $FlowExpectedError[incompatible-call]
@@ -424,6 +431,22 @@ describe('Performance', () => {
expect(measure.detail).toEqual(originalDetail);
expect(measure.detail).not.toBe(originalDetail);
});
it('casts measure name to a string', () => {
// $FlowExpectedError[incompatible-call]
const measure = performance.measure(10);
expect(measure.name).toBe('10');
});
it('throws if no name is provided', () => {
expect(() => {
// $FlowExpectedError[incompatible-call]
performance.measure();
}).toThrow(
`Failed to execute 'measure' on 'Performance': 1 argument required, but only 0 present.`,
);
});
});
describe('getting and clearing marks and measures', () => {