Bug 1654927 - Move js.msg and js::GetErrorMessage into a new js/public/friend directory so users of them don't have to depend on all of jsfriendapi.h. r=mgaudet
authorJeff Walden <jwalden@mit.edu>
Wed, 29 Jul 2020 04:44:50 +0000
changeset 542415 b5f49e696fee6b7be0e034f686e2f77bf6df764f
parent 542414 3a40fe8b0122cf9dff947e2777da2371e663eb68
child 542416 d8b099f96214c3fe3f4e89873e1e59dbccea5f4d
push id37648
push userapavel@mozilla.com
push dateWed, 29 Jul 2020 09:49:32 +0000
treeherdermozilla-central@3059084abf6e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmgaudet
bugs1654927
milestone81.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1654927 - Move js.msg and js::GetErrorMessage into a new js/public/friend directory so users of them don't have to depend on all of jsfriendapi.h. r=mgaudet Differential Revision: https://phabricator.services.mozilla.com/D84756
config/check_spidermonkey_style.py
dom/bindings/nsIScriptError.idl
js/public/Class.h
js/public/ErrorReport.h
js/public/friend/ErrorMessages.h
js/public/friend/ErrorNumbers.msg
js/src/ctypes/ctypes.msg
js/src/debugger/Object.cpp
js/src/js.msg
js/src/jsexn.h
js/src/jsfriendapi.h
js/src/jsshell.msg
js/src/moz.build
js/src/vm/ErrorMessages.cpp
js/src/vm/Initialization.cpp
js/src/vm/JSContext.cpp
js/src/vm/JSContext.h
js/src/vm/Opcodes.h
js/src/vm/SourceHook.cpp
js/xpconnect/src/jsshell.msg
--- a/config/check_spidermonkey_style.py
+++ b/config/check_spidermonkey_style.py
@@ -300,17 +300,17 @@ def check_style(enable_fixup):
                 if filename.endswith('.h'):
                     inclname = 'mozilla/' + filename
                     non_js_inclnames.add(inclname)
 
     # Look for header files in js/public.
     js_public_root = os.path.join('js', 'public')
     for dirpath, dirnames, filenames in os.walk(js_public_root):
         for filename in filenames:
-            if filename.endswith('.h'):
+            if filename.endswith(('.h', '.msg')):
                 filepath = os.path.join(dirpath, filename).replace('\\', '/')
                 inclname = 'js/' + filepath[len('js/public/'):]
                 js_names[filepath] = inclname
 
     all_inclnames = non_js_inclnames | set(js_names.values())
 
     edges = dict()      # type: dict(inclname, set(inclname))
 
@@ -361,22 +361,31 @@ def module_name(name):
 
 
 def is_module_header(enclosing_inclname, header_inclname):
     '''Determine if an included name is the "module header", i.e. should be
     first in the file.'''
 
     module = module_name(enclosing_inclname)
 
-    # Normal case, e.g. module == "foo/Bar", header_inclname == "foo/Bar.h".
+    # Normal case, for example:
+    #   module == "vm/Runtime", header_inclname == "vm/Runtime.h".
     if module == module_name(header_inclname):
         return True
 
-    # A public header, e.g. module == "foo/Bar", header_inclname == "js/Bar.h".
-    m = re.match(r'js\/(.*)\.h', header_inclname)
+    # A public header, for example:
+    #
+    #   module == "vm/CharacterEncoding",
+    #   header_inclname == "js/CharacterEncoding.h"
+    #
+    # or (for implementation files for js/public/*/*.h headers)
+    #
+    #   module == "vm/SourceHook",
+    #   header_inclname == "js/experimental/SourceHook.h"
+    m = re.match(r'js\/.*?([^\/]+)\.h', header_inclname)
     if m is not None and module.endswith('/' + m.group(1)):
         return True
 
     return False
 
 
 class Include(object):
     '''Important information for a single #include statement.'''
@@ -399,17 +408,17 @@ class Include(object):
         The section numbers are as follows.
           0. Module header (e.g. jsfoo.h or jsfooinlines.h within jsfoo.cpp)
           1. mozilla/Foo.h
           2. <foo.h> or <foo>
           3. jsfoo.h, prmjtime.h, etc
           4. foo/Bar.h
           5. jsfooinlines.h
           6. foo/Bar-inl.h
