1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @flow
    
  8.  */
    
  9. 
    
  10. import invokeGuardedCallbackImpl from './invokeGuardedCallbackImpl';
    
  11. 
    
  12. // Used by Fiber to simulate a try-catch.
    
  13. let hasError: boolean = false;
    
  14. let caughtError: mixed = null;
    
  15. 
    
  16. // Used by event system to capture/rethrow the first error.
    
  17. let hasRethrowError: boolean = false;
    
  18. let rethrowError: mixed = null;
    
  19. 
    
  20. const reporter = {
    
  21.   onError(error: mixed) {
    
  22.     hasError = true;
    
  23.     caughtError = error;
    
  24.   },
    
  25. };
    
  26. 
    
  27. /**
    
  28.  * Call a function while guarding against errors that happens within it.
    
  29.  * Returns an error if it throws, otherwise null.
    
  30.  *
    
  31.  * In production, this is implemented using a try-catch. The reason we don't
    
  32.  * use a try-catch directly is so that we can swap out a different
    
  33.  * implementation in DEV mode.
    
  34.  *
    
  35.  * @param {String} name of the guard to use for logging or debugging
    
  36.  * @param {Function} func The function to invoke
    
  37.  * @param {*} context The context to use when calling the function
    
  38.  * @param {...*} args Arguments for function
    
  39.  */
    
  40. export function invokeGuardedCallback<A, B, C, D, E, F, Context>(
    
  41.   name: string | null,
    
  42.   func: (a: A, b: B, c: C, d: D, e: E, f: F) => mixed,
    
  43.   context: Context,
    
  44.   a: A,
    
  45.   b: B,
    
  46.   c: C,
    
  47.   d: D,
    
  48.   e: E,
    
  49.   f: F,
    
  50. ): void {
    
  51.   hasError = false;
    
  52.   caughtError = null;
    
  53.   invokeGuardedCallbackImpl.apply(reporter, arguments);
    
  54. }
    
  55. 
    
  56. /**
    
  57.  * Same as invokeGuardedCallback, but instead of returning an error, it stores
    
  58.  * it in a global so it can be rethrown by `rethrowCaughtError` later.
    
  59.  * TODO: See if caughtError and rethrowError can be unified.
    
  60.  *
    
  61.  * @param {String} name of the guard to use for logging or debugging
    
  62.  * @param {Function} func The function to invoke
    
  63.  * @param {*} context The context to use when calling the function
    
  64.  * @param {...*} args Arguments for function
    
  65.  */
    
  66. export function invokeGuardedCallbackAndCatchFirstError<
    
  67.   A,
    
  68.   B,
    
  69.   C,
    
  70.   D,
    
  71.   E,
    
  72.   F,
    
  73.   Context,
    
  74. >(
    
  75.   this: mixed,
    
  76.   name: string | null,
    
  77.   func: (a: A, b: B, c: C, d: D, e: E, f: F) => void,
    
  78.   context: Context,
    
  79.   a: A,
    
  80.   b: B,
    
  81.   c: C,
    
  82.   d: D,
    
  83.   e: E,
    
  84.   f: F,
    
  85. ): void {
    
  86.   invokeGuardedCallback.apply(this, arguments);
    
  87.   if (hasError) {
    
  88.     const error = clearCaughtError();
    
  89.     if (!hasRethrowError) {
    
  90.       hasRethrowError = true;
    
  91.       rethrowError = error;
    
  92.     }
    
  93.   }
    
  94. }
    
  95. 
    
  96. /**
    
  97.  * During execution of guarded functions we will capture the first error which
    
  98.  * we will rethrow to be handled by the top level error handler.
    
  99.  */
    
  100. export function rethrowCaughtError() {
    
  101.   if (hasRethrowError) {
    
  102.     const error = rethrowError;
    
  103.     hasRethrowError = false;
    
  104.     rethrowError = null;
    
  105.     throw error;
    
  106.   }
    
  107. }
    
  108. 
    
  109. export function hasCaughtError(): boolean {
    
  110.   return hasError;
    
  111. }
    
  112. 
    
  113. export function clearCaughtError(): mixed {
    
  114.   if (hasError) {
    
  115.     const error = caughtError;
    
  116.     hasError = false;
    
  117.     caughtError = null;
    
  118.     return error;
    
  119.   } else {
    
  120.     throw new Error(
    
  121.       'clearCaughtError was called but no error was captured. This error ' +
    
  122.         'is likely caused by a bug in React. Please file an issue.',
    
  123.     );
    
  124.   }
    
  125. }