text: /**
   * Copyright (c) 2013-present, Facebook, Inc.
   *
   * 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 'react-reconciler/src/ReactFiber';
  
  import invariant from 'shared/invariant';
  import warningWithoutStack from 'shared/warningWithoutStack';
  
  import * as ReactInstanceMap from 'shared/ReactInstanceMap';
  import ReactSharedInternals from 'shared/ReactSharedInternals';
  import getComponentName from 'shared/getComponentName';
  import {
    ClassComponent,
    HostComponent,
    HostRoot,
    HostPortal,
    HostText,
  } from 'shared/ReactTypeOfWork';
  import {NoEffect, Placement} from 'shared/ReactTypeOfSideEffect';
  
  const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
  
  const MOUNTING = 1;
  const MOUNTED = 2;
  const UNMOUNTED = 3;
  
  function isFiberMountedImpl(fiber: Fiber): number {
    let node = fiber;
    if (!fiber.alternate) {
      // If there is no alternate, this might be a new tree that isn't inserted
      // yet. If it is, then it will have a pending insertion effect on it.
      if ((node.effectTag & Placement) !== NoEffect) {
        return MOUNTING;
      }
      while (node.return) {
        node = node.return;
        if ((node.effectTag & Placement) !== NoEffect) {
          return MOUNTING;
        }
      }
    } else {
      while (node.return) {
        node = node.return;
      }
    }
    if (node.tag === HostRoot) {
      // TODO: Check if this was a nested HostRoot when used with
      // renderContainerIntoSubtree.
      return MOUNTED;
    }
    // If we didn't hit the root, that means that we're in an disconnected tree
    // that has been unmounted.
    return UNMOUNTED;
  }
  
  export function isFiberMounted(fiber: Fiber): boolean {
    return isFiberMountedImpl(fiber) === MOUNTED;
  }
  
  export function isMounted(component: React$Component<any, any>): boolean {
    if (__DEV__) {
      const owner = (ReactCurrentOwner.current: any);
      if (owner !== null && owner.tag === ClassComponent) {
        const ownerFiber: Fiber = owner;
        const instance = ownerFiber.stateNode;
        warningWithoutStack(
          instance._warnedAboutRefsInRender,
          '%s is accessing isMounted inside its render() function. ' +
            'render() should be a pure function of props and state. It should ' +
            'never access something that requires stale data from the previous ' +
            'render, such as refs. Move this logic to componentDidMount and ' +
            'componentDidUpdate instead.',
          getComponentName(ownerFiber.type) || 'A component',
        );
        instance._warnedAboutRefsInRender = true;
      }
    }
  
    const fiber: ?Fiber = ReactInstanceMap.get(component);
    if (!fiber) {
      return false;
    }
    return isFiberMountedImpl(fiber) === MOUNTED;
  }
  
  function assertIsMounted(fiber) {
    invariant(
      isFiberMountedImpl(fiber) === MOUNTED,
      'Unable to find node on an unmounted component.',
    );
  }
  
  export function findCurrentFiberUsingSlowPath(fiber: Fiber): Fiber | null {
    let alternate = fiber.alternate;
    if (!alternate) {
      // If there is no alternate, then we only need to check if it is mounted.
      const state = isFiberMountedImpl(fiber);
      invariant(
        state !== UNMOUNTED,
        'Unable to find node on an unmounted component.',
      );
      if (state === MOUNTING) {
        return null;
      }
      return fiber;
    }
    // If we have two possible branches, we'll walk backwards up to the root
    // to see what path the root points to. On the way we may hit one of the
    // special cases and we'll deal with them.
    let a = fiber;
    let b = alternate;
    while (true) {
      let parentA = a.return;
      let parentB = parentA ? parentA.alternate : null;
      if (!parentA || !parentB) {
        // We're at the root.
        break;
      }
  
      // If both copies of the parent fiber point to the same child, we can
      // assume that the child is current. This happens when we bailout on low
      // priority: the bailed out fiber's child reuses the current child.
      if (parentA.child === parentB.child) {
        let child = parentA.child;
        while (child) {
          if (child === a) {
            // We've determined that A is the current branch.
            assertIsMounted(parentA);
            return fiber;
          }
          if (child === b) {
            // We've determined that B is the current branch.
            assertIsMounted(parentA);
            return alternate;
          }
          child = child.sibling;
        }
        // We should never have an alternate for any mounting node. So the only
        // way this could possibly happen is if this was unmounted, if at all.
        invariant(false, 'Unable to find node on an unmounted component.');
      }
  
      if (a.return !== b.return) {
        // The return pointer of A and the return pointer of B point to different
        // fibers. We assume that return pointers never criss-cross, so A must
        // belong to the child set of A.return, and B must belong to the child
        // set of B.return.
        a = parentA;
        b = parentB;
      } else {
        // The return pointers point to the same fiber. We'll have to use the
        // default, slow path: scan the child sets of each parent alternate to see
        // which child belongs to which set.
        //
        // Search parent A's child set
        let didFindChild = false;
        let child = parentA.child;
        while (child) {
          if (child === a) {
            didFindChild = true;
            a = parentA;
            b = parentB;
            break;
          }
          if (child === b) {
            didFindChild = true;
            b = parentA;
            a = parentB;
            break;
          }
          child = child.sibling;
        }
        if (!didFindChild) {
          // Search parent B's child set
          child = parentB.child;
          while (child) {
            if (child === a) {
              didFindChild = true;
              a = parentB;
              b = parentA;
              break;
            }
            if (child === b) {
              didFindChild = true;
              b = parentB;
              a = parentA;
              break;
            }
            child = child.sibling;
          }
          invariant(
            didFindChild,
            'Child was not found in either parent set. This indicates a bug ' +
              'in React related to the return pointer. Please file an issue.',
          );
        }
      }
  
      invariant(
        a.alternate === b,
        "Return fibers should always be each others' alternates. " +
          'This error is likely caused by a bug in React. Please file an issue.',
      );
    }
    // If the root is not a host container, we're in a disconnected tree. I.e.
    // unmounted.
    invariant(
      a.tag === HostRoot,
      'Unable to find node on an unmounted component.',
    );
    if (a.stateNode.current === a) {
      // We've determined that A is the current branch.
      return fiber;
    }
    // Otherwise B has to be current branch.
    return alternate;
  }
  
  export function findCurrentHostFiber(parent: Fiber): Fiber | null {
    const currentParent = findCurrentFiberUsingSlowPath(parent);
    if (!currentParent) {
      return null;
    }
  
    // Next we'll drill down this component to find the first HostComponent/Text.
    let node: Fiber = currentParent;
    while (true) {
      if (node.tag === HostComponent || node.tag === HostText) {
        return node;
      } else if (node.child) {
        node.child.return = node;
        node = node.child;
        continue;
      }
      if (node === currentParent) {
        return null;
      }
      while (!node.sibling) {
        if (!node.return || node.return === currentParent) {
          return null;
        }
        node = node.return;
      }
      node.sibling.return = node.return;
      node = node.sibling;
    }
    // Flow needs the return null here, but ESLint complains about it.
    // eslint-disable-next-line no-unreachable
    return null;
  }
  
  export function findCurrentHostFiberWithNoPortals(parent: Fiber): Fiber | null {
    const currentParent = findCurrentFiberUsingSlowPath(parent);
    if (!currentParent) {
      return null;
    }
  
    // Next we'll drill down this component to find the first HostComponent/Text.
    let node: Fiber = currentParent;
    while (true) {
      if (node.tag === HostComponent || node.tag === HostText) {
        return node;
      } else if (node.child && node.tag !== HostPortal) {
        node.child.return = node;
        node = node.child;
        continue;
      }
      if (node === currentParent) {
        return null;
      }
      while (!node.sibling) {
        if (!node.return || node.return === currentParent) {
          return null;
        }
        node = node.return;
      }
      node.sibling.return = node.return;
      node = node.sibling;
    }
    // Flow needs the return null here, but ESLint complains about it.
    // eslint-disable-next-line no-unreachable
    return null;
  }
  