import type {Fiber} from './ReactInternalTypes';
import type {SuspendedReason} from './ReactFiberWorkLoop';
import type {Lane, Lanes} from './ReactFiberLane';
import type {CapturedValue} from './ReactCapturedValue';
import {
isTransitionLane,
isBlockingLane,
isSyncLane,
includesTransitionLane,
includesBlockingLane,
includesSyncLane,
} from './ReactFiberLane';
import {resolveEventType, resolveEventTimeStamp} from './ReactFiberConfig';
import {
enableProfilerCommitHooks,
enableProfilerNestedUpdatePhase,
enableProfilerTimer,
enableComponentPerformanceTrack,
} from 'shared/ReactFeatureFlags';
import {isAlreadyRendering} from './ReactFiberWorkLoop';
import * as Scheduler from 'scheduler';
const {unstable_now: now} = Scheduler;
const createTask =
__DEV__ && console.createTask
?
console.createTask
: (name: string) => null;
export let renderStartTime: number = -0;
export let commitStartTime: number = -0;
export let commitEndTime: number = -0;
export let commitErrors: null | Array<CapturedValue<mixed>> = null;
export let profilerStartTime: number = -1.1;
export let profilerEffectDuration: number = -0;
export let componentEffectDuration: number = -0;
export let componentEffectStartTime: number = -1.1;
export let componentEffectEndTime: number = -1.1;
export let componentEffectErrors: null | Array<CapturedValue<mixed>> = null;
export let blockingClampTime: number = -0;
export let blockingUpdateTime: number = -1.1;
export let blockingUpdateTask: null | ConsoleTask = null;
export let blockingEventTime: number = -1.1;
export let blockingEventType: null | string = null;
export let blockingEventIsRepeat: boolean = false;
export let blockingSpawnedUpdate: boolean = false;
export let blockingSuspendedTime: number = -1.1;
export let transitionClampTime: number = -0;
export let transitionStartTime: number = -1.1;
export let transitionUpdateTime: number = -1.1;
export let transitionUpdateTask: null | ConsoleTask = null;
export let transitionEventTime: number = -1.1;
export let transitionEventType: null | string = null;
export let transitionEventIsRepeat: boolean = false;
export let transitionSuspendedTime: number = -1.1;
export let yieldReason: SuspendedReason = (0: any);
export let yieldStartTime: number = -1.1;
export function startYieldTimer(reason: SuspendedReason) {
if (!enableProfilerTimer || !enableComponentPerformanceTrack) {
return;
}
yieldStartTime = now();
yieldReason = reason;
}
export function startUpdateTimerByLane(lane: Lane, method: string): void {
if (!enableProfilerTimer || !enableComponentPerformanceTrack) {
return;
}
if (isSyncLane(lane) || isBlockingLane(lane)) {
if (blockingUpdateTime < 0) {
blockingUpdateTime = now();
blockingUpdateTask = createTask(method);
if (isAlreadyRendering()) {
blockingSpawnedUpdate = true;
}
const newEventTime = resolveEventTimeStamp();
const newEventType = resolveEventType();
if (
newEventTime !== blockingEventTime ||
newEventType !== blockingEventType
) {
blockingEventIsRepeat = false;
} else if (newEventType !== null) {
blockingSpawnedUpdate = true;
}
blockingEventTime = newEventTime;
blockingEventType = newEventType;
}
} else if (isTransitionLane(lane)) {
if (transitionUpdateTime < 0) {
transitionUpdateTime = now();
transitionUpdateTask = createTask(method);
if (transitionStartTime < 0) {
const newEventTime = resolveEventTimeStamp();
const newEventType = resolveEventType();
if (
newEventTime !== transitionEventTime ||
newEventType !== transitionEventType
) {
transitionEventIsRepeat = false;
}
transitionEventTime = newEventTime;
transitionEventType = newEventType;
}
}
}
}
export function startPingTimerByLanes(lanes: Lanes): void {
if (!enableProfilerTimer || !enableComponentPerformanceTrack) {
return;
}
if (includesSyncLane(lanes) || includesBlockingLane(lanes)) {
if (blockingUpdateTime < 0) {
blockingClampTime = blockingUpdateTime = now();
}
} else if (includesTransitionLane(lanes)) {
if (transitionUpdateTime < 0) {
transitionClampTime = transitionUpdateTime = now();
}
}
}
export function trackSuspendedTime(lanes: Lanes, renderEndTime: number) {
if (!enableProfilerTimer || !enableComponentPerformanceTrack) {
return;
}
if (includesSyncLane(lanes) || includesBlockingLane(lanes)) {
blockingSuspendedTime = renderEndTime;
} else if (includesTransitionLane(lanes)) {
transitionSuspendedTime = renderEndTime;
}
}
export function clearBlockingTimers(): void {
blockingUpdateTime = -1.1;
blockingUpdateTask = null;
blockingSuspendedTime = -1.1;
blockingEventIsRepeat = true;
blockingSpawnedUpdate = false;
}
export function startAsyncTransitionTimer(): void {
if (!enableProfilerTimer || !enableComponentPerformanceTrack) {
return;
}
if (transitionStartTime < 0 && transitionUpdateTime < 0) {
transitionStartTime = now();
const newEventTime = resolveEventTimeStamp();
const newEventType = resolveEventType();
if (
newEventTime !== transitionEventTime ||
newEventType !== transitionEventType
) {
transitionEventIsRepeat = false;
}
transitionEventTime = newEventTime;
transitionEventType = newEventType;
}
}
export function hasScheduledTransitionWork(): boolean {
return transitionUpdateTime > -1;
}
const ACTION_STATE_MARKER = -0.5;
export function startActionStateUpdate(): void {
if (!enableProfilerTimer || !enableComponentPerformanceTrack) {
return;
}
if (transitionUpdateTime < 0) {
transitionUpdateTime = ACTION_STATE_MARKER;
transitionUpdateTask = null;
}
}
export function clearAsyncTransitionTimer(): void {
transitionStartTime = -1.1;
}
export function clearTransitionTimers(): void {
transitionStartTime = -1.1;
transitionUpdateTime = -1.1;
transitionUpdateTask = null;
transitionSuspendedTime = -1.1;
transitionEventIsRepeat = true;
}
export function clampBlockingTimers(finalTime: number): void {
if (!enableProfilerTimer || !enableComponentPerformanceTrack) {
return;
}
blockingClampTime = finalTime;
}
export function clampTransitionTimers(finalTime: number): void {
if (!enableProfilerTimer || !enableComponentPerformanceTrack) {
return;
}
transitionClampTime = finalTime;
}
export function pushNestedEffectDurations(): number {
if (!enableProfilerTimer || !enableProfilerCommitHooks) {
return 0;
}
const prevEffectDuration = profilerEffectDuration;
profilerEffectDuration = 0;
return prevEffectDuration;
}
export function popNestedEffectDurations(prevEffectDuration: number): number {
if (!enableProfilerTimer || !enableProfilerCommitHooks) {
return 0;
}
const elapsedTime = profilerEffectDuration;
profilerEffectDuration = prevEffectDuration;
return elapsedTime;
}
export function bubbleNestedEffectDurations(
prevEffectDuration: number,
): number {
if (!enableProfilerTimer || !enableProfilerCommitHooks) {
return 0;
}
const elapsedTime = profilerEffectDuration;
profilerEffectDuration += prevEffectDuration;
return elapsedTime;
}
export function resetComponentEffectTimers(): void {
if (!enableProfilerTimer || !enableProfilerCommitHooks) {
return;
}
componentEffectStartTime = -1.1;
componentEffectEndTime = -1.1;
}
export function pushComponentEffectStart(): number {
if (!enableProfilerTimer || !enableProfilerCommitHooks) {
return 0;
}
const prevEffectStart = componentEffectStartTime;
componentEffectStartTime = -1.1;
return prevEffectStart;
}
export function popComponentEffectStart(prevEffectStart: number): void {
if (!enableProfilerTimer || !enableProfilerCommitHooks) {
return;
}
if (prevEffectStart >= 0) {
componentEffectStartTime = prevEffectStart;
}
}
export function pushComponentEffectDuration(): number {
if (!enableProfilerTimer || !enableProfilerCommitHooks) {
return 0;
}
const prevEffectDuration = componentEffectDuration;
componentEffectDuration = -0;
return prevEffectDuration;
}
export function popComponentEffectDuration(prevEffectDuration: number): void {
if (!enableProfilerTimer || !enableProfilerCommitHooks) {
return;
}
if (prevEffectDuration >= 0) {
componentEffectDuration = prevEffectDuration;
}
}
export function pushComponentEffectErrors(): null | Array<
CapturedValue<mixed>,
> {
if (!enableProfilerTimer || !enableProfilerCommitHooks) {
return null;
}
const prevErrors = componentEffectErrors;
componentEffectErrors = null;
return prevErrors;
}
export function popComponentEffectErrors(
prevErrors: null | Array<CapturedValue<mixed>>,
): void {
if (!enableProfilerTimer || !enableProfilerCommitHooks) {
return;
}
componentEffectErrors = prevErrors;
}
let currentUpdateIsNested: boolean = false;
let nestedUpdateScheduled: boolean = false;
export function isCurrentUpdateNested(): boolean {
return currentUpdateIsNested;
}
export function markNestedUpdateScheduled(): void {
if (enableProfilerNestedUpdatePhase) {
nestedUpdateScheduled = true;
}
}
export function resetNestedUpdateFlag(): void {
if (enableProfilerNestedUpdatePhase) {
currentUpdateIsNested = false;
nestedUpdateScheduled = false;
}
}
export function syncNestedUpdateFlag(): void {
if (enableProfilerNestedUpdatePhase) {
currentUpdateIsNested = nestedUpdateScheduled;
nestedUpdateScheduled = false;
}
}
export function recordRenderTime(): void {
if (!enableProfilerTimer || !enableComponentPerformanceTrack) {
return;
}
renderStartTime = now();
}
export function recordCommitTime(): void {
if (!enableProfilerTimer) {
return;
}
commitStartTime = now();
}
export function recordCommitEndTime(): void {
if (!enableProfilerTimer) {
return;
}
commitEndTime = now();
}
export function startProfilerTimer(fiber: Fiber): void {
if (!enableProfilerTimer) {
return;
}
profilerStartTime = now();
if (((fiber.actualStartTime: any): number) < 0) {
fiber.actualStartTime = profilerStartTime;
}
}
export function stopProfilerTimerIfRunning(fiber: Fiber): void {
if (!enableProfilerTimer) {
return;
}
profilerStartTime = -1;
}
export function stopProfilerTimerIfRunningAndRecordDuration(
fiber: Fiber,
): void {
if (!enableProfilerTimer) {
return;
}
if (profilerStartTime >= 0) {
const elapsedTime = now() - profilerStartTime;
fiber.actualDuration += elapsedTime;
fiber.selfBaseDuration = elapsedTime;
profilerStartTime = -1;
}
}
export function stopProfilerTimerIfRunningAndRecordIncompleteDuration(
fiber: Fiber,
): void {
if (!enableProfilerTimer) {
return;
}
if (profilerStartTime >= 0) {
const elapsedTime = now() - profilerStartTime;
fiber.actualDuration += elapsedTime;
profilerStartTime = -1;
}
}
export function recordEffectDuration(fiber: Fiber): void {
if (!enableProfilerTimer || !enableProfilerCommitHooks) {
return;
}
if (profilerStartTime >= 0) {
const endTime = now();
const elapsedTime = endTime - profilerStartTime;
profilerStartTime = -1;
profilerEffectDuration += elapsedTime;
componentEffectDuration += elapsedTime;
componentEffectEndTime = endTime;
}
}
export function recordEffectError(errorInfo: CapturedValue<mixed>): void {
if (!enableProfilerTimer || !enableProfilerCommitHooks) {
return;
}
if (componentEffectErrors === null) {
componentEffectErrors = [];
}
componentEffectErrors.push(errorInfo);
if (commitErrors === null) {
commitErrors = [];
}
commitErrors.push(errorInfo);
}
export function resetCommitErrors(): void {
commitErrors = null;
}
export function startEffectTimer(): void {
if (!enableProfilerTimer || !enableProfilerCommitHooks) {
return;
}
profilerStartTime = now();
if (componentEffectStartTime < 0) {
componentEffectStartTime = profilerStartTime;
}
}
export function transferActualDuration(fiber: Fiber): void {
let child = fiber.child;
while (child) {
fiber.actualDuration += child.actualDuration;
child = child.sibling;
}
}