/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @flow */ import type {Fiber} from './ReactInternalTypes'; import { enableProfilerTimer, enableProfilerCommitHooks, } from 'shared/ReactFeatureFlags'; import {Profiler} from './ReactWorkTags'; // Intentionally not named imports because Rollup would use dynamic dispatch for // CommonJS interop named imports. import * as Scheduler from 'scheduler'; const {unstable_now: now} = Scheduler; export type ProfilerTimer = { getCommitTime(): number, recordCommitTime(): void, startProfilerTimer(fiber: Fiber): void, stopProfilerTimerIfRunning(fiber: Fiber): void, stopProfilerTimerIfRunningAndRecordDelta(fiber: Fiber): void, ... }; let commitTime: number = 0; let layoutEffectStartTime: number = -1; let profilerStartTime: number = -1; let passiveEffectStartTime: number = -1; function getCommitTime(): number { return commitTime; } function recordCommitTime(): void { if (!enableProfilerTimer) { return; } commitTime = now(); } function startProfilerTimer(fiber: Fiber): void { if (!enableProfilerTimer) { return; } profilerStartTime = now(); if (((fiber.actualStartTime: any): number) < 0) { fiber.actualStartTime = now(); } } function stopProfilerTimerIfRunning(fiber: Fiber): void { if (!enableProfilerTimer) { return; } profilerStartTime = -1; } function stopProfilerTimerIfRunningAndRecordDelta( fiber: Fiber, overrideBaseTime: boolean, ): void { if (!enableProfilerTimer) { return; } if (profilerStartTime >= 0) { const elapsedTime = now() - profilerStartTime; fiber.actualDuration += elapsedTime; if (overrideBaseTime) { fiber.selfBaseDuration = elapsedTime; } profilerStartTime = -1; } } function recordLayoutEffectDuration(fiber: Fiber): void { if (!enableProfilerTimer || !enableProfilerCommitHooks) { return; } if (layoutEffectStartTime >= 0) { const elapsedTime = now() - layoutEffectStartTime; layoutEffectStartTime = -1; // Store duration on the next nearest Profiler ancestor. let parentFiber = fiber.return; while (parentFiber !== null) { if (parentFiber.tag === Profiler) { const parentStateNode = parentFiber.stateNode; parentStateNode.effectDuration += elapsedTime; break; } parentFiber = parentFiber.return; } } } function recordPassiveEffectDuration(fiber: Fiber): void { if (!enableProfilerTimer || !enableProfilerCommitHooks) { return; } if (passiveEffectStartTime >= 0) { const elapsedTime = now() - passiveEffectStartTime; passiveEffectStartTime = -1; // Store duration on the next nearest Profiler ancestor. let parentFiber = fiber.return; while (parentFiber !== null) { if (parentFiber.tag === Profiler) { const parentStateNode = parentFiber.stateNode; if (parentStateNode !== null) { // Detached fibers have their state node cleared out. // In this case, the return pointer is also cleared out, // so we won't be able to report the time spent in this Profiler's subtree. parentStateNode.passiveEffectDuration += elapsedTime; } break; } parentFiber = parentFiber.return; } } } function startLayoutEffectTimer(): void { if (!enableProfilerTimer || !enableProfilerCommitHooks) { return; } layoutEffectStartTime = now(); } function startPassiveEffectTimer(): void { if (!enableProfilerTimer || !enableProfilerCommitHooks) { return; } passiveEffectStartTime = now(); } function transferActualDuration(fiber: Fiber): void { // Transfer time spent rendering these children so we don't lose it // after we rerender. This is used as a helper in special cases // where we should count the work of multiple passes. let child = fiber.child; while (child) { fiber.actualDuration += child.actualDuration; child = child.sibling; } } export { getCommitTime, recordCommitTime, recordLayoutEffectDuration, recordPassiveEffectDuration, startLayoutEffectTimer, startPassiveEffectTimer, startProfilerTimer, stopProfilerTimerIfRunning, stopProfilerTimerIfRunningAndRecordDelta, transferActualDuration, };