import type {Fiber} from './ReactInternalTypes';
import type {SuspendedReason} from './ReactFiberWorkLoop';
import type {Lane, Lanes} from './ReactFiberLane';
import type {CapturedValue} from './ReactCapturedValue';
import {
isTransitionLane,
isBlockingLane,
isGestureRender,
includesTransitionLane,
includesBlockingLane,
NoLanes,
} from './ReactFiberLane';
import {resolveEventType, resolveEventTimeStamp} from './ReactFiberConfig';
import {
enableProfilerCommitHooks,
enableProfilerNestedUpdatePhase,
enableProfilerTimer,
enableComponentPerformanceTrack,
} from 'shared/ReactFeatureFlags';
import getComponentNameFromFiber from './getComponentNameFromFiber';
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 const REGULAR_UPDATE: UpdateType = 0;
export const SPAWNED_UPDATE: UpdateType = 1;
export const PINGED_UPDATE: UpdateType = 2;
export opaque type UpdateType = 0 | 1 | 2;
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 componentEffectSpawnedUpdate: boolean = false;
export let blockingClampTime: number = -0;
export let blockingUpdateTime: number = -1.1;
export let blockingUpdateTask: null | ConsoleTask = null;
export let blockingUpdateType: UpdateType = 0;
export let blockingUpdateMethodName: null | string = null;
export let blockingUpdateComponentName: null | string = null;
export let blockingEventTime: number = -1.1;
export let blockingEventType: null | string = null;
export let blockingEventRepeatTime: number = -1.1;
export let blockingSuspendedTime: number = -1.1;
export let gestureClampTime: number = -0;
export let gestureUpdateTime: number = -1.1;
export let gestureUpdateTask: null | ConsoleTask = null;
export let gestureUpdateType: UpdateType = 0;
export let gestureUpdateMethodName: null | string = null;
export let gestureUpdateComponentName: null | string = null;
export let gestureEventTime: number = -1.1;
export let gestureEventType: null | string = null;
export let gestureEventRepeatTime: number = -1.1;
export let gestureSuspendedTime: number = -1.1;
export let transitionClampTime: number = -0;
export let transitionStartTime: number = -1.1;
export let transitionUpdateTime: number = -1.1;
export let transitionUpdateType: UpdateType = 0;
export let transitionUpdateTask: null | ConsoleTask = null;
export let transitionUpdateMethodName: null | string = null;
export let transitionUpdateComponentName: null | string = null;
export let transitionEventTime: number = -1.1;
export let transitionEventType: null | string = null;
export let transitionEventRepeatTime: number = -1.1;
export let transitionSuspendedTime: number = -1.1;
export let retryClampTime: number = -0;
export let idleClampTime: number = -0;
export let animatingLanes: Lanes = NoLanes;
export let animatingTask: null | ConsoleTask = null;
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,
fiber: Fiber | null,
): void {
if (!enableProfilerTimer || !enableComponentPerformanceTrack) {
return;
}
if (isGestureRender(lane)) {
if (gestureUpdateTime < 0) {
gestureUpdateTime = now();
gestureUpdateTask = createTask(method);
gestureUpdateMethodName = method;
if (__DEV__ && fiber != null) {
gestureUpdateComponentName = getComponentNameFromFiber(fiber);
}
const newEventTime = resolveEventTimeStamp();
const newEventType = resolveEventType();
if (
newEventTime !== gestureEventRepeatTime ||
newEventType !== gestureEventType
) {
gestureEventRepeatTime = -1.1;
}
gestureEventTime = newEventTime;
gestureEventType = newEventType;
}
} else if (isBlockingLane(lane)) {
if (blockingUpdateTime < 0) {
blockingUpdateTime = now();
blockingUpdateTask = createTask(method);
blockingUpdateMethodName = method;
if (__DEV__ && fiber != null) {
blockingUpdateComponentName = getComponentNameFromFiber(fiber);
}
if (isAlreadyRendering()) {
componentEffectSpawnedUpdate = true;
blockingUpdateType = SPAWNED_UPDATE;
}
const newEventTime = resolveEventTimeStamp();
const newEventType = resolveEventType();
if (
newEventTime !== blockingEventRepeatTime ||
newEventType !== blockingEventType
) {
blockingEventRepeatTime = -1.1;
} else if (newEventType !== null) {
blockingUpdateType = SPAWNED_UPDATE;
}
blockingEventTime = newEventTime;
blockingEventType = newEventType;
}
} else if (isTransitionLane(lane)) {
if (transitionUpdateTime < 0) {
transitionUpdateTime = now();
transitionUpdateTask = createTask(method);
transitionUpdateMethodName = method;
if (__DEV__ && fiber != null) {
transitionUpdateComponentName = getComponentNameFromFiber(fiber);
}
if (transitionStartTime < 0) {
const newEventTime = resolveEventTimeStamp();
const newEventType = resolveEventType();
if (
newEventTime !== transitionEventRepeatTime ||
newEventType !== transitionEventType
) {
transitionEventRepeatTime = -1.1;
}
transitionEventTime = newEventTime;
transitionEventType = newEventType;
}
}
}
}
export function startHostActionTimer(fiber: Fiber): void {
if (!enableProfilerTimer || !enableComponentPerformanceTrack) {
return;
}
if (blockingUpdateTime < 0) {
blockingUpdateTime = now();
blockingUpdateTask =
__DEV__ && fiber._debugTask != null ? fiber._debugTask : null;
if (isAlreadyRendering()) {
blockingUpdateType = SPAWNED_UPDATE;
}
const newEventTime = resolveEventTimeStamp();
const newEventType = resolveEventType();
if (
newEventTime !== blockingEventRepeatTime ||
newEventType !== blockingEventType
) {
blockingEventRepeatTime = -1.1;
} else if (newEventType !== null) {
blockingUpdateType = SPAWNED_UPDATE;
}
blockingEventTime = newEventTime;
blockingEventType = newEventType;
}
if (transitionUpdateTime < 0) {
transitionUpdateTime = now();
transitionUpdateTask =
__DEV__ && fiber._debugTask != null ? fiber._debugTask : null;
if (transitionStartTime < 0) {
const newEventTime = resolveEventTimeStamp();
const newEventType = resolveEventType();
if (
newEventTime !== transitionEventRepeatTime ||
newEventType !== transitionEventType
) {
transitionEventRepeatTime = -1.1;
}
transitionEventTime = newEventTime;
transitionEventType = newEventType;
}
}
}
export function startPingTimerByLanes(lanes: Lanes): void {
if (!enableProfilerTimer || !enableComponentPerformanceTrack) {
return;
}
if (isGestureRender(lanes)) {
if (gestureUpdateTime < 0) {
gestureClampTime = gestureUpdateTime = now();
gestureUpdateTask = createTask('Promise Resolved');
gestureUpdateType = PINGED_UPDATE;
}
} else if (includesBlockingLane(lanes)) {
if (blockingUpdateTime < 0) {
blockingClampTime = blockingUpdateTime = now();
blockingUpdateTask = createTask('Promise Resolved');
blockingUpdateType = PINGED_UPDATE;
}
} else if (includesTransitionLane(lanes)) {
if (transitionUpdateTime < 0) {
transitionClampTime = transitionUpdateTime = now();
transitionUpdateTask = createTask('Promise Resolved');
transitionUpdateType = PINGED_UPDATE;
}
}
}
export function trackSuspendedTime(lanes: Lanes, renderEndTime: number) {
if (!enableProfilerTimer || !enableComponentPerformanceTrack) {
return;
}
if (isGestureRender(lanes)) {
gestureSuspendedTime = renderEndTime;
} else if (includesBlockingLane(lanes)) {
blockingSuspendedTime = renderEndTime;
} else if (includesTransitionLane(lanes)) {
transitionSuspendedTime = renderEndTime;
}
}
export function clearBlockingTimers(): void {
blockingUpdateTime = -1.1;
blockingUpdateType = 0;
blockingUpdateMethodName = null;
blockingUpdateComponentName = null;
blockingSuspendedTime = -1.1;
blockingEventRepeatTime = blockingEventTime;
blockingEventTime = -1.1;
blockingClampTime = now();
}
export function startAsyncTransitionTimer(): void {
if (!enableProfilerTimer || !enableComponentPerformanceTrack) {
return;
}
if (transitionStartTime < 0 && transitionUpdateTime < 0) {
transitionStartTime = now();
const newEventTime = resolveEventTimeStamp();
const newEventType = resolveEventType();
if (
newEventTime !== transitionEventRepeatTime ||
newEventType !== transitionEventType
) {
transitionEventRepeatTime = -1.1;
}
transitionEventTime = newEventTime;
transitionEventType = newEventType;
}
}
export function hasScheduledTransitionWork(): boolean {
return transitionUpdateTime > -1;
}
export function clearAsyncTransitionTimer(): void {
transitionStartTime = -1.1;
}
export function clearTransitionTimers(): void {
transitionStartTime = -1.1;
transitionUpdateTime = -1.1;
transitionUpdateType = 0;
transitionSuspendedTime = -1.1;
transitionEventRepeatTime = transitionEventTime;
transitionEventTime = -1.1;
transitionClampTime = now();
}
export function hasScheduledGestureTransitionWork(): boolean {
return gestureUpdateTime > -1;
}
export function clearGestureTimers(): void {
gestureUpdateTime = -1.1;
gestureUpdateType = 0;
gestureSuspendedTime = -1.1;
gestureEventRepeatTime = gestureEventTime;
gestureEventTime = -1.1;
gestureClampTime = now();
}
export function clearGestureUpdates(): void {
gestureUpdateTime = -1.1;
gestureUpdateType = 0;
gestureSuspendedTime = -1.1;
gestureEventRepeatTime = gestureEventTime;
gestureEventTime = -1.1;
}
export function clampBlockingTimers(finalTime: number): void {
if (!enableProfilerTimer || !enableComponentPerformanceTrack) {
return;
}
blockingClampTime = finalTime;
}
export function clampGestureTimers(finalTime: number): void {
if (!enableProfilerTimer || !enableComponentPerformanceTrack) {
return;
}
gestureClampTime = finalTime;
}
export function clampTransitionTimers(finalTime: number): void {
if (!enableProfilerTimer || !enableComponentPerformanceTrack) {
return;
}
transitionClampTime = finalTime;
}
export function clampRetryTimers(finalTime: number): void {
if (!enableProfilerTimer || !enableComponentPerformanceTrack) {
return;
}
retryClampTime = finalTime;
}
export function clampIdleTimers(finalTime: number): void {
if (!enableProfilerTimer || !enableComponentPerformanceTrack) {
return;
}
idleClampTime = 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;
}
export function pushComponentEffectDidSpawnUpdate(): boolean {
if (!enableProfilerTimer || !enableProfilerCommitHooks) {
return false;
}
const prev = componentEffectSpawnedUpdate;
componentEffectSpawnedUpdate = false;
return prev;
}
export function popComponentEffectDidSpawnUpdate(previousValue: boolean): void {
if (!enableProfilerTimer || !enableProfilerCommitHooks) {
return;
}
componentEffectSpawnedUpdate = previousValue;
}
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;
}
}
export function startAnimating(lanes: Lanes): void {
animatingLanes |= lanes;
animatingTask = null;
}
export function stopAnimating(lanes: Lanes): void {
animatingLanes &= ~lanes;
animatingTask = null;
}
export function trackAnimatingTask(task: ConsoleTask): void {
if (animatingTask === null) {
animatingTask = task;
}
}