-          7. non-.h, e.g. *.tbl, *.msg
+          7. non-.h, e.g. *.tbl, *.msg (these can be scattered throughout files)
         '''
 
         if self.is_system:
             return 2
 
         if not self.inclname.endswith('.h'):
             return 7
 
--- a/dom/bindings/nsIScriptError.idl
+++ b/dom/bindings/nsIScriptError.idl
@@ -112,18 +112,18 @@ interface nsIScriptError : nsIConsoleMes
      * same-compartment with |stack|. This can be used to enter the correct
      * realm when working with the stack object. We can't use the object itself
      * because it might be a cross-compartment wrapper and CCWs are not
      * associated with a single realm/global.
      */
     [noscript] readonly attribute jsval stackGlobal;
 
     /**
-     * The name of a template string, as found in js.msg, associated with the
-     * error message.
+     * The name of a template string associated with the error message.  See
+     * js/public/friend/ErrorNumbers.msg.
      */
     attribute AString errorMessageName;
 
     readonly attribute nsIArray notes;
 
     /**
      * If the ScriptError is a CSS parser error, this value will contain the
      * CSS selectors of the CSS ruleset where the error occured.
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -73,19 +73,19 @@ namespace JS {
  * Users don't have to call `result.report()`; another possible ending is:
  *
  *     argv.rval().setBoolean(result.reallyOk());
  *     return true;
  */
 class ObjectOpResult {
  private:
   /**
-   * code_ is either one of the special codes OkCode or Uninitialized, or
-   * an error code. For now the error codes are private to the JS engine;
-   * they're defined in js/src/js.msg.
+   * code_ is either one of the special codes OkCode or Uninitialized, or an
+   * error code. For now the error codes are JS friend API and are defined in
+   * js/public/friend/ErrorNumbers.msg.
    *
    * code_ is uintptr_t (rather than uint32_t) for the convenience of the
    * JITs, which would otherwise have to deal with either padding or stack
    * alignment on 64-bit platforms.
    */
   uintptr_t code_;
 
  public:
--- a/js/public/ErrorReport.h
+++ b/js/public/ErrorReport.h
@@ -35,18 +35,19 @@
 
 struct JS_PUBLIC_API JSContext;
 class JS_PUBLIC_API JSString;
 
 /**
  * Possible exception types. These types are part of a JSErrorFormatString
  * structure. They define which error to throw in case of a runtime error.
  *
- * JSEXN_WARN is used for warnings in js.msg files (for instance because we
- * don't want to prepend 'Error:' to warning messages). This value can go away
+ * JSEXN_WARN is used for warnings, that are not strictly errors but are handled
+ * using the generalized error reporting mechanism.  (One side effect of this
+ * type is to not prepend 'Error:' to warning messages.)  This value can go away
  * if we ever decide to use an entirely separate mechanism for warnings.
  */
 enum JSExnType {
   JSEXN_ERR,
   JSEXN_FIRST = JSEXN_ERR,
   JSEXN_INTERNALERR,
   JSEXN_AGGREGATEERR,
   JSEXN_EVALERR,
@@ -100,17 +101,17 @@ class JSErrorBase {
   unsigned sourceId;
 
   // Source line number.
   unsigned lineno;
 
   // Zero-based column index in line.
   unsigned column;
 
-  // the error number, e.g. see js.msg.
+  // the error number, e.g. see js/public/friend/ErrorNumbers.msg.
   unsigned errorNumber;
 
   // Points to JSErrorFormatString::name.
   // This string must never be freed.
   const char* errorMessageName;
 
  private:
   bool ownsMessage_ : 1;
new file mode 100644
--- /dev/null
+++ b/js/public/friend/ErrorMessages.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * 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/. */
+
+/*
+ * SpiderMonkey internal error numbering and error-formatting functionality
+ * (also for warnings).
+ *
+ * This functionality is moderately stable.  JSErrNum and js::GetErrorMessage
+ * are widely used inside SpiderMonkey, and Gecko uses them to produce errors
+ * identical to those SpiderMonkey itself would produce, in various situations.
+ * However, the set of error numbers is not stable, error number values are not
+ * stable, error types are not stable, etc.  Use your own error reporting code
+ * if you can.
+ */
+
+#ifndef js_friend_ErrorMessages_h
+#define js_friend_ErrorMessages_h
+
+#include "jstypes.h"  // JS_FRIEND_API
+
+struct JSErrorFormatString;
+
+enum JSErrNum {
+#define MSG_DEF(name, count, exception, format) name,
+#include "js/friend/ErrorNumbers.msg"
+#undef MSG_DEF
+  JSErr_Limit
+};
+
+namespace js {
+
+/**
+ * A JSErrorCallback suitable for passing to |JS_ReportErrorNumberASCII| and
+ * similar functions in concert with one of the |JSErrNum| error numbers.
+ *
+ * This function is a function only of |errorNumber|: |userRef| and ambient
+ * state have no effect on its behavior.
+ */
+extern JS_FRIEND_API const JSErrorFormatString* GetErrorMessage(
+    void* userRef, unsigned errorNumber);
+
+}  // namespace js
+
+#endif  // js_friend_ErrorMessages_h
rename from js/src/js.msg
rename to js/public/friend/ErrorNumbers.msg
--- a/js/src/js.msg
+++ b/js/public/friend/ErrorNumbers.msg
@@ -1,30 +1,40 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  *
  * 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/. */
 
 /*
+ * SpiderMonkey error messages.
+ *
+ * These are largely for internal use, but they're exposed publicly as a
+ * "friend" interface for embedders that want to replicate SpiderMonkey's exact
+ * error message behavior in particular circumstances.  All names, arities,
+ * types, and messages are subject to change or removal.  However, the
+ * longer-lived the error message, the less likely it is to change.
+ */
+
+/*
  * This is the JavaScript error message file.
  *
  * The format for each JS error message is:
  *
  * MSG_DEF(<SYMBOLIC_NAME>, <ARGUMENT_COUNT>, <EXCEPTION_NAME>,
  *         <FORMAT_STRING>)
  *
  * where ;
  * <SYMBOLIC_NAME> is a legal C identifer that will be used in the
  * JS engine source.
  *
  * <ARGUMENT_COUNT> is an integer literal specifying the total number of
  * replaceable arguments in the following format string.
  *
- * <EXCEPTION_NAME> is an enum JSExnType value, defined in jsapi.h.
+ * <EXCEPTION_NAME> is an enum JSExnType value, defined in js/ErrorReport.h.
  *
  * <FORMAT_STRING> is a string literal, optionally containing sequences
  * {X} where X  is an integer representing the argument number that will
  * be replaced with a string value when the error is reported.
  *
  * e.g.
  *
  * MSG_DEF(JSMSG_NOT_A_SUBSPECIES, 2, JSEXN_TYPEERROR,
--- a/js/src/ctypes/ctypes.msg
+++ b/js/src/ctypes/ctypes.msg
@@ -1,17 +1,17 @@
 /* -*-  Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
 /* 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/. */
 
 /*
  * This is the jsctypes error message file.
  *
- * For syntax details, see js/src/js.msg.
+ * For syntax details, see js/public/friend/ErrorNumbers.msg.
  */
 
 MSG_DEF(CTYPESMSG_PLACEHOLDER_0, 0, JSEXN_ERR, NULL)
 
 /* type conversion */
 MSG_DEF(CTYPESMSG_CONV_ERROR_ARG,3, JSEXN_TYPEERR, "can't pass {0} to argument {1} of {2}")
 MSG_DEF(CTYPESMSG_CONV_ERROR_ARRAY,3, JSEXN_TYPEERR, "can't convert {0} to element {1} of the type {2}")
 MSG_DEF(CTYPESMSG_CONV_ERROR_FIN,2, JSEXN_TYPEERR, "can't convert {0} to the type of argument 1 of {1}")
--- a/js/src/debugger/Object.cpp
+++ b/js/src/debugger/Object.cpp
@@ -494,19 +494,19 @@ bool DebuggerObject::CallData::allocatio
   if (!DebuggerObject::getAllocationSite(cx, object, &result)) {
     return false;
   }
 
   args.rval().setObjectOrNull(result);
   return true;
 }
 
-// Returns the "name" field (see js.msg), which may be used as a unique
-// identifier, for any error object with a JSErrorReport or undefined
-// if the object has no JSErrorReport.
+// Returns the "name" field (see js/public/friend/ErrorNumbers.msg), which may
+// be used as a unique identifier, for any error object with a JSErrorReport or
+// undefined if the object has no JSErrorReport.
 bool DebuggerObject::CallData::errorMessageNameGetter() {
   RootedString result(cx);
   if (!DebuggerObject::getErrorMessageName(cx, object, &result)) {
     return false;
   }
 
   if (result) {
     args.rval().setString(result);
--- a/js/src/jsexn.h
+++ b/js/src/jsexn.h
@@ -14,22 +14,26 @@
 #include "mozilla/Assertions.h"
 
 #include "jsapi.h"
 #include "jspubtd.h"
 #include "jstypes.h"
 #include "NamespaceImports.h"
 
 #include "js/ErrorReport.h"
+#include "js/friend/ErrorMessages.h"  // JSErr_Limit
 #include "js/RootingAPI.h"
 #include "js/TypeDecls.h"
 #include "js/UniquePtr.h"
 #include "js/Utility.h"
 
+extern const JSErrorFormatString js_ErrorFormatString[JSErr_Limit];
+
 namespace js {
+
 class ErrorObject;
 
 UniquePtr<JSErrorNotes::Note> CopyErrorNote(JSContext* cx,
                                             JSErrorNotes::Note* note);
 
 UniquePtr<JSErrorReport> CopyErrorReport(JSContext* cx, JSErrorReport* report);
 
 bool CaptureStack(JSContext* cx, MutableHandleObject stack);
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -17,16 +17,17 @@
 #include "jspubtd.h"
 
 #include "js/CallArgs.h"
 #include "js/CallNonGenericMethod.h"
 #include "js/CharacterEncoding.h"
 #include "js/Class.h"
 #include "js/ErrorReport.h"
 #include "js/Exception.h"
+#include "js/friend/ErrorMessages.h"
 #include "js/HeapAPI.h"
 #include "js/TypeDecls.h"
 #include "js/Utility.h"
 
 #ifndef JS_STACK_GROWTH_DIRECTION
 #  ifdef __hppa
 #    define JS_STACK_GROWTH_DIRECTION (1)
 #  else
@@ -1271,30 +1272,18 @@ extern JS_FRIEND_API bool DateIsValid(JS
                                       bool* isValid);
 
 extern JS_FRIEND_API bool DateGetMsecSinceEpoch(JSContext* cx,
                                                 JS::HandleObject obj,
                                                 double* msecSinceEpoch);
 
 } /* namespace js */
 
-typedef enum JSErrNum {
-#define MSG_DEF(name, count, exception, format) name,
-#include "js.msg"
-#undef MSG_DEF
-  JSErr_Limit
-} JSErrNum;
-
 namespace js {
 
-/* Implemented in vm/JSContext.cpp. */
-
-extern JS_FRIEND_API const JSErrorFormatString* GetErrorMessage(
-    void* userRef, const unsigned errorNumber);
-
 /* Implemented in vm/StructuredClone.cpp. */
 extern JS_FRIEND_API uint64_t GetSCOffset(JSStructuredCloneWriter* writer);
 
 namespace Scalar {
 
 // Scalar types that can appear in typed arrays and typed objects.
 // The enum values must be kept in sync with:
 //  * the JS_SCALARTYPEREPR constants
--- a/js/src/jsshell.msg
+++ b/js/src/jsshell.msg
@@ -1,16 +1,16 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  *
  * 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/. */
 
 /*
-	Error messages for JSShell. See js.msg for format.
+ * Error messages for JSShell. See js/public/friend/ErrorNumbers.msg for format.
 */
 
 MSG_DEF(JSSMSG_NOT_AN_ERROR,            0, JSEXN_ERR, "<Error #0 is reserved>")
 MSG_DEF(JSSMSG_CANT_OPEN,               2, JSEXN_ERR, "can't open {0}: {1}")
 MSG_DEF(JSSMSG_LINE2PC_USAGE,           0, JSEXN_ERR, "usage: line2pc [fun] line")
 MSG_DEF(JSSMSG_FILE_SCRIPTS_ONLY,       0, JSEXN_ERR, "only works on JS scripts read from files")
 MSG_DEF(JSSMSG_UNEXPECTED_EOF,          1, JSEXN_ERR, "unexpected EOF in {0}")
 MSG_DEF(JSSMSG_NOT_ENOUGH_ARGS,         1, JSEXN_ERR, "{0}: not enough arguments")
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -108,17 +108,16 @@ if CONFIG['HAVE_DTRACE']:
     GeneratedFile('javascript-trace.h')
     EXPORTS += ['!javascript-trace.h']
 
 # Changes to internal header files, used externally, massively slow down
 # browser builds.  Don't add new files here unless you know what you're
 # doing!
 EXPORTS += [
     '!js-config.h',
-    'js.msg',
     'jsapi.h',
     'jsfriendapi.h',
     'jspubtd.h',
     'jstypes.h',
 ]
 
 EXPORTS.js += [
     '../public/AllocationRecording.h',
@@ -211,20 +210,33 @@ EXPORTS.js += [
 ]
 
 # This lives in mozglue/baseprofiler but we re-publish ourselves to support
 # standalone SpiderMonkey configurations.
 EXPORTS.js += [
     '../../mozglue/baseprofiler/public/ProfilingCategoryList.h',
 ]
 
+# Public APIs that are experimental: the precise contour of the APIs may still
+# change, but they're at least plausible first passes at designing something.
+# We expose them as-is, buyer beware.
 EXPORTS.js.experimental += [
     '../public/experimental/SourceHook.h',
 ]
 
+# Friend APIs are APIs that either basically SpiderMonkey-internal, or their
+# contours are gross and terrible -- but the functionality is too important to
+# some embedder (often, Gecko) to just not expose anything or to agonize through
+# a clean design.  Use this only if you absolutely must, and feel free to
+# propose clean APIs to replace what's here!
+EXPORTS.js.friend += [
+    '../public/friend/ErrorMessages.h',
+    '../public/friend/ErrorNumbers.msg',
+]
+
 UNIFIED_SOURCES += [
     'builtin/Array.cpp',
     'builtin/AtomicsObject.cpp',
     'builtin/BigInt.cpp',
     'builtin/Boolean.cpp',
     'builtin/DataViewObject.cpp',
     'builtin/Eval.cpp',
     'builtin/FinalizationRegistryObject.cpp',
@@ -308,16 +320,17 @@ UNIFIED_SOURCES += [
     'vm/CharacterEncoding.cpp',
     'vm/CodeCoverage.cpp',
     'vm/Compartment.cpp',
     'vm/CompilationAndEvaluation.cpp',
     'vm/Compression.cpp',
     'vm/DateTime.cpp',
     'vm/EnvironmentObject.cpp',
     'vm/EqualityOperations.cpp',
+    'vm/ErrorMessages.cpp',
     'vm/ErrorObject.cpp',
     'vm/ErrorReporting.cpp',
     'vm/Exception.cpp',
     'vm/ForOfIterator.cpp',
     'vm/FrameIter.cpp',
     'vm/FunctionFlags.cpp',
     'vm/GeckoProfiler.cpp',
     'vm/GeneratorObject.cpp',
@@ -472,17 +485,17 @@ DIRS += [
     'wasm']
 
 if CONFIG['JS_JITSPEW']:
     DIRS += ['zydis']
 
 FINAL_LIBRARY = 'js'
 
 selfhosted_inputs = [
-    'js.msg',
+    '../public/friend/ErrorNumbers.msg',
     'builtin/TypedObjectConstants.h',
     'builtin/SelfHostingDefines.h',
     'builtin/Utilities.js',
     'builtin/Array.js',
     'builtin/AsyncFunction.js',
     'builtin/AsyncIteration.js',
     'builtin/BigInt.js',
     'builtin/Classes.js',
copy from js/src/vm/JSContext.cpp
copy to js/src/vm/ErrorMessages.cpp
--- a/js/src/vm/JSContext.cpp
+++ b/js/src/vm/ErrorMessages.cpp
@@ -1,1265 +1,29 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * vim: set ts=8 sts=2 et sw=2 tw=80:
  * 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/. */
 
-/*
- * JS execution context.
- */
-
-#include "vm/JSContext-inl.h"
-
-#include "mozilla/ArrayUtils.h"
-#include "mozilla/CheckedInt.h"
-#include "mozilla/DebugOnly.h"
-#include "mozilla/MemoryReporting.h"
-#include "mozilla/Sprintf.h"
-#include "mozilla/TextUtils.h"
-#include "mozilla/Unused.h"
-#include "mozilla/Utf8.h"  // mozilla::ConvertUtf16ToUtf8
-
-#include <stdarg.h>
-#include <string.h>
-#ifdef ANDROID
-#  include <android/log.h>
-#  include <fstream>
-#  include <string>
-#endif  // ANDROID
-#ifdef XP_WIN
-#  include <processthreadsapi.h>
-#endif  // XP_WIN
-
-#include "jsexn.h"
-#include "jspubtd.h"
-#include "jstypes.h"
-
-#include "gc/FreeOp.h"
-#include "gc/Marking.h"
-#include "gc/PublicIterators.h"
-#include "irregexp/RegExpAPI.h"
-#include "jit/Ion.h"
-#include "jit/PcScriptCache.h"
-#include "js/CharacterEncoding.h"
-#include "js/ContextOptions.h"  // JS::ContextOptions
-#include "js/Printf.h"
-#ifdef JS_SIMULATOR_ARM
-#  include "jit/arm/Simulator-arm.h"
-#endif
-#ifdef JS_SIMULATOR_ARM64
-#  include "jit/arm64/vixl/Simulator-vixl.h"
-#endif
-#ifdef JS_SIMULATOR_MIPS32
-#  include "jit/mips32/Simulator-mips32.h"
-#endif
-#ifdef JS_SIMULATOR_MIPS64
-#  include "jit/mips64/Simulator-mips64.h"
-#endif
-#include "util/DiagnosticAssertions.h"
-#include "util/DoubleToString.h"
-#include "util/NativeStack.h"
-#include "util/Windows.h"
-#include "vm/BytecodeUtil.h"  // JSDVG_IGNORE_STACK
-#include "vm/ErrorObject.h"
-#include "vm/ErrorReporting.h"
-#include "vm/HelperThreads.h"
-#include "vm/Iteration.h"
-#include "vm/JSAtom.h"
-#include "vm/JSFunction.h"
-#include "vm/JSObject.h"
-#include "vm/JSScript.h"
-#include "vm/PlainObject.h"  // js::PlainObject
-#include "vm/Realm.h"
-#include "vm/Shape.h"
-#include "vm/StringType.h"  // StringToNewUTF8CharsZ
-#include "vm/ToSource.h"    // js::ValueToSource
-
-#include "vm/Compartment-inl.h"
-#include "vm/JSObject-inl.h"
-#include "vm/JSScript-inl.h"
-#include "vm/Stack-inl.h"
-
-using namespace js;
-
-using mozilla::DebugOnly;
-using mozilla::PodArrayZero;
-
-bool js::AutoCycleDetector::init() {
-  MOZ_ASSERT(cyclic);
-
-  AutoCycleDetector::Vector& vector = cx->cycleDetectorVector();
-
-  for (JSObject* obj2 : vector) {
-    if (MOZ_UNLIKELY(obj == obj2)) {
-      return true;
-    }
-  }
-
-  if (!vector.append(obj)) {
-    return false;
-  }
-
-  cyclic = false;
-  return true;
-}
-
-js::AutoCycleDetector::~AutoCycleDetector() {
-  if (MOZ_LIKELY(!cyclic)) {
-    AutoCycleDetector::Vector& vec = cx->cycleDetectorVector();
-    MOZ_ASSERT(vec.back() == obj);
-    if (vec.length() > 1) {
-      vec.popBack();
-    } else {
-      // Avoid holding on to unused heap allocations.
-      vec.clearAndFree();
-    }
-  }
-}
-
-bool JSContext::init(ContextKind kind) {
-  // Skip most of the initialization if this thread will not be running JS.
-  if (kind == ContextKind::MainThread) {
-    TlsContext.set(this);
-    currentThread_ = ThreadId::ThisThreadId();
-
-    if (!fx.initInstance()) {
-      return false;
-    }
-
-#ifdef JS_SIMULATOR
-    simulator_ = jit::Simulator::Create();
-    if (!simulator_) {
-      return false;
-    }
-#endif
-
-  } else {
-    atomsZoneFreeLists_ = js_new<gc::FreeLists>();
-    if (!atomsZoneFreeLists_) {
-      return false;
-    }
-  }
-
-  isolate = irregexp::CreateIsolate(this);
-  if (!isolate) {
-    return false;
-  }
-
-  // Set the ContextKind last, so that ProtectedData checks will allow us to
-  // initialize this context before it becomes the runtime's active context.
-  kind_ = kind;
-
-  return true;
-}
-
-JSContext* js::NewContext(uint32_t maxBytes, JSRuntime* parentRuntime) {
-  AutoNoteSingleThreadedRegion anstr;
-
-  MOZ_RELEASE_ASSERT(!TlsContext.get());
-
-#if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
-  js::oom::SetThreadType(!parentRuntime ? js::THREAD_TYPE_MAIN
-                                        : js::THREAD_TYPE_WORKER);
-#endif
+/* SpiderMonkey-internal error-reporting formatting functionality. */
 
-  JSRuntime* runtime = js_new<JSRuntime>(parentRuntime);
-  if (!runtime) {
-    return nullptr;
-  }
-
-  JSContext* cx = js_new<JSContext>(runtime, JS::ContextOptions());
-  if (!cx) {
-    js_delete(runtime);
-    return nullptr;
-  }
-
-  if (!cx->init(ContextKind::MainThread)) {
-    js_delete(cx);
-    js_delete(runtime);
-    return nullptr;
-  }
-
-  if (!runtime->init(cx, maxBytes)) {
-    runtime->destroyRuntime();
-    js_delete(cx);
-    js_delete(runtime);
-    return nullptr;
-  }
-
-  return cx;
-}
-
-void js::DestroyContext(JSContext* cx) {
-  JS_AbortIfWrongThread(cx);
-
-  cx->checkNoGCRooters();
-
-  // Cancel all off thread Ion compiles. Completed Ion compiles may try to
-  // interrupt this context. See HelperThread::handleIonWorkload.
-  CancelOffThreadIonCompile(cx->runtime());
-
-  cx->jobQueue = nullptr;
-  cx->internalJobQueue = nullptr;
-  SetContextProfilingStack(cx, nullptr);
-
-  JSRuntime* rt = cx->runtime();
-
-  // Flush promise tasks executing in helper threads early, before any parts
-  // of the JSRuntime that might be visible to helper threads are torn down.
-  rt->offThreadPromiseState.ref().shutdown(cx);
-
-  // Destroy the runtime along with its last context.
-  js::AutoNoteSingleThreadedRegion nochecks;
-  rt->destroyRuntime();
-  js_delete_poison(cx);
-  js_delete_poison(rt);
-}
-
-void JS::RootingContext::checkNoGCRooters() {
-#ifdef DEBUG
-  for (auto const& stackRootPtr : stackRoots_) {
-    MOZ_ASSERT(stackRootPtr == nullptr);
-  }
-#endif
-}
-
-bool AutoResolving::alreadyStartedSlow() const {
-  MOZ_ASSERT(link);
-  AutoResolving* cursor = link;
-  do {
-    MOZ_ASSERT(this != cursor);
-    if (object.get() == cursor->object && id.get() == cursor->id &&
-        kind == cursor->kind) {
-      return true;
-    }
-  } while (!!(cursor = cursor->link));
-  return false;
-}
-
-/*
- * Since memory has been exhausted, avoid the normal error-handling path which
- * allocates an error object, report and callstack. If code is running, simply
- * throw the static atom "out of memory". If code is not running, call the
- * error reporter directly.
- *
- * Furthermore, callers of ReportOutOfMemory (viz., malloc) assume a GC does
- * not occur, so GC must be avoided or suppressed.
- */
-JS_FRIEND_API void js::ReportOutOfMemory(JSContext* cx) {
-#ifdef JS_MORE_DETERMINISTIC
-  /*
-   * OOMs are non-deterministic, especially across different execution modes
-   * (e.g. interpreter vs JIT). In more-deterministic builds, print to stderr
-   * so that the fuzzers can detect this.
-   */
-  fprintf(stderr, "ReportOutOfMemory called\n");
-#endif
-
-  if (cx->isHelperThreadContext()) {
-    return cx->addPendingOutOfMemory();
-  }
-
-  cx->runtime()->hadOutOfMemory = true;
-  gc::AutoSuppressGC suppressGC(cx);
-
-  /* Report the oom. */
-  if (JS::OutOfMemoryCallback oomCallback = cx->runtime()->oomCallback) {
-    oomCallback(cx, cx->runtime()->oomCallbackData);
-  }
-
-  // If we OOM early in process startup, this may be unavailable so just return
-  // instead of crashing unexpectedly.
-  if (MOZ_UNLIKELY(!cx->runtime()->hasInitializedSelfHosting())) {
-    return;
-  }
-
-  RootedValue oomMessage(cx, StringValue(cx->names().outOfMemory));
-  cx->setPendingException(oomMessage, nullptr);
-}
-
-mozilla::GenericErrorResult<OOM&> js::ReportOutOfMemoryResult(JSContext* cx) {
-  ReportOutOfMemory(cx);
-  return cx->alreadyReportedOOM();
-}
-
-void js::ReportOverRecursed(JSContext* maybecx, unsigned errorNumber) {
-#ifdef JS_MORE_DETERMINISTIC
-  /*
-   * We cannot make stack depth deterministic across different
-   * implementations (e.g. JIT vs. interpreter will differ in
-   * their maximum stack depth).
-   * However, we can detect externally when we hit the maximum
-   * stack depth which is useful for external testing programs
-   * like fuzzers.
-   */
-  fprintf(stderr, "ReportOverRecursed called\n");
-#endif
-  if (maybecx) {
-    if (!maybecx->isHelperThreadContext()) {
-      JS_ReportErrorNumberASCII(maybecx, GetErrorMessage, nullptr, errorNumber);
-      maybecx->overRecursed_ = true;
-    } else {
-      maybecx->addPendingOverRecursed();
-    }
-  }
-}
-
-JS_FRIEND_API void js::ReportOverRecursed(JSContext* maybecx) {
-  ReportOverRecursed(maybecx, JSMSG_OVER_RECURSED);
-}
-
-void js::ReportAllocationOverflow(JSContext* cx) {
-  if (!cx) {
-    return;
-  }
-
-  if (cx->isHelperThreadContext()) {
-    return;
-  }
-
-  gc::AutoSuppressGC suppressGC(cx);
-  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ALLOC_OVERFLOW);
-}
-
-/* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */
-void js::ReportUsageErrorASCII(JSContext* cx, HandleObject callee,
-                               const char* msg) {
-  RootedValue usage(cx);
-  if (!JS_GetProperty(cx, callee, "usage", &usage)) {
-    return;
-  }
+#include "js/friend/ErrorMessages.h"
 
-  if (!usage.isString()) {
-    JS_ReportErrorASCII(cx, "%s", msg);
-  } else {
-    RootedString usageStr(cx, usage.toString());
-    UniqueChars str = JS_EncodeStringToUTF8(cx, usageStr);
-    if (!str) {
-      return;
-    }
-    JS_ReportErrorUTF8(cx, "%s. Usage: %s", msg, str.get());
-  }
-}
-
-enum class PrintErrorKind { Error, Warning, Note };
-
-static void PrintErrorLine(FILE* file, const char* prefix,
-                           JSErrorReport* report) {
-  if (const char16_t* linebuf = report->linebuf()) {
-    UniqueChars line;
-    size_t n;
-    {
-      size_t linebufLen = report->linebufLength();
-
-      // This function is only used for shell command-line sorts of stuff where
-      // performance doesn't really matter, so just encode into max-sized
-      // memory.
-      mozilla::CheckedInt<size_t> utf8Len(linebufLen);
-      utf8Len *= 3;
-      if (utf8Len.isValid()) {
-        line = UniqueChars(js_pod_malloc<char>(utf8Len.value()));
-        if (line) {
-          n = mozilla::ConvertUtf16toUtf8({linebuf, linebufLen},
-                                          {line.get(), utf8Len.value()});
-        }
-      }
-    }
-
-    const char* utf8buf;
-    if (line) {
-      utf8buf = line.get();
-    } else {
-      static const char unavailableStr[] = "<context unavailable>";
-      utf8buf = unavailableStr;
-      n = mozilla::ArrayLength(unavailableStr) - 1;
-    }
-
-    fputs(":\n", file);
-    if (prefix) {
-      fputs(prefix, file);
-    }
-
-    for (size_t i = 0; i < n; i++) {
-      fputc(utf8buf[i], file);
-    }
-
-    // linebuf/utf8buf usually ends with a newline. If not, add one here.
-    if (n == 0 || utf8buf[n - 1] != '\n') {
-      fputc('\n', file);
-    }
-
-    if (prefix) {
-      fputs(prefix, file);
-    }
-
-    n = report->tokenOffset();
-    for (size_t i = 0, j = 0; i < n; i++) {
-      if (utf8buf[i] == '\t') {
-        for (size_t k = (j + 8) & ~7; j < k; j++) {
-          fputc('.', file);
-        }
-        continue;
-      }
-      fputc('.', file);
-      j++;
-    }
-    fputc('^', file);
-  }
-}
-
-static void PrintErrorLine(FILE* file, const char* prefix,
-                           JSErrorNotes::Note* note) {}
-
-template <typename T>
-static void PrintSingleError(JSContext* cx, FILE* file,
-                             JS::ConstUTF8CharsZ toStringResult, T* report,
-                             PrintErrorKind kind) {
-  UniqueChars prefix;
-  if (report->filename) {
-    prefix = JS_smprintf("%s:", report->filename);
-  }
-
-  if (report->lineno) {
-    prefix = JS_smprintf("%s%u:%u ", prefix ? prefix.get() : "", report->lineno,
-                         report->column);
-  }
-
-  if (kind != PrintErrorKind::Error) {
-    const char* kindPrefix = nullptr;
-    switch (kind) {
-      case PrintErrorKind::Error:
-        MOZ_CRASH("unreachable");
-      case PrintErrorKind::Warning:
-        kindPrefix = "warning";
-        break;
-      case PrintErrorKind::Note:
-        kindPrefix = "note";
-        break;
-    }
-
-    prefix = JS_smprintf("%s%s: ", prefix ? prefix.get() : "", kindPrefix);
-  }
-
-  const char* message =
-      toStringResult ? toStringResult.c_str() : report->message().c_str();
-
-  /* embedded newlines -- argh! */
-  const char* ctmp;
-  while ((ctmp = strchr(message, '\n')) != 0) {
-    ctmp++;
-    if (prefix) {
-      fputs(prefix.get(), file);
-    }
-    mozilla::Unused << fwrite(message, 1, ctmp - message, file);
-    message = ctmp;
-  }
-
-  /* If there were no filename or lineno, the prefix might be empty */
-  if (prefix) {
-    fputs(prefix.get(), file);
-  }
-  fputs(message, file);
-
-  PrintErrorLine(file, prefix.get(), report);
-  fputc('\n', file);
-
-  fflush(file);
-}
-
-static void PrintErrorImpl(JSContext* cx, FILE* file,
-                           JS::ConstUTF8CharsZ toStringResult,
-                           JSErrorReport* report, bool reportWarnings) {
-  MOZ_ASSERT(report);
-
-  /* Conditionally ignore reported warnings. */
-  if (report->isWarning() && !reportWarnings) {
-    return;
-  }
-
-  PrintErrorKind kind = PrintErrorKind::Error;
-  if (report->isWarning()) {
-    kind = PrintErrorKind::Warning;
-  }
-  PrintSingleError(cx, file, toStringResult, report, kind);
-
-  if (report->notes) {
-    for (auto&& note : *report->notes) {
-      PrintSingleError(cx, file, JS::ConstUTF8CharsZ(), note.get(),
-                       PrintErrorKind::Note);
-    }
-  }
-}
+#include "jsexn.h"  // js_ErrorFormatString
 
-JS_PUBLIC_API void JS::PrintError(JSContext* cx, FILE* file,
-                                  JSErrorReport* report, bool reportWarnings) {
-  PrintErrorImpl(cx, file, JS::ConstUTF8CharsZ(), report, reportWarnings);
-}
-
-JS_PUBLIC_API void JS::PrintError(JSContext* cx, FILE* file,
-                                  const JS::ErrorReportBuilder& builder,
-                                  bool reportWarnings) {
-  PrintErrorImpl(cx, file, builder.toStringResult(), builder.report(),
-                 reportWarnings);
-}
-
-void js::ReportIsNotDefined(JSContext* cx, HandleId id) {
-  if (UniqueChars printable =
-          IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsIdentifier)) {
-    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NOT_DEFINED,
-                             printable.get());
-  }
-}
-
-void js::ReportIsNotDefined(JSContext* cx, HandlePropertyName name) {
-  RootedId id(cx, NameToId(name));
-  ReportIsNotDefined(cx, id);
-}
-
-const char* NullOrUndefinedToCharZ(HandleValue v) {
-  MOZ_ASSERT(v.isNullOrUndefined());
-  return v.isNull() ? js_null_str : js_undefined_str;
-}
-
-void js::ReportIsNullOrUndefinedForPropertyAccess(JSContext* cx, HandleValue v,
-                                                  int vIndex) {
-  MOZ_ASSERT(v.isNullOrUndefined());
-
-  if (vIndex == JSDVG_IGNORE_STACK) {
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                              JSMSG_CANT_CONVERT_TO, NullOrUndefinedToCharZ(v),
-                              "object");
-    return;
-  }
-
-  UniqueChars bytes = DecompileValueGenerator(cx, vIndex, v, nullptr);
-  if (!bytes) {
-    return;
-  }
-
-  if (strcmp(bytes.get(), js_undefined_str) == 0 ||
-      strcmp(bytes.get(), js_null_str) == 0) {
-    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NO_PROPERTIES,
-                             bytes.get());
-  } else {
-    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
-                             JSMSG_UNEXPECTED_TYPE, bytes.get(),
-                             NullOrUndefinedToCharZ(v));
-  }
-}
-
-void js::ReportIsNullOrUndefinedForPropertyAccess(JSContext* cx, HandleValue v,
-                                                  int vIndex, HandleId key) {
-  MOZ_ASSERT(v.isNullOrUndefined());
-
-  if (!cx->realm()->creationOptions().getPropertyErrorMessageFixEnabled()) {
-    ReportIsNullOrUndefinedForPropertyAccess(cx, v, vIndex);
-    return;
-  }
-
-  RootedValue idVal(cx, IdToValue(key));
-  RootedString idStr(cx, ValueToSource(cx, idVal));
-  if (!idStr) {
-    return;
-  }
-
-  UniqueChars keyStr = StringToNewUTF8CharsZ(cx, *idStr);
-  if (!keyStr) {
-    return;
-  }
-
-  if (vIndex == JSDVG_IGNORE_STACK) {
-    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL,
-                             keyStr.get(), NullOrUndefinedToCharZ(v));
-    return;
-  }
-
-  UniqueChars bytes = DecompileValueGenerator(cx, vIndex, v, nullptr);
-  if (!bytes) {
-    return;
-  }
-
-  if (strcmp(bytes.get(), js_undefined_str) == 0 ||
-      strcmp(bytes.get(), js_null_str) == 0) {
-    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL,
-                             keyStr.get(), bytes.get());
-    return;
-  }
-
-  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
-                           JSMSG_PROPERTY_FAIL_EXPR, keyStr.get(), bytes.get(),
-                           NullOrUndefinedToCharZ(v));
-}
-
-bool js::ReportValueError(JSContext* cx, const unsigned errorNumber,
-                          int spindex, HandleValue v, HandleString fallback,
-                          const char* arg1, const char* arg2) {
-  MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount >= 1);
-  MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount <= 3);
-  UniqueChars bytes = DecompileValueGenerator(cx, spindex, v, fallback);
-  if (!bytes) {
-    return false;
-  }
-
-  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber,
-                           bytes.get(), arg1, arg2);
-  return false;
-}
-
-JSObject* js::CreateErrorNotesArray(JSContext* cx, JSErrorReport* report) {
-  RootedArrayObject notesArray(cx, NewDenseEmptyArray(cx));
-  if (!notesArray) {
-    return nullptr;
-  }
-
-  if (!report->notes) {
-    return notesArray;
-  }
-
-  for (auto&& note : *report->notes) {
-    RootedPlainObject noteObj(cx, NewBuiltinClassInstance<PlainObject>(cx));
-    if (!noteObj) {
-      return nullptr;
-    }
-
-    RootedString messageStr(cx, note->newMessageString(cx));
-    if (!messageStr) {
-      return nullptr;
-    }
-    RootedValue messageVal(cx, StringValue(messageStr));
-    if (!DefineDataProperty(cx, noteObj, cx->names().message, messageVal)) {
-      return nullptr;
-    }
-
-    RootedValue filenameVal(cx);
-    if (note->filename) {
-      RootedString filenameStr(cx, NewStringCopyZ<CanGC>(cx, note->filename));
-      if (!filenameStr) {
-        return nullptr;
-      }
-      filenameVal = StringValue(filenameStr);
-    }
-    if (!DefineDataProperty(cx, noteObj, cx->names().fileName, filenameVal)) {
-      return nullptr;
-    }
-
-    RootedValue linenoVal(cx, Int32Value(note->lineno));
-    if (!DefineDataProperty(cx, noteObj, cx->names().lineNumber, linenoVal)) {
-      return nullptr;
-    }
-    RootedValue columnVal(cx, Int32Value(note->column));
-    if (!DefineDataProperty(cx, noteObj, cx->names().columnNumber, columnVal)) {
-      return nullptr;
-    }
-
-    if (!NewbornArrayPush(cx, notesArray, ObjectValue(*noteObj))) {
-      return nullptr;
-    }
-  }
-
-  return notesArray;
-}
+#include "js/ErrorReport.h"  // JSErrorFormatString
 
 const JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = {
 #define MSG_DEF(name, count, exception, format) \
   {#name, format, count, exception},
-#include "js.msg"
+#include "js/friend/ErrorNumbers.msg"
 #undef MSG_DEF
 };
 
-JS_FRIEND_API const JSErrorFormatString* js::GetErrorMessage(
-    void* userRef, const unsigned errorNumber) {
+const JSErrorFormatString* js::GetErrorMessage(void* userRef,
+                                               unsigned errorNumber) {
   if (errorNumber > 0 && errorNumber < JSErr_Limit) {
     return &js_ErrorFormatString[errorNumber];
   }
+
   return nullptr;
 }
-
-void JSContext::recoverFromOutOfMemory() {
-  if (isHelperThreadContext()) {
-    // Keep in sync with addPendingOutOfMemory.
-    if (ParseTask* task = parseTask()) {
-      task->outOfMemory = false;
-    }
-  } else {
-    if (isExceptionPending()) {
-      MOZ_ASSERT(isThrowingOutOfMemory());
-      clearPendingException();
-    }
-  }
-}
-
-JS_FRIEND_API bool js::UseInternalJobQueues(JSContext* cx) {
-  // Internal job queue handling must be set up very early. Self-hosting
-  // initialization is as good a marker for that as any.
-  MOZ_RELEASE_ASSERT(
-      !cx->runtime()->hasInitializedSelfHosting(),
-      "js::UseInternalJobQueues must be called early during runtime startup.");
-  MOZ_ASSERT(!cx->jobQueue);
-  auto queue = MakeUnique<InternalJobQueue>(cx);
-  if (!queue) {
-    return false;
-  }
-
-  cx->internalJobQueue = std::move(queue);
-  cx->jobQueue = cx->internalJobQueue.ref().get();
-
-  cx->runtime()->offThreadPromiseState.ref().initInternalDispatchQueue();
-  MOZ_ASSERT(cx->runtime()->offThreadPromiseState.ref().initialized());
-
-  return true;
-}
-
-JS_FRIEND_API bool js::EnqueueJob(JSContext* cx, JS::HandleObject job) {
-  MOZ_ASSERT(cx->jobQueue);
-  return cx->jobQueue->enqueuePromiseJob(cx, nullptr, job, nullptr, nullptr);
-}
-
-JS_FRIEND_API void js::StopDrainingJobQueue(JSContext* cx) {
-  MOZ_ASSERT(cx->internalJobQueue.ref());
-  cx->internalJobQueue->interrupt();
-}
-
-JS_FRIEND_API void js::RunJobs(JSContext* cx) {
-  MOZ_ASSERT(cx->jobQueue);
-  cx->jobQueue->runJobs(cx);
-  JS::ClearKeptObjects(cx);
-}
-
-JSObject* InternalJobQueue::getIncumbentGlobal(JSContext* cx) {
-  if (!cx->compartment()) {
-    return nullptr;
-  }
-  return cx->global();
-}
-
-bool InternalJobQueue::enqueuePromiseJob(JSContext* cx,
-                                         JS::HandleObject promise,
-                                         JS::HandleObject job,
-                                         JS::HandleObject allocationSite,
-                                         JS::HandleObject incumbentGlobal) {
-  MOZ_ASSERT(job);
-  if (!queue.pushBack(job)) {
-    ReportOutOfMemory(cx);
-    return false;
-  }
-
-  JS::JobQueueMayNotBeEmpty(cx);
-  return true;
-}
-
-void InternalJobQueue::runJobs(JSContext* cx) {
-  if (draining_ || interrupted_) {
-    return;
-  }
-
-  while (true) {
-    cx->runtime()->offThreadPromiseState.ref().internalDrain(cx);
-
-    // It doesn't make sense for job queue draining to be reentrant. At the
-    // same time we don't want to assert against it, because that'd make
-    // drainJobQueue unsafe for fuzzers. We do want fuzzers to test this,
-    // so we simply ignore nested calls of drainJobQueue.
-    draining_ = true;
-
-    RootedObject job(cx);
-    JS::HandleValueArray args(JS::HandleValueArray::empty());
-    RootedValue rval(cx);
-
-    // Execute jobs in a loop until we've reached the end of the queue.
-    while (!queue.empty()) {
-      // A previous job might have set this flag. E.g., the js shell
-      // sets it if the `quit` builtin function is called.
-      if (interrupted_) {
-        break;
-      }
-
-      job = queue.front();
-      queue.popFront();
-
-      // If the next job is the last job in the job queue, allow
-      // skipping the standard job queuing behavior.
-      if (queue.empty()) {
-        JS::JobQueueIsEmpty(cx);
-      }
-
-      AutoRealm ar(cx, &job->as<JSFunction>());
-      {
-        if (!JS::Call(cx, UndefinedHandleValue, job, args, &rval)) {
-          // Nothing we can do about uncatchable exceptions.
-          if (!cx->isExceptionPending()) {
-            continue;
-          }
-          RootedValue exn(cx);
-          if (cx->getPendingException(&exn)) {
-            /*
-             * Clear the exception, because
-             * PrepareScriptEnvironmentAndInvoke will assert that we don't
-             * have one.
-             */
-            cx->clearPendingException();
-            js::ReportExceptionClosure reportExn(exn);
-            PrepareScriptEnvironmentAndInvoke(cx, cx->global(), reportExn);
-          }
-        }
-      }
-    }
-
-    draining_ = false;
-
-    if (interrupted_) {
-      interrupted_ = false;
-      break;
-    }
-
-    queue.clear();
-
-    // It's possible a job added a new off-thread promise task.
-    if (!cx->runtime()->offThreadPromiseState.ref().internalHasPending()) {
-      break;
-    }
-  }
-}
-
-bool InternalJobQueue::empty() const { return queue.empty(); }
-
-JSObject* InternalJobQueue::maybeFront() const {
-  if (queue.empty()) {
-    return nullptr;
-  }
-
-  return queue.get().front();
-}
-
-class js::InternalJobQueue::SavedQueue : public JobQueue::SavedJobQueue {
- public:
-  SavedQueue(JSContext* cx, Queue&& saved, bool draining)
-      : cx(cx), saved(cx, std::move(saved)), draining_(draining) {
-    MOZ_ASSERT(cx->internalJobQueue.ref());
-  }
-
-  ~SavedQueue() {
-    MOZ_ASSERT(cx->internalJobQueue.ref());
-    cx->internalJobQueue->queue = std::move(saved.get());
-    cx->internalJobQueue->draining_ = draining_;
-  }
-
- private:
-  JSContext* cx;
-  PersistentRooted<Queue> saved;
-  bool draining_;
-};
-
-js::UniquePtr<JS::JobQueue::SavedJobQueue> InternalJobQueue::saveJobQueue(
-    JSContext* cx) {
-  auto saved =
-      js::MakeUnique<SavedQueue>(cx, std::move(queue.get()), draining_);
-  if (!saved) {
-    // When MakeUnique's allocation fails, the SavedQueue constructor is never
-    // called, so this->queue is still initialized. (The move doesn't occur
-    // until the constructor gets called.)
-    ReportOutOfMemory(cx);
-    return nullptr;
-  }
-
-  queue = Queue(SystemAllocPolicy());
-  draining_ = false;
-  return saved;
-}
-
-JS::Error JSContext::reportedError;
-JS::OOM JSContext::reportedOOM;
-
-mozilla::GenericErrorResult<OOM&> JSContext::alreadyReportedOOM() {
-#ifdef DEBUG
-  if (isHelperThreadContext()) {
-    // Keep in sync with addPendingOutOfMemory.
-    if (ParseTask* task = parseTask()) {
-      MOZ_ASSERT(task->outOfMemory);
-    }
-  } else {
-    MOZ_ASSERT(isThrowingOutOfMemory());
-  }
-#endif
-  return mozilla::Err(reportedOOM);
-}
-
-mozilla::GenericErrorResult<JS::Error&> JSContext::alreadyReportedError() {
-#ifdef DEBUG
-  if (!isHelperThreadContext()) {
-    MOZ_ASSERT(isExceptionPending());
-  }
-#endif
-  return mozilla::Err(reportedError);
-}
-
-JSContext::JSContext(JSRuntime* runtime, const JS::ContextOptions& options)
-    : runtime_(runtime),
-      kind_(ContextKind::HelperThread),
-      nurserySuppressions_(this),
-      options_(this, options),
-      freeLists_(this, nullptr),
-      atomsZoneFreeLists_(this),
-      defaultFreeOp_(this, runtime, true),
-      freeUnusedMemory(false),
-      jitActivation(this, nullptr),
-      isolate(this, nullptr),
-      activation_(this, nullptr),
-      profilingActivation_(nullptr),
-      nativeStackBase(GetNativeStackBase()),
-      entryMonitor(this, nullptr),
-      noExecuteDebuggerTop(this, nullptr),
-#ifdef DEBUG
-      inUnsafeCallWithABI(this, false),
-      hasAutoUnsafeCallWithABI(this, false),
-#endif
-#ifdef JS_SIMULATOR
-      simulator_(this, nullptr),
-#endif
-#ifdef JS_TRACE_LOGGING
-      traceLogger(nullptr),
-#endif
-      dtoaState(this, nullptr),
-      suppressGC(this, 0),
-#ifdef DEBUG
-      gcUse(this, GCUse::None),
-      gcSweepZone(this, nullptr),
-      isTouchingGrayThings(this, false),
-      noNurseryAllocationCheck(this, 0),
-      disableStrictProxyCheckingCount(this, 0),
-#endif
-#if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
-      runningOOMTest(this, false),
-#endif
-      enableAccessValidation(this, false),
-      inUnsafeRegion(this, 0),
-      generationalDisabled(this, 0),
-      compactingDisabledCount(this, 0),
-      frontendCollectionPool_(this),
-      suppressProfilerSampling(false),
-      wasmTriedToInstallSignalHandlers(false),
-      wasmHaveSignalHandlers(false),
-      tempLifoAlloc_(this, (size_t)TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
-      debuggerMutations(this, 0),
-      ionPcScriptCache(this, nullptr),
-      throwing(this, false),
-      unwrappedException_(this),
-      unwrappedExceptionStack_(this),
-      overRecursed_(this, false),
-      propagatingForcedReturn_(this, false),
-      reportGranularity(this, JS_DEFAULT_JITREPORT_GRANULARITY),
-      resolvingList(this, nullptr),
-#ifdef DEBUG
-      enteredPolicy(this, nullptr),
-#endif
-      generatingError(this, false),
-      cycleDetectorVector_(this, this),
-      data(nullptr),
-      asyncStackForNewActivations_(this),
-      asyncCauseForNewActivations(this, nullptr),
-      asyncCallIsExplicit(this, false),
-      interruptCallbacks_(this),
-      interruptCallbackDisabled(this, false),
-      interruptBits_(0),
-      inlinedICScript_(this, nullptr),
-      ionReturnOverride_(this, MagicValue(JS_ARG_POISON)),
-      jitStackLimit(UINTPTR_MAX),
-      jitStackLimitNoInterrupt(this, UINTPTR_MAX),
-      jobQueue(this, nullptr),
-      internalJobQueue(this),
-      canSkipEnqueuingJobs(this, false),
-      promiseRejectionTrackerCallback(this, nullptr),
-      promiseRejectionTrackerCallbackData(this, nullptr),
-#ifdef JS_STRUCTURED_SPEW
-      structuredSpewer_(),
-#endif
-      insideDebuggerEvaluationWithOnNativeCallHook(this, nullptr) {
-  MOZ_ASSERT(static_cast<JS::RootingContext*>(this) ==
-             JS::RootingContext::get(this));
-}
-
-JSContext::~JSContext() {
-  // Clear the ContextKind first, so that ProtectedData checks will allow us to
-  // destroy this context even if the runtime is already gone.
-  kind_ = ContextKind::HelperThread;
-
-  /* Free the stuff hanging off of cx. */
-  MOZ_ASSERT(!resolvingList);
-
-  if (dtoaState) {
-    DestroyDtoaState(dtoaState);
-  }
-
-  fx.destroyInstance();
-
-#ifdef JS_SIMULATOR
-  js::jit::Simulator::Destroy(simulator_);
-#endif
-
-#ifdef JS_TRACE_LOGGING
-  if (traceLogger) {
-    DestroyTraceLogger(traceLogger);
-  }
-#endif
-
-  if (isolate) {
-    irregexp::DestroyIsolate(isolate.ref());
-  }
-
-  js_delete(atomsZoneFreeLists_.ref());
-
-  TlsContext.set(nullptr);
-}
-
-void JSContext::setHelperThread(AutoLockHelperThreadState& locked) {
-  MOZ_ASSERT_IF(!JSRuntime::hasLiveRuntimes(), !TlsContext.get());
-  TlsContext.set(this);
-  currentThread_ = ThreadId::ThisThreadId();
-}
-
-void JSContext::clearHelperThread(AutoLockHelperThreadState& locked) {
-  currentThread_ = ThreadId();
-  TlsContext.set(nullptr);
-}
-
-void JSContext::setRuntime(JSRuntime* rt) {
-  MOZ_ASSERT(!resolvingList);
-  MOZ_ASSERT(!compartment());
-  MOZ_ASSERT(!activation());
-  MOZ_ASSERT(!unwrappedException_.ref().initialized());
-  MOZ_ASSERT(!unwrappedExceptionStack_.ref().initialized());
-  MOZ_ASSERT(!asyncStackForNewActivations_.ref().initialized());
-
-  runtime_ = rt;
-}
-
-static bool IsOutOfMemoryException(JSContext* cx, const Value& v) {
-  return v == StringValue(cx->names().outOfMemory);
-}
-
-void JSContext::setPendingException(HandleValue v, HandleSavedFrame stack) {
-#if defined(NIGHTLY_BUILD)
-  do {
-    // Do not intercept exceptions if we are already
-    // in the exception interceptor. That would lead
-    // to infinite recursion.
-    if (this->runtime()->errorInterception.isExecuting) {
-      break;
-    }
-
-    // Check whether we have an interceptor at all.
-    if (!this->runtime()->errorInterception.interceptor) {
-      break;
-    }
-
-    // Don't report OOM exceptions. The interceptor isn't interested in those
-    // and they can confuse the interceptor because OOM can be thrown when we
-    // are not in a realm (atom allocation, for example).
-    if (IsOutOfMemoryException(this, v)) {
-      break;
-    }
-
-    // Make sure that we do not call the interceptor from within
-    // the interceptor.
-    this->runtime()->errorInterception.isExecuting = true;
-
-    // The interceptor must be infallible.
-    const mozilla::DebugOnly<bool> wasExceptionPending =
-        this->isExceptionPending();
-    this->runtime()->errorInterception.interceptor->interceptError(this, v);
-    MOZ_ASSERT(wasExceptionPending == this->isExceptionPending());
-
-    this->runtime()->errorInterception.isExecuting = false;
-  } while (false);
-#endif  // defined(NIGHTLY_BUILD)
-
-  // overRecursed_ is set after the fact by ReportOverRecursed.
-  this->overRecursed_ = false;
-  this->throwing = true;
-  this->unwrappedException() = v;
-  this->unwrappedExceptionStack() = stack;
-}
-
-void JSContext::setPendingExceptionAndCaptureStack(HandleValue value) {
-  RootedObject stack(this);
-  if (!CaptureStack(this, &stack)) {
-    clearPendingException();
-  }
-
-  RootedSavedFrame nstack(this);
-  if (stack) {
-    nstack = &stack->as<SavedFrame>();
-  }
-  setPendingException(value, nstack);
-}
-
-bool JSContext::getPendingException(MutableHandleValue rval) {
-  MOZ_ASSERT(throwing);
-  rval.set(unwrappedException());
-  if (zone()->isAtomsZone()) {
-    return true;
-  }
-  RootedSavedFrame stack(this, unwrappedExceptionStack());
-  bool wasOverRecursed = overRecursed_;
-  clearPendingException();
-  if (!compartment()->wrap(this, rval)) {
-    return false;
-  }
-  this->check(rval);
-  setPendingException(rval, stack);
-  overRecursed_ = wasOverRecursed;
-  return true;
-}
-
-SavedFrame* JSContext::getPendingExceptionStack() {
-  return unwrappedExceptionStack();
-}
-
-bool JSContext::isThrowingOutOfMemory() {
-  return throwing && IsOutOfMemoryException(this, unwrappedException());
-}
-
-bool JSContext::isClosingGenerator() {
-  return throwing && unwrappedException().isMagic(JS_GENERATOR_CLOSING);
-}
-
-bool JSContext::isThrowingDebuggeeWouldRun() {
-  return throwing && unwrappedException().isObject() &&
-         unwrappedException().toObject().is<ErrorObject>() &&
-         unwrappedException().toObject().as<ErrorObject>().type() ==
-             JSEXN_DEBUGGEEWOULDRUN;
-}
-
-size_t JSContext::sizeOfExcludingThis(
-    mozilla::MallocSizeOf mallocSizeOf) const {
-  /*
-   * There are other JSContext members that could be measured; the following
-   * ones have been found by DMD to be worth measuring.  More stuff may be
-   * added later.
-   */
-  return cycleDetectorVector().sizeOfExcludingThis(mallocSizeOf) +
-         irregexp::IsolateSizeOfIncludingThis(isolate, mallocSizeOf);
-}
-
-size_t JSContext::sizeOfIncludingThis(
-    mozilla::MallocSizeOf mallocSizeOf) const {
-  return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
-}
-
-#ifdef DEBUG
-bool JSContext::inAtomsZone() const { return zone_->isAtomsZone(); }
-#endif
-
-void JSContext::trace(JSTracer* trc) {
-  cycleDetectorVector().trace(trc);
-  geckoProfiler().trace(trc);
-}
-
-uintptr_t JSContext::stackLimitForJitCode(JS::StackKind kind) {
-#ifdef JS_SIMULATOR
-  return simulator()->stackLimit();
-#else
-  return stackLimit(kind);
-#endif
-}
-
-void JSContext::resetJitStackLimit() {
-  // Note that, for now, we use the untrusted limit for ion. This is fine,
-  // because it's the most conservative limit, and if we hit it, we'll bail
-  // out of ion into the interpreter, which will do a proper recursion check.
-#ifdef JS_SIMULATOR
-  jitStackLimit = jit::Simulator::StackLimit();
-#else
-  jitStackLimit = nativeStackLimit[JS::StackForUntrustedScript];
-#endif
-  jitStackLimitNoInterrupt = jitStackLimit;
-}
-
-void JSContext::initJitStackLimit() { resetJitStackLimit(); }
-
-#ifdef JS_CRASH_DIAGNOSTICS
-void ContextChecks::check(AbstractFramePtr frame, int argIndex) {
-  if (frame) {
-    check(frame.realm(), argIndex);
-  }
-}
-#endif
-
-void AutoEnterOOMUnsafeRegion::crash(const char* reason) {
-  char msgbuf[1024];
-  js::NoteIntentionalCrash();
-  SprintfLiteral(msgbuf, "[unhandlable oom] %s", reason);
-#ifndef DEBUG
-  // In non-DEBUG builds MOZ_CRASH normally doesn't print to stderr so we have
-  // to do this explicitly (the jit-test allow-unhandlable-oom annotation and
-  // fuzzers depend on it).
-  MOZ_ReportCrash(msgbuf, __FILE__, __LINE__);
-#endif
-  MOZ_CRASH_UNSAFE(msgbuf);
-}
-
-mozilla::Atomic<AutoEnterOOMUnsafeRegion::AnnotateOOMAllocationSizeCallback,
-                mozilla::Relaxed>
-    AutoEnterOOMUnsafeRegion::annotateOOMSizeCallback(nullptr);
-
-void AutoEnterOOMUnsafeRegion::crash(size_t size, const char* reason) {
-  {
-    JS::AutoSuppressGCAnalysis suppress;
-    if (annotateOOMSizeCallback) {
-      annotateOOMSizeCallback(size);
-    }
-  }
-  crash(reason);
-}
-
-AutoKeepAtoms::AutoKeepAtoms(
-    JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
-    : cx(cx) {
-  MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-  cx->zone()->keepAtoms();
-}
-
-AutoKeepAtoms::~AutoKeepAtoms() { cx->zone()->releaseAtoms(); };
-
-void ExternalValueArray::trace(JSTracer* trc) {
-  if (Value* vp = begin()) {
-    TraceRootRange(trc, length(), vp, "js::ExternalValueArray");
-  }
-}
-
-#ifdef DEBUG
-AutoUnsafeCallWithABI::AutoUnsafeCallWithABI(UnsafeABIStrictness strictness)
-    : cx_(TlsContext.get()),
-      nested_(cx_ ? cx_->hasAutoUnsafeCallWithABI : false),
-      nogc(cx_) {
-  if (!cx_) {
-    // This is a helper thread doing Ion or Wasm compilation - nothing to do.
-    return;
-  }
-  switch (strictness) {
-    case UnsafeABIStrictness::NoExceptions:
-      MOZ_ASSERT(!JS_IsExceptionPending(cx_));
-      checkForPendingException_ = true;
-      break;
-    case UnsafeABIStrictness::AllowPendingExceptions:
-      checkForPendingException_ = !JS_IsExceptionPending(cx_);
-      break;
-    case UnsafeABIStrictness::AllowThrownExceptions:
-      checkForPendingException_ = false;
-      break;
-  }
-
-  cx_->hasAutoUnsafeCallWithABI = true;
-}
-
-AutoUnsafeCallWithABI::~AutoUnsafeCallWithABI() {
-  if (!cx_) {
-    return;
-  }
-  MOZ_ASSERT(cx_->hasAutoUnsafeCallWithABI);
-  if (!nested_) {
-    cx_->hasAutoUnsafeCallWithABI = false;
-    cx_->inUnsafeCallWithABI = false;
-  }
-  MOZ_ASSERT_IF(checkForPendingException_, !JS_IsExceptionPending(cx_));
-}
-#endif
--- a/js/src/vm/Initialization.cpp
+++ b/js/src/vm/Initialization.cpp
@@ -57,17 +57,17 @@ static unsigned MessageParameterCount(co
   return numfmtspecs;
 }
 
 static void CheckMessageParameterCounts() {
   // Assert that each message format has the correct number of braced
   // parameters.
 #  define MSG_DEF(name, count, exception, format) \
     MOZ_ASSERT(MessageParameterCount(format) == count);
-#  include "js.msg"
+#  include "js/friend/ErrorNumbers.msg"
 #  undef MSG_DEF
 }
 #endif /* DEBUG */
 
 #if defined(JS_RUNTIME_CANONICAL_NAN)
 namespace JS::detail {
 uint64_t CanonicalizedNaNBits;
 }  // namespace JS::detail
--- a/js/src/vm/JSContext.cpp
+++ b/js/src/vm/JSContext.cpp
@@ -654,31 +654,16 @@ JSObject* js::CreateErrorNotesArray(JSCo
     if (!NewbornArrayPush(cx, notesArray, ObjectValue(*noteObj))) {
       return nullptr;
     }
   }
 
   return notesArray;
 }
 
-const JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = {
-#define MSG_DEF(name, count, exception, format) \
-  {#name, format, count, exception},
-#include "js.msg"
-#undef MSG_DEF
-};
-
-JS_FRIEND_API const JSErrorFormatString* js::GetErrorMessage(
-    void* userRef, const unsigned errorNumber) {
-  if (errorNumber > 0 && errorNumber < JSErr_Limit) {
-    return &js_ErrorFormatString[errorNumber];
-  }
-  return nullptr;
-}
-
 void JSContext::recoverFromOutOfMemory() {
   if (isHelperThreadContext()) {
     // Keep in sync with addPendingOutOfMemory.
     if (ParseTask* task = parseTask()) {
       task->outOfMemory = false;
     }
   } else {
     if (isExceptionPending()) {
--- a/js/src/vm/JSContext.h
+++ b/js/src/vm/JSContext.h
@@ -1072,22 +1072,16 @@ extern void ReportIsNullOrUndefinedForPr
  */
 extern bool ReportValueError(JSContext* cx, const unsigned errorNumber,
                              int spindex, HandleValue v, HandleString fallback,
                              const char* arg1 = nullptr,
                              const char* arg2 = nullptr);
 
 JSObject* CreateErrorNotesArray(JSContext* cx, JSErrorReport* report);
 
-} /* namespace js */
-
-extern const JSErrorFormatString js_ErrorFormatString[JSErr_Limit];
-
-namespace js {
-
 /************************************************************************/
 
 /*
  * Encapsulates an external array of values and adds a trace method, for use in
  * Rooted.
  */
 class MOZ_STACK_CLASS ExternalValueArray {
  public:
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -2547,19 +2547,19 @@
     MACRO(Throw, throw_, NULL, 1, 1, 0, JOF_BYTE) \
     /*
      * Create and throw an Error object.
      *
      * Sometimes we know at emit time that an operation always throws. For
      * example, `delete super.prop;` is allowed in methods, but always throws a
      * ReferenceError.
      *
-     * `msgNumber` must be one of the error codes listed in js/src/js.msg; it
-     * determines the `.message` and [[Prototype]] of the new Error object. The
-     * number of arguments in the error message must be 0.
+     * `msgNumber` determines the `.message` and [[Prototype]] of the new Error
+     * object.  It must be an error number in js/public/friend/ErrorNumbers.msg.
+     * The number of arguments in the error message must be 0.
      *
      *   Category: Control flow
      *   Type: Exceptions
      *   Operands: ThrowMsgKind msgNumber
      *   Stack: =>
      */ \
     MACRO(ThrowMsg, throw_msg, NULL, 2, 0, 0, JOF_UINT8) \
     /*
--- a/js/src/vm/SourceHook.cpp
+++ b/js/src/vm/SourceHook.cpp
@@ -1,22 +1,24 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * vim: set ts=8 sts=2 et sw=2 tw=80:
  * 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/. */
 
+#include "js/experimental/SourceHook.h"
+
 #include "mozilla/UniquePtr.h"  // mozilla::UniquePtr
 
 #include <utility>  // std::move
 
 #include "jstypes.h"  // JS_FRIEND_API
 
-#include "js/experimental/SourceHook.h"  // js::{Set,Forget,}SourceHook
-#include "vm/JSContext.h"
+#include "vm/JSContext.h"  // JSContext
+#include "vm/Runtime.h"    // JSRuntime
 
 JS_FRIEND_API void js::SetSourceHook(JSContext* cx,
                                      mozilla::UniquePtr<SourceHook> hook) {
   cx->runtime()->sourceHook.ref() = std::move(hook);
 }
 
 JS_FRIEND_API mozilla::UniquePtr<js::SourceHook> js::ForgetSourceHook(
     JSContext* cx) {
--- a/js/xpconnect/src/jsshell.msg
+++ b/js/xpconnect/src/jsshell.msg
@@ -1,12 +1,12 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  *
  * 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/. */
 
 /*
-	Error messages for JSShell. See js.msg for format.
-*/
+ * Error messages for JSShell. See js/public/friend/ErrorNumbers.msg for format.
+ */
 
 MSG_DEF(JSSMSG_NOT_AN_ERROR,             0, 0, JSEXN_ERR, "<Error #0 is reserved>")
 MSG_DEF(JSSMSG_CANT_OPEN,                1, 2, JSEXN_ERR, "can't open {0}: {1}")