summaryrefslogtreecommitdiffstats
path: root/devtools/shared/DevToolsInfaillibleUtils.sys.mjs
blob: ddb2b170eebf4cabb7328371d5ef95f38de6db01 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * The 3 methods here are duplicated from ThreadSafeDevToolsUtils.js
 * The ones defined here are used from other sys.mjs files, mostly from the
 * NetworkObserver codebase, while the ones remaining in ThreadSafeDevToolsUtils.js
 * are used in commonjs modules, including modules which can be loaded in workers.
 *
 * sys.mjs modules are currently not supported in workers, see Bug 1247687.
 */

/**
 * Report that |who| threw an exception, |exception|.
 */
function reportException(who, exception) {
  const msg = `${who} threw an exception: ${safeErrorString(exception)}`;
  dump(msg + "\n");

  if (typeof console !== "undefined" && console && console.error) {
    console.error(exception);
  }
}

/**
 * Given a handler function that may throw, return an infallible handler
 * function that calls the fallible handler, and logs any exceptions it
 * throws.
 *
 * @param handler function
 *      A handler function, which may throw.
 * @param aName string
 *      A name for handler, for use in error messages. If omitted, we use
 *      handler.name.
 *
 * (SpiderMonkey does generate good names for anonymous functions, but we
 * don't have a way to get at them from JavaScript at the moment.)
 */
function makeInfallible(handler, name = handler.name) {
  return function () {
    try {
      return handler.apply(this, arguments);
    } catch (ex) {
      let who = "Handler function";
      if (name) {
        who += " " + name;
      }
      reportException(who, ex);
      return undefined;
    }
  };
}

/**
 * Turn the |error| into a string, without fail.
 *
 * @param {Error|any} error
 */
function safeErrorString(error) {
  try {
    let errorString = error.toString();
    if (typeof errorString == "string") {
      // Attempt to attach a stack to |errorString|. If it throws an error, or
      // isn't a string, don't use it.
      try {
        if (error.stack) {
          const stack = error.stack.toString();
          if (typeof stack == "string") {
            errorString += "\nStack: " + stack;
          }
        }
      } catch (ee) {
        // Ignore.
      }

      // Append additional line and column number information to the output,
      // since it might not be part of the stringified error.
      if (
        typeof error.lineNumber == "number" &&
        typeof error.columnNumber == "number"
      ) {
        errorString +=
          "Line: " + error.lineNumber + ", column: " + error.columnNumber;
      }

      return errorString;
    }
  } catch (ee) {
    // Ignore.
  }

  // We failed to find a good error description, so do the next best thing.
  return Object.prototype.toString.call(error);
}

export const DevToolsInfaillibleUtils = {
  makeInfallible,
  reportException,
  safeErrorString,
};