error-guard.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. /**
  2. * Copyright (c) Facebook, Inc. and its 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. * @format
  8. * @flow strict
  9. * @polyfill
  10. */
  11. let _inGuard = 0;
  12. type ErrorHandler = (error: mixed, isFatal: boolean) => void;
  13. type Fn<Args, Return> = (...Args) => Return;
  14. /**
  15. * This is the error handler that is called when we encounter an exception
  16. * when loading a module. This will report any errors encountered before
  17. * ExceptionsManager is configured.
  18. */
  19. let _globalHandler: ErrorHandler = function onError(
  20. e: mixed,
  21. isFatal: boolean,
  22. ) {
  23. throw e;
  24. };
  25. /**
  26. * The particular require runtime that we are using looks for a global
  27. * `ErrorUtils` object and if it exists, then it requires modules with the
  28. * error handler specified via ErrorUtils.setGlobalHandler by calling the
  29. * require function with applyWithGuard. Since the require module is loaded
  30. * before any of the modules, this ErrorUtils must be defined (and the handler
  31. * set) globally before requiring anything.
  32. */
  33. const ErrorUtils = {
  34. setGlobalHandler(fun: ErrorHandler): void {
  35. _globalHandler = fun;
  36. },
  37. getGlobalHandler(): ErrorHandler {
  38. return _globalHandler;
  39. },
  40. reportError(error: mixed): void {
  41. _globalHandler && _globalHandler(error, false);
  42. },
  43. reportFatalError(error: mixed): void {
  44. // NOTE: This has an untyped call site in Metro.
  45. _globalHandler && _globalHandler(error, true);
  46. },
  47. applyWithGuard<TArgs: $ReadOnlyArray<mixed>, TOut>(
  48. fun: Fn<TArgs, TOut>,
  49. context?: ?mixed,
  50. args?: ?TArgs,
  51. // Unused, but some code synced from www sets it to null.
  52. unused_onError?: null,
  53. // Some callers pass a name here, which we ignore.
  54. unused_name?: ?string,
  55. ): ?TOut {
  56. try {
  57. _inGuard++;
  58. // $FlowFixMe: TODO T48204745 (1) apply(context, null) is fine. (2) array -> rest array should work
  59. return fun.apply(context, args);
  60. } catch (e) {
  61. ErrorUtils.reportError(e);
  62. } finally {
  63. _inGuard--;
  64. }
  65. return null;
  66. },
  67. applyWithGuardIfNeeded<TArgs: $ReadOnlyArray<mixed>, TOut>(
  68. fun: Fn<TArgs, TOut>,
  69. context?: ?mixed,
  70. args?: ?TArgs,
  71. ): ?TOut {
  72. if (ErrorUtils.inGuard()) {
  73. // $FlowFixMe: TODO T48204745 (1) apply(context, null) is fine. (2) array -> rest array should work
  74. return fun.apply(context, args);
  75. } else {
  76. ErrorUtils.applyWithGuard(fun, context, args);
  77. }
  78. return null;
  79. },
  80. inGuard(): boolean {
  81. return !!_inGuard;
  82. },
  83. guard<TArgs: $ReadOnlyArray<mixed>, TOut>(
  84. fun: Fn<TArgs, TOut>,
  85. name?: ?string,
  86. context?: ?mixed,
  87. ): ?(...TArgs) => ?TOut {
  88. // TODO: (moti) T48204753 Make sure this warning is never hit and remove it - types
  89. // should be sufficient.
  90. if (typeof fun !== 'function') {
  91. console.warn('A function must be passed to ErrorUtils.guard, got ', fun);
  92. return null;
  93. }
  94. const guardName = name ?? fun.name ?? '<generated guard>';
  95. function guarded(...args: TArgs): ?TOut {
  96. return ErrorUtils.applyWithGuard(
  97. fun,
  98. context ?? this,
  99. args,
  100. null,
  101. guardName,
  102. );
  103. }
  104. return guarded;
  105. },
  106. };
  107. global.ErrorUtils = ErrorUtils;
  108. export type ErrorUtilsT = typeof ErrorUtils;