Bug 1568903 - Part 2: Move ErrorObject parts from jsexn.cpp to ErrorObject.cpp. r=jorendorff
☠☠ backed out by cfe990fab350 ☠ ☠
authorAndré Bargull <andre.bargull@gmail.com>
Tue, 12 Nov 2019 11:12:23 +0000
changeset 502138 d2b01a1ad0a966ea1cbd871d33ecdad59a7eee68
parent 502137 58c002a9cb78fa913cbd3b8dce38d1f0032b6bda
child 502139 27c54b414c35ad96a4319eba9ba2da7782fb37e4
push id114172
push userdluca@mozilla.com
push dateTue, 19 Nov 2019 11:31:10 +0000
treeherdermozilla-inbound@b5c5ba07d3db [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs1568903
milestone72.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 1568903 - Part 2: Move ErrorObject parts from jsexn.cpp to ErrorObject.cpp. r=jorendorff The ErrorObject classes are already declared in ErrorObject.h, so it seems useful to also move their definitions into the corresponding cpp file. Also adds `js::CaptureStack` to jsexn.h so it can be called from ErrorObject.cpp and to remove the duplicated implementation in JSContext.cpp. Differential Revision: https://phabricator.services.mozilla.com/D51651
js/src/jsexn.cpp
js/src/jsexn.h
js/src/vm/ErrorObject.cpp
js/src/vm/ErrorObject.h
js/src/vm/JSContext.cpp
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -41,135 +41,16 @@
 #include "vm/StringType.h"
 
 #include "vm/ErrorObject-inl.h"
 #include "vm/JSObject-inl.h"
 #include "vm/SavedStacks-inl.h"
 
 using namespace js;
 
-static void exn_finalize(JSFreeOp* fop, JSObject* obj);
-
-static bool exn_toSource(JSContext* cx, unsigned argc, Value* vp);
-
-#define IMPLEMENT_ERROR_PROTO_CLASS(name)                        \
-  {                                                              \
-    js_Object_str, JSCLASS_HAS_CACHED_PROTO(JSProto_##name),     \
-        JS_NULL_CLASS_OPS,                                       \
-        &ErrorObject::classSpecs[JSProto_##name - JSProto_Error] \
-  }
-
-const JSClass ErrorObject::protoClasses[JSEXN_ERROR_LIMIT] = {
-    IMPLEMENT_ERROR_PROTO_CLASS(Error),
-
-    IMPLEMENT_ERROR_PROTO_CLASS(InternalError),
-    IMPLEMENT_ERROR_PROTO_CLASS(EvalError),
-    IMPLEMENT_ERROR_PROTO_CLASS(RangeError),
-    IMPLEMENT_ERROR_PROTO_CLASS(ReferenceError),
-    IMPLEMENT_ERROR_PROTO_CLASS(SyntaxError),
-    IMPLEMENT_ERROR_PROTO_CLASS(TypeError),
-    IMPLEMENT_ERROR_PROTO_CLASS(URIError),
-
-    IMPLEMENT_ERROR_PROTO_CLASS(DebuggeeWouldRun),
-    IMPLEMENT_ERROR_PROTO_CLASS(CompileError),
-    IMPLEMENT_ERROR_PROTO_CLASS(LinkError),
-    IMPLEMENT_ERROR_PROTO_CLASS(RuntimeError)};
-
-static const JSFunctionSpec error_methods[] = {
-    JS_FN(js_toSource_str, exn_toSource, 0, 0),
-    JS_SELF_HOSTED_FN(js_toString_str, "ErrorToString", 0, 0), JS_FS_END};
-
-static const JSPropertySpec error_properties[] = {
-    JS_STRING_PS("message", "", 0), JS_STRING_PS("name", "Error", 0),
-    // Only Error.prototype has .stack!
-    JS_PSGS("stack", ErrorObject::getStack, ErrorObject::setStack, 0),
-    JS_PS_END};
-
-#define IMPLEMENT_ERROR_PROPERTIES(name) \
-  { JS_STRING_PS("message", "", 0), JS_STRING_PS("name", #name, 0), JS_PS_END }
-
-static const JSPropertySpec other_error_properties[JSEXN_ERROR_LIMIT - 1][3] = {
-    IMPLEMENT_ERROR_PROPERTIES(InternalError),
-    IMPLEMENT_ERROR_PROPERTIES(EvalError),
-    IMPLEMENT_ERROR_PROPERTIES(RangeError),
-    IMPLEMENT_ERROR_PROPERTIES(ReferenceError),
-    IMPLEMENT_ERROR_PROPERTIES(SyntaxError),
-    IMPLEMENT_ERROR_PROPERTIES(TypeError),
-    IMPLEMENT_ERROR_PROPERTIES(URIError),
-    IMPLEMENT_ERROR_PROPERTIES(DebuggeeWouldRun),
-    IMPLEMENT_ERROR_PROPERTIES(CompileError),
-    IMPLEMENT_ERROR_PROPERTIES(LinkError),
-    IMPLEMENT_ERROR_PROPERTIES(RuntimeError)};
-
-#define IMPLEMENT_NATIVE_ERROR_SPEC(name)                                    \
-  {                                                                          \
-    ErrorObject::createConstructor, ErrorObject::createProto, nullptr,       \
-        nullptr, nullptr,                                                    \
-        other_error_properties[JSProto_##name - JSProto_Error - 1], nullptr, \
-        JSProto_Error                                                        \
-  }
-
-#define IMPLEMENT_NONGLOBAL_ERROR_SPEC(name)                                 \
-  {                                                                          \
-    ErrorObject::createConstructor, ErrorObject::createProto, nullptr,       \
-        nullptr, nullptr,                                                    \
-        other_error_properties[JSProto_##name - JSProto_Error - 1], nullptr, \
-        JSProto_Error | ClassSpec::DontDefineConstructor                     \
-  }
-
-const ClassSpec ErrorObject::classSpecs[JSEXN_ERROR_LIMIT] = {
-    {ErrorObject::createConstructor, ErrorObject::createProto, nullptr, nullptr,
-     error_methods, error_properties},
-
-    IMPLEMENT_NATIVE_ERROR_SPEC(InternalError),
-    IMPLEMENT_NATIVE_ERROR_SPEC(EvalError),
-    IMPLEMENT_NATIVE_ERROR_SPEC(RangeError),
-    IMPLEMENT_NATIVE_ERROR_SPEC(ReferenceError),
-    IMPLEMENT_NATIVE_ERROR_SPEC(SyntaxError),
-    IMPLEMENT_NATIVE_ERROR_SPEC(TypeError),
-    IMPLEMENT_NATIVE_ERROR_SPEC(URIError),
-
-    IMPLEMENT_NONGLOBAL_ERROR_SPEC(DebuggeeWouldRun),
-    IMPLEMENT_NONGLOBAL_ERROR_SPEC(CompileError),
-    IMPLEMENT_NONGLOBAL_ERROR_SPEC(LinkError),
-    IMPLEMENT_NONGLOBAL_ERROR_SPEC(RuntimeError)};
-
-#define IMPLEMENT_ERROR_CLASS(name)                                   \
-  {                                                                   \
-    js_Error_str, /* yes, really */                                   \
-        JSCLASS_HAS_CACHED_PROTO(JSProto_##name) |                    \
-            JSCLASS_HAS_RESERVED_SLOTS(ErrorObject::RESERVED_SLOTS) | \
-            JSCLASS_BACKGROUND_FINALIZE,                              \
-        &ErrorObjectClassOps,                                         \
-        &ErrorObject::classSpecs[JSProto_##name - JSProto_Error]      \
-  }
-
-static const JSClassOps ErrorObjectClassOps = {
-    nullptr,               /* addProperty */
-    nullptr,               /* delProperty */
-    nullptr,               /* enumerate */
-    nullptr,               /* newEnumerate */
-    nullptr,               /* resolve */
-    nullptr,               /* mayResolve */
-    exn_finalize, nullptr, /* call        */
-    nullptr,               /* hasInstance */
-    nullptr,               /* construct   */
-    nullptr,               /* trace       */
-};
-
-const JSClass ErrorObject::classes[JSEXN_ERROR_LIMIT] = {
-    IMPLEMENT_ERROR_CLASS(Error), IMPLEMENT_ERROR_CLASS(InternalError),
-    IMPLEMENT_ERROR_CLASS(EvalError), IMPLEMENT_ERROR_CLASS(RangeError),
-    IMPLEMENT_ERROR_CLASS(ReferenceError), IMPLEMENT_ERROR_CLASS(SyntaxError),
-    IMPLEMENT_ERROR_CLASS(TypeError), IMPLEMENT_ERROR_CLASS(URIError),
-    // These Error subclasses are not accessible via the global object:
-    IMPLEMENT_ERROR_CLASS(DebuggeeWouldRun),
-    IMPLEMENT_ERROR_CLASS(CompileError), IMPLEMENT_ERROR_CLASS(LinkError),
-    IMPLEMENT_ERROR_CLASS(RuntimeError)};
-
 size_t ExtraMallocSize(JSErrorReport* report) {
   if (report->linebuf()) {
     /*
      * Count with null terminator and alignment.
      * See CopyExtraData for the details about alignment.
      */
     return (report->linebufLength() + 1) * sizeof(char16_t) + 1;
   }
@@ -312,17 +193,17 @@ struct SuppressErrorsGuard {
 
   ~SuppressErrorsGuard() { JS::SetWarningReporter(cx, prevReporter); }
 };
 
 // Cut off the stack if it gets too deep (most commonly for infinite recursion
 // errors).
 static const size_t MAX_REPORTED_STACK_DEPTH = 1u << 7;
 
-static bool CaptureStack(JSContext* cx, MutableHandleObject stack) {
+bool js::CaptureStack(JSContext* cx, MutableHandleObject stack) {
   return CaptureCurrentStack(
       cx, stack, JS::StackCapture(JS::MaxFrames(MAX_REPORTED_STACK_DEPTH)));
 }
 
 JSString* js::ComputeStackString(JSContext* cx) {
   SuppressErrorsGuard seg(cx);
 
   RootedObject stack(cx);
@@ -333,24 +214,16 @@ JSString* js::ComputeStackString(JSConte
   RootedString str(cx);
   if (!BuildStackString(cx, cx->realm()->principals(), stack, &str)) {
     return nullptr;
   }
 
   return str.get();
 }
 
-static void exn_finalize(JSFreeOp* fop, JSObject* obj) {
-  MOZ_ASSERT(fop->maybeOnHelperThread());
-  if (JSErrorReport* report = obj->as<ErrorObject>().getErrorReport()) {
-    // Bug 1560019: This allocation is not currently tracked.
-    fop->deleteUntracked(report);
-  }
-}
-
 JSErrorReport* js::ErrorFromException(JSContext* cx, HandleObject objArg) {
   // It's ok to UncheckedUnwrap here, since all we do is get the
   // JSErrorReport, and consumers are careful with the information they get
   // from that anyway.  Anyone doing things that would expose anything in the
   // JSErrorReport to page script either does a security check on the
   // JSErrorReport's principal or also tries to do toString on our object and
   // will fail if they can't unwrap it.
   RootedObject obj(cx, UncheckedUnwrap(objArg));
@@ -384,222 +257,16 @@ JS_PUBLIC_API uint64_t JS::ExceptionTime
   ErrorObject* obj = value.toObject().maybeUnwrapIf<ErrorObject>();
   if (!obj) {
     return 0;
   }
 
   return obj->timeWarpTarget();
 }
 
-bool Error(JSContext* cx, unsigned argc, Value* vp) {
-  CallArgs args = CallArgsFromVp(argc, vp);
-
-  // ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when
-  // called as functions, without operator new.  But as we do not give
-  // each constructor a distinct JSClass, we must get the exception type
-  // ourselves.
-  JSExnType exnType =
-      JSExnType(args.callee().as<JSFunction>().getExtendedSlot(0).toInt32());
-
-  JSProtoKey protoKey =
-      JSCLASS_CACHED_PROTO_KEY(&ErrorObject::classes[exnType]);
-
-  // ES6 19.5.1.1 mandates the .prototype lookup happens before the toString
-  RootedObject proto(cx);
-  if (!GetPrototypeFromBuiltinConstructor(cx, args, protoKey, &proto)) {
-    return false;
-  }
-
-  // Compute the error message, if any.
-  RootedString message(cx, nullptr);
-  if (args.hasDefined(0)) {
-    message = ToString<CanGC>(cx, args[0]);
-    if (!message) {
-      return false;
-    }
-  }
-
-  // Find the scripted caller, but only ones we're allowed to know about.
-  NonBuiltinFrameIter iter(cx, cx->realm()->principals());
-
-  RootedString fileName(cx);
-  uint32_t sourceId = 0;
-  if (args.length() > 1) {
-    fileName = ToString<CanGC>(cx, args[1]);
-  } else {
-    fileName = cx->runtime()->emptyString;
-    if (!iter.done()) {
-      if (const char* cfilename = iter.filename()) {
-        fileName = JS_NewStringCopyZ(cx, cfilename);
-      }
-      if (iter.hasScript()) {
-        sourceId = iter.script()->scriptSource()->id();
-      }
-    }
-  }
-  if (!fileName) {
-    return false;
-  }
-
-  uint32_t lineNumber, columnNumber = 0;
-  if (args.length() > 2) {
-    if (!ToUint32(cx, args[2], &lineNumber)) {
-      return false;
-    }
-  } else {
-    lineNumber = iter.done() ? 0 : iter.computeLine(&columnNumber);
-    columnNumber = FixupColumnForDisplay(columnNumber);
-  }
-
-  RootedObject stack(cx);
-  if (!CaptureStack(cx, &stack)) {
-    return false;
-  }
-
-  RootedObject obj(cx, ErrorObject::create(cx, exnType, stack, fileName,
-                                           sourceId, lineNumber, columnNumber,
-                                           nullptr, message, proto));
-  if (!obj) {
-    return false;
-  }
-
-  args.rval().setObject(*obj);
-  return true;
-}
-
-/*
- * Return a string that may eval to something similar to the original object.
- */
-static bool exn_toSource(JSContext* cx, unsigned argc, Value* vp) {
-  if (!CheckRecursionLimit(cx)) {
-    return false;
-  }
-  CallArgs args = CallArgsFromVp(argc, vp);
-
-  RootedObject obj(cx, ToObject(cx, args.thisv()));
-  if (!obj) {
-    return false;
-  }
-
-  RootedValue nameVal(cx);
-  RootedString name(cx);
-  if (!GetProperty(cx, obj, obj, cx->names().name, &nameVal) ||
-      !(name = ToString<CanGC>(cx, nameVal))) {
-    return false;
-  }
-
-  RootedValue messageVal(cx);
-  RootedString message(cx);
-  if (!GetProperty(cx, obj, obj, cx->names().message, &messageVal) ||
-      !(message = ValueToSource(cx, messageVal))) {
-    return false;
-  }
-
-  RootedValue filenameVal(cx);
-  RootedString filename(cx);
-  if (!GetProperty(cx, obj, obj, cx->names().fileName, &filenameVal) ||
-      !(filename = ValueToSource(cx, filenameVal))) {
-    return false;
-  }
-
-  RootedValue linenoVal(cx);
-  uint32_t lineno;
-  if (!GetProperty(cx, obj, obj, cx->names().lineNumber, &linenoVal) ||
-      !ToUint32(cx, linenoVal, &lineno)) {
-    return false;
-  }
-
-  JSStringBuilder sb(cx);
-  if (!sb.append("(new ") || !sb.append(name) || !sb.append("(")) {
-    return false;
-  }
-
-  if (!sb.append(message)) {
-    return false;
-  }
-
-  if (!filename->empty()) {
-    if (!sb.append(", ") || !sb.append(filename)) {
-      return false;
-    }
-  }
-  if (lineno != 0) {
-    /* We have a line, but no filename, add empty string */
-    if (filename->empty() && !sb.append(", \"\"")) {
-      return false;
-    }
-
-    JSString* linenumber = ToString<CanGC>(cx, linenoVal);
-    if (!linenumber) {
-      return false;
-    }
-    if (!sb.append(", ") || !sb.append(linenumber)) {
-      return false;
-    }
-  }
-
-  if (!sb.append("))")) {
-    return false;
-  }
-
-  JSString* str = sb.finishString();
-  if (!str) {
-    return false;
-  }
-  args.rval().setString(str);
-  return true;
-}
-
-/* static */
-JSObject* ErrorObject::createProto(JSContext* cx, JSProtoKey key) {
-  JSExnType type = ExnTypeFromProtoKey(key);
-
-  if (type == JSEXN_ERR) {
-    return GlobalObject::createBlankPrototype(
-        cx, cx->global(), &ErrorObject::protoClasses[JSEXN_ERR]);
-  }
-
-  RootedObject protoProto(
-      cx, GlobalObject::getOrCreateErrorPrototype(cx, cx->global()));
-  if (!protoProto) {
-    return nullptr;
-  }
-
-  return GlobalObject::createBlankPrototypeInheriting(
-      cx, &ErrorObject::protoClasses[type], protoProto);
-}
-
-/* static */
-JSObject* ErrorObject::createConstructor(JSContext* cx, JSProtoKey key) {
-  JSExnType type = ExnTypeFromProtoKey(key);
-  RootedObject ctor(cx);
-
-  if (type == JSEXN_ERR) {
-    ctor = GenericCreateConstructor<Error, 1, gc::AllocKind::FUNCTION_EXTENDED>(
-        cx, key);
-  } else {
-    RootedFunction proto(
-        cx, GlobalObject::getOrCreateErrorConstructor(cx, cx->global()));
-    if (!proto) {
-      return nullptr;
-    }
-
-    ctor = NewFunctionWithProto(
-        cx, Error, 1, FunctionFlags::NATIVE_CTOR, nullptr, ClassName(key, cx),
-        proto, gc::AllocKind::FUNCTION_EXTENDED, SingletonObject);
-  }
-
-  if (!ctor) {
-    return nullptr;
-  }
-
-  ctor->as<JSFunction>().setExtendedSlot(0, Int32Value(type));
-  return ctor;
-}
-
 JS_FRIEND_API JSLinearString* js::GetErrorTypeName(JSContext* cx,
                                                    int16_t exnType) {
   /*
    * JSEXN_INTERNALERR returns null to prevent that "InternalError: "
    * is prepended before "uncaught exception: "
    */
   if (exnType < 0 || exnType >= JSEXN_LIMIT || exnType == JSEXN_INTERNALERR ||
       exnType == JSEXN_WARN || exnType == JSEXN_NOTE) {
--- a/js/src/jsexn.h
+++ b/js/src/jsexn.h
@@ -20,16 +20,18 @@
 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);
+
 JSString* ComputeStackString(JSContext* cx);
 
 /*
  * Given a JSErrorReport, check to see if there is an exception associated with
  * the error number.  If there is, then create an appropriate exception object,
  * set it as the pending exception, and set the JSREPORT_EXCEPTION flag on the
  * error report.
  *
--- a/js/src/vm/ErrorObject.cpp
+++ b/js/src/vm/ErrorObject.cpp
@@ -10,27 +10,278 @@
 #include "mozilla/Range.h"
 
 #include <utility>
 
 #include "jsexn.h"
 
 #include "js/CallArgs.h"
 #include "js/CharacterEncoding.h"
+#include "util/StringBuffer.h"
 #include "vm/GlobalObject.h"
 #include "vm/SelfHosting.h"
 #include "vm/StringType.h"
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/SavedStacks-inl.h"
 #include "vm/Shape-inl.h"
 
 using namespace js;
 
+#define IMPLEMENT_ERROR_PROTO_CLASS(name)                        \
+  {                                                              \
+    js_Object_str, JSCLASS_HAS_CACHED_PROTO(JSProto_##name),     \
+        JS_NULL_CLASS_OPS,                                       \
+        &ErrorObject::classSpecs[JSProto_##name - JSProto_Error] \
+  }
+
+const JSClass ErrorObject::protoClasses[JSEXN_ERROR_LIMIT] = {
+    IMPLEMENT_ERROR_PROTO_CLASS(Error),
+
+    IMPLEMENT_ERROR_PROTO_CLASS(InternalError),
+    IMPLEMENT_ERROR_PROTO_CLASS(EvalError),
+    IMPLEMENT_ERROR_PROTO_CLASS(RangeError),
+    IMPLEMENT_ERROR_PROTO_CLASS(ReferenceError),
+    IMPLEMENT_ERROR_PROTO_CLASS(SyntaxError),
+    IMPLEMENT_ERROR_PROTO_CLASS(TypeError),
+    IMPLEMENT_ERROR_PROTO_CLASS(URIError),
+
+    IMPLEMENT_ERROR_PROTO_CLASS(DebuggeeWouldRun),
+    IMPLEMENT_ERROR_PROTO_CLASS(CompileError),
+    IMPLEMENT_ERROR_PROTO_CLASS(LinkError),
+    IMPLEMENT_ERROR_PROTO_CLASS(RuntimeError)};
+
+static bool exn_toSource(JSContext* cx, unsigned argc, Value* vp);
+
+static const JSFunctionSpec error_methods[] = {
+    JS_FN(js_toSource_str, exn_toSource, 0, 0),
+    JS_SELF_HOSTED_FN(js_toString_str, "ErrorToString", 0, 0), JS_FS_END};
+
+static const JSPropertySpec error_properties[] = {
+    JS_STRING_PS("message", "", 0), JS_STRING_PS("name", "Error", 0),
+    // Only Error.prototype has .stack!
+    JS_PSGS("stack", ErrorObject::getStack, ErrorObject::setStack, 0),
+    JS_PS_END};
+
+#define IMPLEMENT_ERROR_PROPERTIES(name) \
+  { JS_STRING_PS("message", "", 0), JS_STRING_PS("name", #name, 0), JS_PS_END }
+
+static const JSPropertySpec other_error_properties[JSEXN_ERROR_LIMIT - 1][3] = {
+    IMPLEMENT_ERROR_PROPERTIES(InternalError),
+    IMPLEMENT_ERROR_PROPERTIES(EvalError),
+    IMPLEMENT_ERROR_PROPERTIES(RangeError),
+    IMPLEMENT_ERROR_PROPERTIES(ReferenceError),
+    IMPLEMENT_ERROR_PROPERTIES(SyntaxError),
+    IMPLEMENT_ERROR_PROPERTIES(TypeError),
+    IMPLEMENT_ERROR_PROPERTIES(URIError),
+    IMPLEMENT_ERROR_PROPERTIES(DebuggeeWouldRun),
+    IMPLEMENT_ERROR_PROPERTIES(CompileError),
+    IMPLEMENT_ERROR_PROPERTIES(LinkError),
+    IMPLEMENT_ERROR_PROPERTIES(RuntimeError)};
+
+#define IMPLEMENT_NATIVE_ERROR_SPEC(name)                                    \
+  {                                                                          \
+    ErrorObject::createConstructor, ErrorObject::createProto, nullptr,       \
+        nullptr, nullptr,                                                    \
+        other_error_properties[JSProto_##name - JSProto_Error - 1], nullptr, \
+        JSProto_Error                                                        \
+  }
+
+#define IMPLEMENT_NONGLOBAL_ERROR_SPEC(name)                                 \
+  {                                                                          \
+    ErrorObject::createConstructor, ErrorObject::createProto, nullptr,       \
+        nullptr, nullptr,                                                    \
+        other_error_properties[JSProto_##name - JSProto_Error - 1], nullptr, \
+        JSProto_Error | ClassSpec::DontDefineConstructor                     \
+  }
+
+const ClassSpec ErrorObject::classSpecs[JSEXN_ERROR_LIMIT] = {
+    {ErrorObject::createConstructor, ErrorObject::createProto, nullptr, nullptr,
+     error_methods, error_properties},
+
+    IMPLEMENT_NATIVE_ERROR_SPEC(InternalError),
+    IMPLEMENT_NATIVE_ERROR_SPEC(EvalError),
+    IMPLEMENT_NATIVE_ERROR_SPEC(RangeError),
+    IMPLEMENT_NATIVE_ERROR_SPEC(ReferenceError),
+    IMPLEMENT_NATIVE_ERROR_SPEC(SyntaxError),
+    IMPLEMENT_NATIVE_ERROR_SPEC(TypeError),
+    IMPLEMENT_NATIVE_ERROR_SPEC(URIError),
+
+    IMPLEMENT_NONGLOBAL_ERROR_SPEC(DebuggeeWouldRun),
+    IMPLEMENT_NONGLOBAL_ERROR_SPEC(CompileError),
+    IMPLEMENT_NONGLOBAL_ERROR_SPEC(LinkError),
+    IMPLEMENT_NONGLOBAL_ERROR_SPEC(RuntimeError)};
+
+#define IMPLEMENT_ERROR_CLASS(name)                                   \
+  {                                                                   \
+    js_Error_str, /* yes, really */                                   \
+        JSCLASS_HAS_CACHED_PROTO(JSProto_##name) |                    \
+            JSCLASS_HAS_RESERVED_SLOTS(ErrorObject::RESERVED_SLOTS) | \
+            JSCLASS_BACKGROUND_FINALIZE,                              \
+        &ErrorObjectClassOps,                                         \
+        &ErrorObject::classSpecs[JSProto_##name - JSProto_Error]      \
+  }
+
+static void exn_finalize(JSFreeOp* fop, JSObject* obj);
+
+static const JSClassOps ErrorObjectClassOps = {
+    nullptr,               /* addProperty */
+    nullptr,               /* delProperty */
+    nullptr,               /* enumerate */
+    nullptr,               /* newEnumerate */
+    nullptr,               /* resolve */
+    nullptr,               /* mayResolve */
+    exn_finalize, nullptr, /* call        */
+    nullptr,               /* hasInstance */
+    nullptr,               /* construct   */
+    nullptr,               /* trace       */
+};
+
+const JSClass ErrorObject::classes[JSEXN_ERROR_LIMIT] = {
+    IMPLEMENT_ERROR_CLASS(Error), IMPLEMENT_ERROR_CLASS(InternalError),
+    IMPLEMENT_ERROR_CLASS(EvalError), IMPLEMENT_ERROR_CLASS(RangeError),
+    IMPLEMENT_ERROR_CLASS(ReferenceError), IMPLEMENT_ERROR_CLASS(SyntaxError),
+    IMPLEMENT_ERROR_CLASS(TypeError), IMPLEMENT_ERROR_CLASS(URIError),
+    // These Error subclasses are not accessible via the global object:
+    IMPLEMENT_ERROR_CLASS(DebuggeeWouldRun),
+    IMPLEMENT_ERROR_CLASS(CompileError), IMPLEMENT_ERROR_CLASS(LinkError),
+    IMPLEMENT_ERROR_CLASS(RuntimeError)};
+
+static void exn_finalize(JSFreeOp* fop, JSObject* obj) {
+  MOZ_ASSERT(fop->maybeOnHelperThread());
+  if (JSErrorReport* report = obj->as<ErrorObject>().getErrorReport()) {
+    // Bug 1560019: This allocation is not currently tracked.
+    fop->deleteUntracked(report);
+  }
+}
+
+static bool Error(JSContext* cx, unsigned argc, Value* vp) {
+  CallArgs args = CallArgsFromVp(argc, vp);
+
+  // ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when
+  // called as functions, without operator new.  But as we do not give
+  // each constructor a distinct JSClass, we must get the exception type
+  // ourselves.
+  JSExnType exnType =
+      JSExnType(args.callee().as<JSFunction>().getExtendedSlot(0).toInt32());
+
+  JSProtoKey protoKey =
+      JSCLASS_CACHED_PROTO_KEY(&ErrorObject::classes[exnType]);
+
+  // ES6 19.5.1.1 mandates the .prototype lookup happens before the toString
+  RootedObject proto(cx);
+  if (!GetPrototypeFromBuiltinConstructor(cx, args, protoKey, &proto)) {
+    return false;
+  }
+
+  // Compute the error message, if any.
+  RootedString message(cx, nullptr);
+  if (args.hasDefined(0)) {
+    message = ToString<CanGC>(cx, args[0]);
+    if (!message) {
+      return false;
+    }
+  }
+
+  // Find the scripted caller, but only ones we're allowed to know about.
+  NonBuiltinFrameIter iter(cx, cx->realm()->principals());
+
+  RootedString fileName(cx);
+  uint32_t sourceId = 0;
+  if (args.length() > 1) {
+    fileName = ToString<CanGC>(cx, args[1]);
+  } else {
+    fileName = cx->runtime()->emptyString;
+    if (!iter.done()) {
+      if (const char* cfilename = iter.filename()) {
+        fileName = JS_NewStringCopyZ(cx, cfilename);
+      }
+      if (iter.hasScript()) {
+        sourceId = iter.script()->scriptSource()->id();
+      }
+    }
+  }
+  if (!fileName) {
+    return false;
+  }
+
+  uint32_t lineNumber, columnNumber = 0;
+  if (args.length() > 2) {
+    if (!ToUint32(cx, args[2], &lineNumber)) {
+      return false;
+    }
+  } else {
+    lineNumber = iter.done() ? 0 : iter.computeLine(&columnNumber);
+    columnNumber = FixupColumnForDisplay(columnNumber);
+  }
+
+  RootedObject stack(cx);
+  if (!CaptureStack(cx, &stack)) {
+    return false;
+  }
+
+  RootedObject obj(cx, ErrorObject::create(cx, exnType, stack, fileName,
+                                           sourceId, lineNumber, columnNumber,
+                                           nullptr, message, proto));
+  if (!obj) {
+    return false;
+  }
+
+  args.rval().setObject(*obj);
+  return true;
+}
+
+/* static */
+JSObject* ErrorObject::createProto(JSContext* cx, JSProtoKey key) {
+  JSExnType type = ExnTypeFromProtoKey(key);
+
+  if (type == JSEXN_ERR) {
+    return GlobalObject::createBlankPrototype(
+        cx, cx->global(), &ErrorObject::protoClasses[JSEXN_ERR]);
+  }
+
+  RootedObject protoProto(
+      cx, GlobalObject::getOrCreateErrorPrototype(cx, cx->global()));
+  if (!protoProto) {
+    return nullptr;
+  }
+
+  return GlobalObject::createBlankPrototypeInheriting(
+      cx, &ErrorObject::protoClasses[type], protoProto);
+}
+
+/* static */
+JSObject* ErrorObject::createConstructor(JSContext* cx, JSProtoKey key) {
+  JSExnType type = ExnTypeFromProtoKey(key);
+  RootedObject ctor(cx);
+
+  if (type == JSEXN_ERR) {
+    ctor = GenericCreateConstructor<Error, 1, gc::AllocKind::FUNCTION_EXTENDED>(
+        cx, key);
+  } else {
+    RootedFunction proto(
+        cx, GlobalObject::getOrCreateErrorConstructor(cx, cx->global()));
+    if (!proto) {
+      return nullptr;
+    }
+
+    ctor = NewFunctionWithProto(
+        cx, Error, 1, FunctionFlags::NATIVE_CTOR, nullptr, ClassName(key, cx),
+        proto, gc::AllocKind::FUNCTION_EXTENDED, SingletonObject);
+  }
+
+  if (!ctor) {
+    return nullptr;
+  }
+
+  ctor->as<JSFunction>().setExtendedSlot(0, Int32Value(type));
+  return ctor;
+}
+
 /* static */
 Shape* js::ErrorObject::assignInitialShape(JSContext* cx,
                                            Handle<ErrorObject*> obj) {
   MOZ_ASSERT(obj->empty());
 
   if (!NativeObject::addDataProperty(cx, obj, cx->names().fileName,
                                      FILENAME_SLOT, 0)) {
     return nullptr;
@@ -309,8 +560,91 @@ bool js::ErrorObject::setStack_impl(JSCo
 
   if (!args.requireAtLeast(cx, "(set stack)", 1)) {
     return false;
   }
   RootedValue val(cx, args[0]);
 
   return DefineDataProperty(cx, thisObj, cx->names().stack, val);
 }
+
+/*
+ * Return a string that may eval to something similar to the original object.
+ */
+static bool exn_toSource(JSContext* cx, unsigned argc, Value* vp) {
+  if (!CheckRecursionLimit(cx)) {
+    return false;
+  }
+  CallArgs args = CallArgsFromVp(argc, vp);
+
+  RootedObject obj(cx, ToObject(cx, args.thisv()));
+  if (!obj) {
+    return false;
+  }
+
+  RootedValue nameVal(cx);
+  RootedString name(cx);
+  if (!GetProperty(cx, obj, obj, cx->names().name, &nameVal) ||
+      !(name = ToString<CanGC>(cx, nameVal))) {
+    return false;
+  }
+
+  RootedValue messageVal(cx);
+  RootedString message(cx);
+  if (!GetProperty(cx, obj, obj, cx->names().message, &messageVal) ||
+      !(message = ValueToSource(cx, messageVal))) {
+    return false;
+  }
+
+  RootedValue filenameVal(cx);
+  RootedString filename(cx);
+  if (!GetProperty(cx, obj, obj, cx->names().fileName, &filenameVal) ||
+      !(filename = ValueToSource(cx, filenameVal))) {
+    return false;
+  }
+
+  RootedValue linenoVal(cx);
+  uint32_t lineno;
+  if (!GetProperty(cx, obj, obj, cx->names().lineNumber, &linenoVal) ||
+      !ToUint32(cx, linenoVal, &lineno)) {
+    return false;
+  }
+
+  JSStringBuilder sb(cx);
+  if (!sb.append("(new ") || !sb.append(name) || !sb.append("(")) {
+    return false;
+  }
+
+  if (!sb.append(message)) {
+    return false;
+  }
+
+  if (!filename->empty()) {
+    if (!sb.append(", ") || !sb.append(filename)) {
+      return false;
+    }
+  }
+  if (lineno != 0) {
+    /* We have a line, but no filename, add empty string */
+    if (filename->empty() && !sb.append(", \"\"")) {
+      return false;
+    }
+
+    JSString* linenumber = ToString<CanGC>(cx, linenoVal);
+    if (!linenumber) {
+      return false;
+    }
+    if (!sb.append(", ") || !sb.append(linenumber)) {
+      return false;
+    }
+  }
+
+  if (!sb.append("))")) {
+    return false;
+  }
+
+  JSString* str = sb.finishString();
+  if (!str) {
+    return false;
+  }
+  args.rval().setString(str);
+  return true;
+}
--- a/js/src/vm/ErrorObject.h
+++ b/js/src/vm/ErrorObject.h
@@ -11,29 +11,21 @@
 
 #include "js/UniquePtr.h"
 #include "vm/NativeObject.h"
 #include "vm/SavedStacks.h"
 #include "vm/Shape.h"
 
 namespace js {
 
-/*
- * Initialize the exception constructor/prototype hierarchy.
- */
-extern JSObject* InitExceptionClasses(JSContext* cx, HandleObject obj);
-
 class ErrorObject : public NativeObject {
   static JSObject* createProto(JSContext* cx, JSProtoKey key);
 
   static JSObject* createConstructor(JSContext* cx, JSProtoKey key);
 
-  /* For access to createProto. */
-  friend JSObject* js::InitExceptionClasses(JSContext* cx, HandleObject global);
-
   static bool init(JSContext* cx, Handle<ErrorObject*> obj, JSExnType type,
                    UniquePtr<JSErrorReport> errorReport, HandleString fileName,
                    HandleObject stack, uint32_t sourceId, uint32_t lineNumber,
                    uint32_t columnNumber, HandleString message);
 
   static const ClassSpec classSpecs[JSEXN_ERROR_LIMIT];
   static const JSClass protoClasses[JSEXN_ERROR_LIMIT];
 
--- a/js/src/vm/JSContext.cpp
+++ b/js/src/vm/JSContext.cpp
@@ -1398,23 +1398,19 @@ void JSContext::setPendingException(Hand
   // overRecursed_ is set after the fact by ReportOverRecursed.
   this->overRecursed_ = false;
   this->throwing = true;
   this->unwrappedException() = v;
   this->unwrappedExceptionStack() = stack;
   check(v);
 }
 
-static const size_t MAX_REPORTED_STACK_DEPTH = 1u << 7;
-
 void JSContext::setPendingExceptionAndCaptureStack(HandleValue value) {
   RootedObject stack(this);
-  if (!CaptureCurrentStack(
-          this, &stack,
-          JS::StackCapture(JS::MaxFrames(MAX_REPORTED_STACK_DEPTH)))) {
+  if (!CaptureStack(this, &stack)) {
     clearPendingException();
   }
 
   RootedSavedFrame nstack(this);
   if (stack) {
     nstack = &stack->as<SavedFrame>();
   }
   setPendingException(value, nstack);