LCOV - code coverage report
Current view: top level - js/src - jsexn.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 115 504 22.8 %
Date: 2017-07-14 16:53:18 Functions: 9 34 26.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       3             :  * This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : /*
       8             :  * JS standard exception implementation.
       9             :  */
      10             : 
      11             : #include "jsexn.h"
      12             : 
      13             : #include "mozilla/ArrayUtils.h"
      14             : #include "mozilla/PodOperations.h"
      15             : #include "mozilla/Sprintf.h"
      16             : 
      17             : #include <string.h>
      18             : 
      19             : #include "jsapi.h"
      20             : #include "jscntxt.h"
      21             : #include "jsfun.h"
      22             : #include "jsnum.h"
      23             : #include "jsobj.h"
      24             : #include "jsprf.h"
      25             : #include "jsscript.h"
      26             : #include "jstypes.h"
      27             : #include "jsutil.h"
      28             : #include "jswrapper.h"
      29             : 
      30             : #include "gc/Marking.h"
      31             : #include "js/CharacterEncoding.h"
      32             : #include "vm/ErrorObject.h"
      33             : #include "vm/GlobalObject.h"
      34             : #include "vm/SavedStacks.h"
      35             : #include "vm/SelfHosting.h"
      36             : #include "vm/StringBuffer.h"
      37             : 
      38             : #include "jsobjinlines.h"
      39             : 
      40             : #include "vm/ErrorObject-inl.h"
      41             : #include "vm/SavedStacks-inl.h"
      42             : 
      43             : using namespace js;
      44             : using namespace js::gc;
      45             : 
      46             : using mozilla::ArrayLength;
      47             : using mozilla::PodArrayZero;
      48             : 
      49             : static void
      50             : exn_finalize(FreeOp* fop, JSObject* obj);
      51             : 
      52             : static bool
      53             : exn_toSource(JSContext* cx, unsigned argc, Value* vp);
      54             : 
      55             : #define IMPLEMENT_ERROR_PROTO_CLASS(name) \
      56             :     { \
      57             :         js_Object_str, \
      58             :         JSCLASS_HAS_CACHED_PROTO(JSProto_##name), \
      59             :         JS_NULL_CLASS_OPS, \
      60             :         &ErrorObject::classSpecs[JSProto_##name - JSProto_Error] \
      61             :     }
      62             : 
      63             : const Class
      64             : ErrorObject::protoClasses[JSEXN_ERROR_LIMIT] = {
      65             :     IMPLEMENT_ERROR_PROTO_CLASS(Error),
      66             : 
      67             :     IMPLEMENT_ERROR_PROTO_CLASS(InternalError),
      68             :     IMPLEMENT_ERROR_PROTO_CLASS(EvalError),
      69             :     IMPLEMENT_ERROR_PROTO_CLASS(RangeError),
      70             :     IMPLEMENT_ERROR_PROTO_CLASS(ReferenceError),
      71             :     IMPLEMENT_ERROR_PROTO_CLASS(SyntaxError),
      72             :     IMPLEMENT_ERROR_PROTO_CLASS(TypeError),
      73             :     IMPLEMENT_ERROR_PROTO_CLASS(URIError),
      74             : 
      75             :     IMPLEMENT_ERROR_PROTO_CLASS(DebuggeeWouldRun),
      76             :     IMPLEMENT_ERROR_PROTO_CLASS(CompileError),
      77             :     IMPLEMENT_ERROR_PROTO_CLASS(LinkError),
      78             :     IMPLEMENT_ERROR_PROTO_CLASS(RuntimeError)
      79             : };
      80             : 
      81             : static const JSFunctionSpec error_methods[] = {
      82             : #if JS_HAS_TOSOURCE
      83             :     JS_FN(js_toSource_str, exn_toSource, 0, 0),
      84             : #endif
      85             :     JS_SELF_HOSTED_FN(js_toString_str, "ErrorToString", 0,0),
      86             :     JS_FS_END
      87             : };
      88             : 
      89             : static const JSPropertySpec error_properties[] = {
      90             :     JS_STRING_PS("message", "", 0),
      91             :     JS_STRING_PS("name", "Error", 0),
      92             :     // Only Error.prototype has .stack!
      93             :     JS_PSGS("stack", ErrorObject::getStack, ErrorObject::setStack, 0),
      94             :     JS_PS_END
      95             : };
      96             : 
      97             : #define IMPLEMENT_ERROR_PROPERTIES(name) \
      98             :     { \
      99             :         JS_STRING_PS("message", "", 0), \
     100             :         JS_STRING_PS("name", #name, 0), \
     101             :         JS_PS_END \
     102             :     }
     103             : 
     104             : static const JSPropertySpec other_error_properties[JSEXN_ERROR_LIMIT - 1][3] = {
     105             :     IMPLEMENT_ERROR_PROPERTIES(InternalError),
     106             :     IMPLEMENT_ERROR_PROPERTIES(EvalError),
     107             :     IMPLEMENT_ERROR_PROPERTIES(RangeError),
     108             :     IMPLEMENT_ERROR_PROPERTIES(ReferenceError),
     109             :     IMPLEMENT_ERROR_PROPERTIES(SyntaxError),
     110             :     IMPLEMENT_ERROR_PROPERTIES(TypeError),
     111             :     IMPLEMENT_ERROR_PROPERTIES(URIError),
     112             :     IMPLEMENT_ERROR_PROPERTIES(DebuggeeWouldRun),
     113             :     IMPLEMENT_ERROR_PROPERTIES(CompileError),
     114             :     IMPLEMENT_ERROR_PROPERTIES(LinkError),
     115             :     IMPLEMENT_ERROR_PROPERTIES(RuntimeError)
     116             : };
     117             : 
     118             : #define IMPLEMENT_NATIVE_ERROR_SPEC(name) \
     119             :     { \
     120             :         ErrorObject::createConstructor, \
     121             :         ErrorObject::createProto, \
     122             :         nullptr, \
     123             :         nullptr, \
     124             :         nullptr, \
     125             :         other_error_properties[JSProto_##name - JSProto_Error - 1], \
     126             :         nullptr, \
     127             :         JSProto_Error \
     128             :     }
     129             : 
     130             : #define IMPLEMENT_NONGLOBAL_ERROR_SPEC(name) \
     131             :     { \
     132             :         ErrorObject::createConstructor, \
     133             :         ErrorObject::createProto, \
     134             :         nullptr, \
     135             :         nullptr, \
     136             :         nullptr, \
     137             :         other_error_properties[JSProto_##name - JSProto_Error - 1], \
     138             :         nullptr, \
     139             :         JSProto_Error | ClassSpec::DontDefineConstructor \
     140             :     }
     141             : 
     142             : const ClassSpec
     143             : ErrorObject::classSpecs[JSEXN_ERROR_LIMIT] = {
     144             :     {
     145             :         ErrorObject::createConstructor,
     146             :         ErrorObject::createProto,
     147             :         nullptr,
     148             :         nullptr,
     149             :         error_methods,
     150             :         error_properties
     151             :     },
     152             : 
     153             :     IMPLEMENT_NATIVE_ERROR_SPEC(InternalError),
     154             :     IMPLEMENT_NATIVE_ERROR_SPEC(EvalError),
     155             :     IMPLEMENT_NATIVE_ERROR_SPEC(RangeError),
     156             :     IMPLEMENT_NATIVE_ERROR_SPEC(ReferenceError),
     157             :     IMPLEMENT_NATIVE_ERROR_SPEC(SyntaxError),
     158             :     IMPLEMENT_NATIVE_ERROR_SPEC(TypeError),
     159             :     IMPLEMENT_NATIVE_ERROR_SPEC(URIError),
     160             : 
     161             :     IMPLEMENT_NONGLOBAL_ERROR_SPEC(DebuggeeWouldRun),
     162             :     IMPLEMENT_NONGLOBAL_ERROR_SPEC(CompileError),
     163             :     IMPLEMENT_NONGLOBAL_ERROR_SPEC(LinkError),
     164             :     IMPLEMENT_NONGLOBAL_ERROR_SPEC(RuntimeError)
     165             : };
     166             : 
     167             : #define IMPLEMENT_ERROR_CLASS(name) \
     168             :     { \
     169             :         js_Error_str, /* yes, really */ \
     170             :         JSCLASS_HAS_CACHED_PROTO(JSProto_##name) | \
     171             :         JSCLASS_HAS_RESERVED_SLOTS(ErrorObject::RESERVED_SLOTS) | \
     172             :         JSCLASS_BACKGROUND_FINALIZE, \
     173             :         &ErrorObjectClassOps, \
     174             :         &ErrorObject::classSpecs[JSProto_##name - JSProto_Error ] \
     175             :     }
     176             : 
     177             : static const ClassOps ErrorObjectClassOps = {
     178             :     nullptr,                 /* addProperty */
     179             :     nullptr,                 /* delProperty */
     180             :     nullptr,                 /* getProperty */
     181             :     nullptr,                 /* setProperty */
     182             :     nullptr,                 /* enumerate */
     183             :     nullptr,                 /* newEnumerate */
     184             :     nullptr,                 /* resolve */
     185             :     nullptr,                 /* mayResolve */
     186             :     exn_finalize,
     187             :     nullptr,                 /* call        */
     188             :     nullptr,                 /* hasInstance */
     189             :     nullptr,                 /* construct   */
     190             :     nullptr,                 /* trace       */
     191             : };
     192             : 
     193             : const Class
     194             : ErrorObject::classes[JSEXN_ERROR_LIMIT] = {
     195             :     IMPLEMENT_ERROR_CLASS(Error),
     196             :     IMPLEMENT_ERROR_CLASS(InternalError),
     197             :     IMPLEMENT_ERROR_CLASS(EvalError),
     198             :     IMPLEMENT_ERROR_CLASS(RangeError),
     199             :     IMPLEMENT_ERROR_CLASS(ReferenceError),
     200             :     IMPLEMENT_ERROR_CLASS(SyntaxError),
     201             :     IMPLEMENT_ERROR_CLASS(TypeError),
     202             :     IMPLEMENT_ERROR_CLASS(URIError),
     203             :     // These Error subclasses are not accessible via the global object:
     204             :     IMPLEMENT_ERROR_CLASS(DebuggeeWouldRun),
     205             :     IMPLEMENT_ERROR_CLASS(CompileError),
     206             :     IMPLEMENT_ERROR_CLASS(LinkError),
     207             :     IMPLEMENT_ERROR_CLASS(RuntimeError)
     208             : };
     209             : 
     210             : size_t
     211           2 : ExtraMallocSize(JSErrorReport* report)
     212             : {
     213           2 :     if (report->linebuf()) {
     214             :         /*
     215             :          * Count with null terminator and alignment.
     216             :          * See CopyExtraData for the details about alignment.
     217             :          */
     218           0 :         return (report->linebufLength() + 1) * sizeof(char16_t) + 1;
     219             :     }
     220             : 
     221           2 :     return 0;
     222             : }
     223             : 
     224             : size_t
     225           0 : ExtraMallocSize(JSErrorNotes::Note* note)
     226             : {
     227           0 :     return 0;
     228             : }
     229             : 
     230             : bool
     231           2 : CopyExtraData(JSContext* cx, uint8_t** cursor, JSErrorReport* copy, JSErrorReport* report)
     232             : {
     233           2 :     if (report->linebuf()) {
     234             :         /*
     235             :          * Make sure cursor is properly aligned for char16_t for platforms
     236             :          * which need it and it's at the end of the buffer on exit.
     237             :          */
     238           0 :         size_t alignment_backlog = 0;
     239           0 :         if (size_t(*cursor) % 2)
     240           0 :             (*cursor)++;
     241             :         else
     242           0 :             alignment_backlog = 1;
     243             : 
     244           0 :         size_t linebufSize = (report->linebufLength() + 1) * sizeof(char16_t);
     245           0 :         const char16_t* linebufCopy = (const char16_t*)(*cursor);
     246           0 :         js_memcpy(*cursor, report->linebuf(), linebufSize);
     247           0 :         *cursor += linebufSize + alignment_backlog;
     248           0 :         copy->initBorrowedLinebuf(linebufCopy, report->linebufLength(), report->tokenOffset());
     249             :     }
     250             : 
     251             :     /* Copy non-pointer members. */
     252           2 :     copy->isMuted = report->isMuted;
     253           2 :     copy->exnType = report->exnType;
     254             : 
     255             :     /* Note that this is before it gets flagged with JSREPORT_EXCEPTION */
     256           2 :     copy->flags = report->flags;
     257             : 
     258             :     /* Deep copy notes. */
     259           2 :     if (report->notes) {
     260           0 :         auto copiedNotes = report->notes->copy(cx);
     261           0 :         if (!copiedNotes)
     262           0 :             return false;
     263           0 :         copy->notes = Move(copiedNotes);
     264             :     } else {
     265           2 :         copy->notes.reset(nullptr);
     266             :     }
     267             : 
     268           2 :     return true;
     269             : }
     270             : 
     271             : bool
     272           0 : CopyExtraData(JSContext* cx, uint8_t** cursor, JSErrorNotes::Note* copy, JSErrorNotes::Note* report)
     273             : {
     274           0 :     return true;
     275             : }
     276             : 
     277             : template <typename T>
     278             : static T*
     279           2 : CopyErrorHelper(JSContext* cx, T* report)
     280             : {
     281             :     /*
     282             :      * We use a single malloc block to make a deep copy of JSErrorReport or
     283             :      * JSErrorNotes::Note, except JSErrorNotes linked from JSErrorReport with
     284             :      * the following layout:
     285             :      *   JSErrorReport or JSErrorNotes::Note
     286             :      *   char array with characters for message_
     287             :      *   char array with characters for filename
     288             :      *   char16_t array with characters for linebuf (only for JSErrorReport)
     289             :      * Such layout together with the properties enforced by the following
     290             :      * asserts does not need any extra alignment padding.
     291             :      */
     292             :     JS_STATIC_ASSERT(sizeof(T) % sizeof(const char*) == 0);
     293             :     JS_STATIC_ASSERT(sizeof(const char*) % sizeof(char16_t) == 0);
     294             : 
     295           2 :     size_t filenameSize = report->filename ? strlen(report->filename) + 1 : 0;
     296           2 :     size_t messageSize = 0;
     297           2 :     if (report->message())
     298           2 :         messageSize = strlen(report->message().c_str()) + 1;
     299             : 
     300             :     /*
     301             :      * The mallocSize can not overflow since it represents the sum of the
     302             :      * sizes of already allocated objects.
     303             :      */
     304           2 :     size_t mallocSize = sizeof(T) + messageSize + filenameSize + ExtraMallocSize(report);
     305           2 :     uint8_t* cursor = cx->pod_calloc<uint8_t>(mallocSize);
     306           2 :     if (!cursor)
     307           0 :         return nullptr;
     308             : 
     309           2 :     T* copy = new (cursor) T();
     310           2 :     cursor += sizeof(T);
     311             : 
     312           2 :     if (report->message()) {
     313           2 :         copy->initBorrowedMessage((const char*)cursor);
     314           2 :         js_memcpy(cursor, report->message().c_str(), messageSize);
     315           2 :         cursor += messageSize;
     316             :     }
     317             : 
     318           2 :     if (report->filename) {
     319           2 :         copy->filename = (const char*)cursor;
     320           2 :         js_memcpy(cursor, report->filename, filenameSize);
     321           2 :         cursor += filenameSize;
     322             :     }
     323             : 
     324           2 :     if (!CopyExtraData(cx, &cursor, copy, report)) {
     325             :         /* js_delete calls destructor for T and js_free for pod_calloc. */
     326           0 :         js_delete(copy);
     327           0 :         return nullptr;
     328             :     }
     329             : 
     330           2 :     MOZ_ASSERT(cursor == (uint8_t*)copy + mallocSize);
     331             : 
     332             :     /* Copy non-pointer members. */
     333           2 :     copy->lineno = report->lineno;
     334           2 :     copy->column = report->column;
     335           2 :     copy->errorNumber = report->errorNumber;
     336             : 
     337           2 :     return copy;
     338             : }
     339             : 
     340             : JSErrorNotes::Note*
     341           0 : js::CopyErrorNote(JSContext* cx, JSErrorNotes::Note* note)
     342             : {
     343           0 :     return CopyErrorHelper(cx, note);
     344             : }
     345             : 
     346             : JSErrorReport*
     347           2 : js::CopyErrorReport(JSContext* cx, JSErrorReport* report)
     348             : {
     349           2 :     return CopyErrorHelper(cx, report);
     350             : }
     351             : 
     352             : struct SuppressErrorsGuard
     353             : {
     354             :     JSContext* cx;
     355             :     JS::WarningReporter prevReporter;
     356             :     JS::AutoSaveExceptionState prevState;
     357             : 
     358           0 :     explicit SuppressErrorsGuard(JSContext* cx)
     359           0 :       : cx(cx),
     360           0 :         prevReporter(JS::SetWarningReporter(cx, nullptr)),
     361           0 :         prevState(cx)
     362           0 :     {}
     363             : 
     364           0 :     ~SuppressErrorsGuard()
     365           0 :     {
     366           0 :         JS::SetWarningReporter(cx, prevReporter);
     367           0 :     }
     368             : };
     369             : 
     370             : // Cut off the stack if it gets too deep (most commonly for infinite recursion
     371             : // errors).
     372             : static const size_t MAX_REPORTED_STACK_DEPTH = 1u << 7;
     373             : 
     374             : static bool
     375           3 : CaptureStack(JSContext* cx, MutableHandleObject stack)
     376             : {
     377             :     return CaptureCurrentStack(cx, stack,
     378           3 :                                JS::StackCapture(JS::MaxFrames(MAX_REPORTED_STACK_DEPTH)));
     379             : }
     380             : 
     381             : JSString*
     382           0 : js::ComputeStackString(JSContext* cx)
     383             : {
     384           0 :     SuppressErrorsGuard seg(cx);
     385             : 
     386           0 :     RootedObject stack(cx);
     387           0 :     if (!CaptureStack(cx, &stack))
     388           0 :         return nullptr;
     389             : 
     390           0 :     RootedString str(cx);
     391           0 :     if (!BuildStackString(cx, stack, &str))
     392           0 :         return nullptr;
     393             : 
     394           0 :     return str.get();
     395             : }
     396             : 
     397             : static void
     398           0 : exn_finalize(FreeOp* fop, JSObject* obj)
     399             : {
     400           0 :     MOZ_ASSERT(fop->maybeOnHelperThread());
     401           0 :     if (JSErrorReport* report = obj->as<ErrorObject>().getErrorReport())
     402           0 :         fop->delete_(report);
     403           0 : }
     404             : 
     405             : JSErrorReport*
     406           0 : js::ErrorFromException(JSContext* cx, HandleObject objArg)
     407             : {
     408             :     // It's ok to UncheckedUnwrap here, since all we do is get the
     409             :     // JSErrorReport, and consumers are careful with the information they get
     410             :     // from that anyway.  Anyone doing things that would expose anything in the
     411             :     // JSErrorReport to page script either does a security check on the
     412             :     // JSErrorReport's principal or also tries to do toString on our object and
     413             :     // will fail if they can't unwrap it.
     414           0 :     RootedObject obj(cx, UncheckedUnwrap(objArg));
     415           0 :     if (!obj->is<ErrorObject>())
     416           0 :         return nullptr;
     417             : 
     418           0 :     JSErrorReport* report = obj->as<ErrorObject>().getOrCreateErrorReport(cx);
     419           0 :     if (!report) {
     420           0 :         MOZ_ASSERT(cx->isThrowingOutOfMemory());
     421           0 :         cx->recoverFromOutOfMemory();
     422             :     }
     423             : 
     424           0 :     return report;
     425             : }
     426             : 
     427             : JS_PUBLIC_API(JSObject*)
     428           0 : ExceptionStackOrNull(HandleObject objArg)
     429             : {
     430           0 :     JSObject* obj = CheckedUnwrap(objArg);
     431           0 :     if (!obj || !obj->is<ErrorObject>()) {
     432           0 :       return nullptr;
     433             :     }
     434             : 
     435           0 :     return obj->as<ErrorObject>().stack();
     436             : }
     437             : 
     438             : bool
     439           1 : Error(JSContext* cx, unsigned argc, Value* vp)
     440             : {
     441           1 :     CallArgs args = CallArgsFromVp(argc, vp);
     442             : 
     443             :     // ES6 19.5.1.1 mandates the .prototype lookup happens before the toString
     444           2 :     RootedObject proto(cx);
     445           1 :     if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
     446           0 :         return false;
     447             : 
     448             :     /* Compute the error message, if any. */
     449           2 :     RootedString message(cx, nullptr);
     450           1 :     if (args.hasDefined(0)) {
     451           0 :         message = ToString<CanGC>(cx, args[0]);
     452           0 :         if (!message)
     453           0 :             return false;
     454             :     }
     455             : 
     456             :     /* Find the scripted caller, but only ones we're allowed to know about. */
     457           2 :     NonBuiltinFrameIter iter(cx, cx->compartment()->principals());
     458             : 
     459             :     /* Set the 'fileName' property. */
     460           2 :     RootedString fileName(cx);
     461           1 :     if (args.length() > 1) {
     462           0 :         fileName = ToString<CanGC>(cx, args[1]);
     463             :     } else {
     464           1 :         fileName = cx->runtime()->emptyString;
     465           1 :         if (!iter.done()) {
     466           1 :             if (const char* cfilename = iter.filename())
     467           1 :                 fileName = JS_NewStringCopyZ(cx, cfilename);
     468             :         }
     469             :     }
     470           1 :     if (!fileName)
     471           0 :         return false;
     472             : 
     473             :     /* Set the 'lineNumber' property. */
     474           1 :     uint32_t lineNumber, columnNumber = 0;
     475           1 :     if (args.length() > 2) {
     476           0 :         if (!ToUint32(cx, args[2], &lineNumber))
     477           0 :             return false;
     478             :     } else {
     479           1 :         lineNumber = iter.done() ? 0 : iter.computeLine(&columnNumber);
     480             :         // XXX: Make the column 1-based as in other browsers, instead of 0-based
     481             :         // which is how SpiderMonkey stores it internally. This will be
     482             :         // unnecessary once bug 1144340 is fixed.
     483           1 :         ++columnNumber;
     484             :     }
     485             : 
     486           2 :     RootedObject stack(cx);
     487           1 :     if (!CaptureStack(cx, &stack))
     488           0 :         return false;
     489             : 
     490             :     /*
     491             :      * ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when
     492             :      * called as functions, without operator new.  But as we do not give
     493             :      * each constructor a distinct JSClass, we must get the exception type
     494             :      * ourselves.
     495             :      */
     496           1 :     JSExnType exnType = JSExnType(args.callee().as<JSFunction>().getExtendedSlot(0).toInt32());
     497             : 
     498           2 :     RootedObject obj(cx, ErrorObject::create(cx, exnType, stack, fileName,
     499           2 :                                              lineNumber, columnNumber, nullptr, message, proto));
     500           1 :     if (!obj)
     501           0 :         return false;
     502             : 
     503           1 :     args.rval().setObject(*obj);
     504           1 :     return true;
     505             : }
     506             : 
     507             : #if JS_HAS_TOSOURCE
     508             : /*
     509             :  * Return a string that may eval to something similar to the original object.
     510             :  */
     511             : static bool
     512           0 : exn_toSource(JSContext* cx, unsigned argc, Value* vp)
     513             : {
     514           0 :     if (!CheckRecursionLimit(cx))
     515           0 :         return false;
     516           0 :     CallArgs args = CallArgsFromVp(argc, vp);
     517             : 
     518           0 :     RootedObject obj(cx, ToObject(cx, args.thisv()));
     519           0 :     if (!obj)
     520           0 :         return false;
     521             : 
     522           0 :     RootedValue nameVal(cx);
     523           0 :     RootedString name(cx);
     524           0 :     if (!GetProperty(cx, obj, obj, cx->names().name, &nameVal) ||
     525           0 :         !(name = ToString<CanGC>(cx, nameVal)))
     526             :     {
     527           0 :         return false;
     528             :     }
     529             : 
     530           0 :     RootedValue messageVal(cx);
     531           0 :     RootedString message(cx);
     532           0 :     if (!GetProperty(cx, obj, obj, cx->names().message, &messageVal) ||
     533           0 :         !(message = ValueToSource(cx, messageVal)))
     534             :     {
     535           0 :         return false;
     536             :     }
     537             : 
     538           0 :     RootedValue filenameVal(cx);
     539           0 :     RootedString filename(cx);
     540           0 :     if (!GetProperty(cx, obj, obj, cx->names().fileName, &filenameVal) ||
     541           0 :         !(filename = ValueToSource(cx, filenameVal)))
     542             :     {
     543           0 :         return false;
     544             :     }
     545             : 
     546           0 :     RootedValue linenoVal(cx);
     547             :     uint32_t lineno;
     548           0 :     if (!GetProperty(cx, obj, obj, cx->names().lineNumber, &linenoVal) ||
     549           0 :         !ToUint32(cx, linenoVal, &lineno))
     550             :     {
     551           0 :         return false;
     552             :     }
     553             : 
     554           0 :     StringBuffer sb(cx);
     555           0 :     if (!sb.append("(new ") || !sb.append(name) || !sb.append("("))
     556           0 :         return false;
     557             : 
     558           0 :     if (!sb.append(message))
     559           0 :         return false;
     560             : 
     561           0 :     if (!filename->empty()) {
     562           0 :         if (!sb.append(", ") || !sb.append(filename))
     563           0 :             return false;
     564             :     }
     565           0 :     if (lineno != 0) {
     566             :         /* We have a line, but no filename, add empty string */
     567           0 :         if (filename->empty() && !sb.append(", \"\""))
     568           0 :                 return false;
     569             : 
     570           0 :         JSString* linenumber = ToString<CanGC>(cx, linenoVal);
     571           0 :         if (!linenumber)
     572           0 :             return false;
     573           0 :         if (!sb.append(", ") || !sb.append(linenumber))
     574           0 :             return false;
     575             :     }
     576             : 
     577           0 :     if (!sb.append("))"))
     578           0 :         return false;
     579             : 
     580           0 :     JSString* str = sb.finishString();
     581           0 :     if (!str)
     582           0 :         return false;
     583           0 :     args.rval().setString(str);
     584           0 :     return true;
     585             : }
     586             : #endif
     587             : 
     588             : /* static */ JSObject*
     589          83 : ErrorObject::createProto(JSContext* cx, JSProtoKey key)
     590             : {
     591          83 :     JSExnType type = ExnTypeFromProtoKey(key);
     592             : 
     593          83 :     if (type == JSEXN_ERR) {
     594          15 :         return GlobalObject::createBlankPrototype(cx, cx->global(),
     595          15 :                                                   &ErrorObject::protoClasses[JSEXN_ERR]);
     596             :     }
     597             : 
     598         136 :     RootedObject protoProto(cx, GlobalObject::getOrCreateErrorPrototype(cx, cx->global()));
     599          68 :     if (!protoProto)
     600           0 :         return nullptr;
     601             : 
     602         204 :     return GlobalObject::createBlankPrototypeInheriting(cx, cx->global(),
     603          68 :                                                         &ErrorObject::protoClasses[type],
     604          68 :                                                         protoProto);
     605             : }
     606             : 
     607             : /* static */ JSObject*
     608          83 : ErrorObject::createConstructor(JSContext* cx, JSProtoKey key)
     609             : {
     610          83 :     JSExnType type = ExnTypeFromProtoKey(key);
     611         166 :     RootedObject ctor(cx);
     612             : 
     613          83 :     if (type == JSEXN_ERR) {
     614          15 :         ctor = GenericCreateConstructor<Error, 1, gc::AllocKind::FUNCTION_EXTENDED>(cx, key);
     615             :     } else {
     616         136 :         RootedFunction proto(cx, GlobalObject::getOrCreateErrorConstructor(cx, cx->global()));
     617          68 :         if (!proto)
     618           0 :             return nullptr;
     619             : 
     620         136 :         ctor = NewFunctionWithProto(cx, Error, 1, JSFunction::NATIVE_CTOR, nullptr,
     621             :                                     ClassName(key, cx), proto, gc::AllocKind::FUNCTION_EXTENDED,
     622          68 :                                     SingletonObject);
     623             :     }
     624             : 
     625          83 :     if (!ctor)
     626           0 :         return nullptr;
     627             : 
     628          83 :     ctor->as<JSFunction>().setExtendedSlot(0, Int32Value(type));
     629          83 :     return ctor;
     630             : }
     631             : 
     632             : JS_FRIEND_API(JSFlatString*)
     633           0 : js::GetErrorTypeName(JSContext* cx, int16_t exnType)
     634             : {
     635             :     /*
     636             :      * JSEXN_INTERNALERR returns null to prevent that "InternalError: "
     637             :      * is prepended before "uncaught exception: "
     638             :      */
     639           0 :     if (exnType < 0 || exnType >= JSEXN_LIMIT ||
     640           0 :         exnType == JSEXN_INTERNALERR || exnType == JSEXN_WARN || exnType == JSEXN_NOTE)
     641             :     {
     642           0 :         return nullptr;
     643             :     }
     644           0 :     JSProtoKey key = GetExceptionProtoKey(JSExnType(exnType));
     645           0 :     return ClassName(key, cx);
     646             : }
     647             : 
     648             : void
     649           2 : js::ErrorToException(JSContext* cx, JSErrorReport* reportp,
     650             :                      JSErrorCallback callback, void* userRef)
     651             : {
     652           2 :     MOZ_ASSERT(reportp);
     653           2 :     MOZ_ASSERT(!JSREPORT_IS_WARNING(reportp->flags));
     654             : 
     655             :     // We cannot throw a proper object inside the self-hosting compartment, as
     656             :     // we cannot construct the Error constructor without self-hosted code. Just
     657             :     // print the error to stderr to help debugging.
     658           2 :     if (cx->runtime()->isSelfHostingCompartment(cx->compartment())) {
     659           0 :         PrintError(cx, stderr, JS::ConstUTF8CharsZ(), reportp, true);
     660           0 :         return;
     661             :     }
     662             : 
     663             :     // Find the exception index associated with this error.
     664           2 :     JSErrNum errorNumber = static_cast<JSErrNum>(reportp->errorNumber);
     665           2 :     if (!callback)
     666           0 :         callback = GetErrorMessage;
     667           2 :     const JSErrorFormatString* errorString = callback(userRef, errorNumber);
     668           2 :     JSExnType exnType = errorString ? static_cast<JSExnType>(errorString->exnType) : JSEXN_ERR;
     669           2 :     MOZ_ASSERT(exnType < JSEXN_LIMIT);
     670           2 :     MOZ_ASSERT(exnType != JSEXN_NOTE);
     671             : 
     672           2 :     if (exnType == JSEXN_WARN) {
     673             :         // werror must be enabled, so we use JSEXN_ERR.
     674           0 :         MOZ_ASSERT(cx->options().werror());
     675           0 :         exnType = JSEXN_ERR;
     676             :     }
     677             : 
     678             :     // Prevent infinite recursion.
     679           2 :     if (cx->generatingError)
     680           0 :         return;
     681           4 :     AutoScopedAssign<bool> asa(&cx->generatingError.ref(), true);
     682             : 
     683             :     // Create an exception object.
     684           4 :     RootedString messageStr(cx, reportp->newMessageString(cx));
     685           2 :     if (!messageStr)
     686           0 :         return;
     687             : 
     688           4 :     RootedString fileName(cx, JS_NewStringCopyZ(cx, reportp->filename));
     689           2 :     if (!fileName)
     690           0 :         return;
     691             : 
     692           2 :     uint32_t lineNumber = reportp->lineno;
     693           2 :     uint32_t columnNumber = reportp->column;
     694             : 
     695           4 :     RootedObject stack(cx);
     696           2 :     if (!CaptureStack(cx, &stack))
     697           0 :         return;
     698             : 
     699           4 :     js::ScopedJSFreePtr<JSErrorReport> report(CopyErrorReport(cx, reportp));
     700           2 :     if (!report)
     701           0 :         return;
     702             : 
     703           4 :     RootedObject errObject(cx, ErrorObject::create(cx, exnType, stack, fileName,
     704           4 :                                                    lineNumber, columnNumber, &report, messageStr));
     705           2 :     if (!errObject)
     706           0 :         return;
     707             : 
     708             :     // Throw it.
     709           2 :     cx->setPendingException(ObjectValue(*errObject));
     710             : 
     711             :     // Flag the error report passed in to indicate an exception was raised.
     712           2 :     reportp->flags |= JSREPORT_EXCEPTION;
     713             : }
     714             : 
     715             : static bool
     716           0 : IsDuckTypedErrorObject(JSContext* cx, HandleObject exnObject, const char** filename_strp)
     717             : {
     718             :     /*
     719             :      * This function is called from ErrorReport::init and so should not generate
     720             :      * any new exceptions.
     721             :      */
     722           0 :     AutoClearPendingException acpe(cx);
     723             : 
     724             :     bool found;
     725           0 :     if (!JS_HasProperty(cx, exnObject, js_message_str, &found) || !found)
     726           0 :         return false;
     727             : 
     728           0 :     const char* filename_str = *filename_strp;
     729           0 :     if (!JS_HasProperty(cx, exnObject, filename_str, &found) || !found) {
     730             :         /* Now try "fileName", in case this quacks like an Error */
     731           0 :         filename_str = js_fileName_str;
     732           0 :         if (!JS_HasProperty(cx, exnObject, filename_str, &found) || !found)
     733           0 :             return false;
     734             :     }
     735             : 
     736           0 :     if (!JS_HasProperty(cx, exnObject, js_lineNumber_str, &found) || !found)
     737           0 :         return false;
     738             : 
     739           0 :     *filename_strp = filename_str;
     740           0 :     return true;
     741             : }
     742             : 
     743             : static JSString*
     744           0 : ErrorReportToString(JSContext* cx, JSErrorReport* reportp)
     745             : {
     746             :     /*
     747             :      * We do NOT want to use GetErrorTypeName() here because it will not do the
     748             :      * "right thing" for JSEXN_INTERNALERR.  That is, the caller of this API
     749             :      * expects that "InternalError: " will be prepended but GetErrorTypeName
     750             :      * goes out of its way to avoid this.
     751             :      */
     752           0 :     JSExnType type = static_cast<JSExnType>(reportp->exnType);
     753           0 :     RootedString str(cx);
     754           0 :     if (type != JSEXN_WARN && type != JSEXN_NOTE)
     755           0 :         str = ClassName(GetExceptionProtoKey(type), cx);
     756             : 
     757             :     /*
     758             :      * If "str" is null at this point, that means we just want to use
     759             :      * message without prefixing it with anything.
     760             :      */
     761           0 :     if (str) {
     762           0 :         RootedString separator(cx, JS_NewUCStringCopyN(cx, u": ", 2));
     763           0 :         if (!separator)
     764           0 :             return nullptr;
     765           0 :         str = ConcatStrings<CanGC>(cx, str, separator);
     766           0 :         if (!str)
     767           0 :             return nullptr;
     768             :     }
     769             : 
     770           0 :     RootedString message(cx, reportp->newMessageString(cx));
     771           0 :     if (!message)
     772           0 :         return nullptr;
     773             : 
     774           0 :     if (!str)
     775           0 :         return message;
     776             : 
     777           0 :     return ConcatStrings<CanGC>(cx, str, message);
     778             : }
     779             : 
     780           0 : ErrorReport::ErrorReport(JSContext* cx)
     781             :   : reportp(nullptr),
     782             :     str(cx),
     783             :     strChars(cx),
     784           0 :     exnObject(cx)
     785             : {
     786           0 : }
     787             : 
     788           0 : ErrorReport::~ErrorReport()
     789             : {
     790           0 : }
     791             : 
     792             : void
     793           0 : ErrorReport::ReportAddonExceptionToTelemetry(JSContext* cx)
     794             : {
     795           0 :     MOZ_ASSERT(exnObject);
     796           0 :     RootedObject unwrapped(cx, UncheckedUnwrap(exnObject));
     797           0 :     MOZ_ASSERT(unwrapped, "UncheckedUnwrap failed?");
     798             : 
     799             :     // There is not much we can report if the exception is not an ErrorObject, let's ignore those.
     800           0 :     if (!unwrapped->is<ErrorObject>())
     801           0 :         return;
     802             : 
     803           0 :     Rooted<ErrorObject*> errObj(cx, &unwrapped->as<ErrorObject>());
     804           0 :     RootedObject stack(cx, errObj->stack());
     805             : 
     806             :     // Let's ignore TOP level exceptions. For regular add-ons those will not be reported anyway,
     807             :     // for SDK based once it should not be a valid case either.
     808             :     // At this point the frame stack is unwound but the exception object stored the stack so let's
     809             :     // use that for getting the function name.
     810           0 :     if (!stack)
     811           0 :         return;
     812             : 
     813           0 :     JSCompartment* comp = stack->compartment();
     814           0 :     JSAddonId* addonId = comp->creationOptions().addonIdOrNull();
     815             : 
     816             :     // We only want to send the report if the scope that just have thrown belongs to an add-on.
     817             :     // Let's check the compartment of the youngest function on the stack, to determine that.
     818           0 :     if (!addonId)
     819           0 :         return;
     820             : 
     821           0 :     RootedString funnameString(cx);
     822           0 :     JS::SavedFrameResult result = GetSavedFrameFunctionDisplayName(cx, stack, &funnameString);
     823             :     // AccessDenied should never be the case here for add-ons but let's not risk it.
     824           0 :     JSAutoByteString bytes;
     825           0 :     const char* funname = nullptr;
     826           0 :     bool denied = result == JS::SavedFrameResult::AccessDenied;
     827           0 :     funname = denied ? "unknown"
     828           0 :                      : funnameString ? AtomToPrintableString(cx,
     829           0 :                                                              &funnameString->asAtom(),
     830             :                                                              &bytes)
     831           0 :                                      : "anonymous";
     832             : 
     833           0 :     UniqueChars addonIdChars(JS_EncodeString(cx, addonId));
     834             : 
     835           0 :     const char* filename = nullptr;
     836           0 :     if (reportp && reportp->filename) {
     837           0 :         filename = strrchr(reportp->filename, '/');
     838           0 :         if (filename)
     839           0 :             filename++;
     840             :     }
     841           0 :     if (!filename) {
     842           0 :         filename = "FILE_NOT_FOUND";
     843             :     }
     844             :     char histogramKey[64];
     845           0 :     SprintfLiteral(histogramKey, "%s %s %s %u",
     846             :                    addonIdChars.get(),
     847             :                    funname,
     848             :                    filename,
     849           0 :                    (reportp ? reportp->lineno : 0) );
     850           0 :     cx->runtime()->addTelemetry(JS_TELEMETRY_ADDON_EXCEPTIONS, 1, histogramKey);
     851             : }
     852             : 
     853             : bool
     854           0 : ErrorReport::init(JSContext* cx, HandleValue exn,
     855             :                   SniffingBehavior sniffingBehavior)
     856             : {
     857           0 :     MOZ_ASSERT(!cx->isExceptionPending());
     858           0 :     MOZ_ASSERT(!reportp);
     859             : 
     860           0 :     if (exn.isObject()) {
     861             :         // Because ToString below could error and an exception object could become
     862             :         // unrooted, we must root our exception object, if any.
     863           0 :         exnObject = &exn.toObject();
     864           0 :         reportp = ErrorFromException(cx, exnObject);
     865             : 
     866           0 :         if (!reportp && sniffingBehavior == NoSideEffects) {
     867             :             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
     868           0 :                                       JSMSG_ERR_DURING_THROW);
     869           0 :             return false;
     870             :         }
     871             : 
     872             :         // Let's see if the exception is from add-on code, if so, it should be reported
     873             :         // to telemetry.
     874           0 :         ReportAddonExceptionToTelemetry(cx);
     875             :     }
     876             : 
     877             : 
     878             :     // Be careful not to invoke ToString if we've already successfully extracted
     879             :     // an error report, since the exception might be wrapped in a security
     880             :     // wrapper, and ToString-ing it might throw.
     881           0 :     if (reportp) {
     882           0 :         str = ErrorReportToString(cx, reportp);
     883           0 :     } else if (exn.isSymbol()) {
     884           0 :         RootedValue strVal(cx);
     885           0 :         if (js::SymbolDescriptiveString(cx, exn.toSymbol(), &strVal))
     886           0 :             str = strVal.toString();
     887             :         else
     888           0 :             str = nullptr;
     889             :     } else {
     890           0 :         str = ToString<CanGC>(cx, exn);
     891             :     }
     892             : 
     893           0 :     if (!str)
     894           0 :         cx->clearPendingException();
     895             : 
     896             :     // If ErrorFromException didn't get us a JSErrorReport, then the object
     897             :     // was not an ErrorObject, security-wrapped or otherwise. However, it might
     898             :     // still quack like one. Give duck-typing a chance.  We start by looking for
     899             :     // "filename" (all lowercase), since that's where DOMExceptions store their
     900             :     // filename.  Then we check "fileName", which is where Errors store it.  We
     901             :     // have to do it in that order, because DOMExceptions have Error.prototype
     902             :     // on their proto chain, and hence also have a "fileName" property, but its
     903             :     // value is "".
     904           0 :     const char* filename_str = "filename";
     905           0 :     if (!reportp && exnObject && IsDuckTypedErrorObject(cx, exnObject, &filename_str))
     906             :     {
     907             :         // Temporary value for pulling properties off of duck-typed objects.
     908           0 :         RootedValue val(cx);
     909             : 
     910           0 :         RootedString name(cx);
     911           0 :         if (JS_GetProperty(cx, exnObject, js_name_str, &val) && val.isString())
     912           0 :             name = val.toString();
     913             :         else
     914           0 :             cx->clearPendingException();
     915             : 
     916           0 :         RootedString msg(cx);
     917           0 :         if (JS_GetProperty(cx, exnObject, js_message_str, &val) && val.isString())
     918           0 :             msg = val.toString();
     919             :         else
     920           0 :             cx->clearPendingException();
     921             : 
     922             :         // If we have the right fields, override the ToString we performed on
     923             :         // the exception object above with something built out of its quacks
     924             :         // (i.e. as much of |NameQuack: MessageQuack| as we can make).
     925             :         //
     926             :         // It would be nice to use ErrorReportToString here, but we can't quite
     927             :         // do it - mostly because we'd need to figure out what JSExnType |name|
     928             :         // corresponds to, which may not be any JSExnType at all.
     929           0 :         if (name && msg) {
     930           0 :             RootedString colon(cx, JS_NewStringCopyZ(cx, ": "));
     931           0 :             if (!colon)
     932           0 :                 return false;
     933           0 :             RootedString nameColon(cx, ConcatStrings<CanGC>(cx, name, colon));
     934           0 :             if (!nameColon)
     935           0 :                 return false;
     936           0 :             str = ConcatStrings<CanGC>(cx, nameColon, msg);
     937           0 :             if (!str)
     938           0 :                 return false;
     939           0 :         } else if (name) {
     940           0 :             str = name;
     941           0 :         } else if (msg) {
     942           0 :             str = msg;
     943             :         }
     944             : 
     945           0 :         if (JS_GetProperty(cx, exnObject, filename_str, &val)) {
     946           0 :             RootedString tmp(cx, ToString<CanGC>(cx, val));
     947           0 :             if (tmp)
     948           0 :                 filename.encodeUtf8(cx, tmp);
     949             :             else
     950           0 :                 cx->clearPendingException();
     951             :         } else {
     952           0 :             cx->clearPendingException();
     953             :         }
     954             : 
     955             :         uint32_t lineno;
     956           0 :         if (!JS_GetProperty(cx, exnObject, js_lineNumber_str, &val) ||
     957           0 :             !ToUint32(cx, val, &lineno))
     958             :         {
     959           0 :             cx->clearPendingException();
     960           0 :             lineno = 0;
     961             :         }
     962             : 
     963             :         uint32_t column;
     964           0 :         if (!JS_GetProperty(cx, exnObject, js_columnNumber_str, &val) ||
     965           0 :             !ToUint32(cx, val, &column))
     966             :         {
     967           0 :             cx->clearPendingException();
     968           0 :             column = 0;
     969             :         }
     970             : 
     971           0 :         reportp = &ownedReport;
     972           0 :         new (reportp) JSErrorReport();
     973           0 :         ownedReport.filename = filename.ptr();
     974           0 :         ownedReport.lineno = lineno;
     975           0 :         ownedReport.exnType = JSEXN_INTERNALERR;
     976           0 :         ownedReport.column = column;
     977           0 :         if (str) {
     978             :             // Note that using |str| for |message_| here is kind of wrong,
     979             :             // because |str| is supposed to be of the format
     980             :             // |ErrorName: ErrorMessage|, and |message_| is supposed to
     981             :             // correspond to |ErrorMessage|. But this is what we've
     982             :             // historically done for duck-typed error objects.
     983             :             //
     984             :             // If only this stuff could get specced one day...
     985             :             char* utf8;
     986           0 :             if (str->ensureFlat(cx) &&
     987           0 :                 strChars.initTwoByte(cx, str) &&
     988           0 :                 (utf8 = JS::CharsToNewUTF8CharsZ(cx, strChars.twoByteRange()).c_str()))
     989             :             {
     990           0 :                 ownedReport.initOwnedMessage(utf8);
     991             :             } else {
     992           0 :                 cx->clearPendingException();
     993           0 :                 str = nullptr;
     994             :             }
     995             :         }
     996             :     }
     997             : 
     998           0 :     const char* utf8Message = nullptr;
     999           0 :     if (str)
    1000           0 :         utf8Message = toStringResultBytesStorage.encodeUtf8(cx, str);
    1001           0 :     if (!utf8Message)
    1002           0 :         utf8Message = "unknown (can't convert to string)";
    1003             : 
    1004           0 :     if (!reportp) {
    1005             :         // This is basically an inlined version of
    1006             :         //
    1007             :         //   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    1008             :         //                            JSMSG_UNCAUGHT_EXCEPTION, utf8Message);
    1009             :         //
    1010             :         // but without the reporting bits.  Instead it just puts all
    1011             :         // the stuff we care about in our ownedReport and message_.
    1012           0 :         if (!populateUncaughtExceptionReportUTF8(cx, utf8Message)) {
    1013             :             // Just give up.  We're out of memory or something; not much we can
    1014             :             // do here.
    1015           0 :             return false;
    1016             :         }
    1017             :     } else {
    1018           0 :         toStringResult_ = JS::ConstUTF8CharsZ(utf8Message, strlen(utf8Message));
    1019             :         /* Flag the error as an exception. */
    1020           0 :         reportp->flags |= JSREPORT_EXCEPTION;
    1021             :     }
    1022             : 
    1023           0 :     return true;
    1024             : }
    1025             : 
    1026             : bool
    1027           0 : ErrorReport::populateUncaughtExceptionReportUTF8(JSContext* cx, ...)
    1028             : {
    1029             :     va_list ap;
    1030           0 :     va_start(ap, cx);
    1031           0 :     bool ok = populateUncaughtExceptionReportUTF8VA(cx, ap);
    1032           0 :     va_end(ap);
    1033           0 :     return ok;
    1034             : }
    1035             : 
    1036             : bool
    1037           0 : ErrorReport::populateUncaughtExceptionReportUTF8VA(JSContext* cx, va_list ap)
    1038             : {
    1039           0 :     new (&ownedReport) JSErrorReport();
    1040           0 :     ownedReport.flags = JSREPORT_ERROR;
    1041           0 :     ownedReport.errorNumber = JSMSG_UNCAUGHT_EXCEPTION;
    1042             :     // XXXbz this assumes the stack we have right now is still
    1043             :     // related to our exception object.  It would be better if we
    1044             :     // could accept a passed-in stack of some sort instead.
    1045           0 :     NonBuiltinFrameIter iter(cx, cx->compartment()->principals());
    1046           0 :     if (!iter.done()) {
    1047           0 :         ownedReport.filename = iter.filename();
    1048           0 :         ownedReport.lineno = iter.computeLine(&ownedReport.column);
    1049             :         // XXX: Make the column 1-based as in other browsers, instead of 0-based
    1050             :         // which is how SpiderMonkey stores it internally. This will be
    1051             :         // unnecessary once bug 1144340 is fixed.
    1052           0 :         ++ownedReport.column;
    1053           0 :         ownedReport.isMuted = iter.mutedErrors();
    1054             :     }
    1055             : 
    1056           0 :     if (!ExpandErrorArgumentsVA(cx, GetErrorMessage, nullptr,
    1057             :                                 JSMSG_UNCAUGHT_EXCEPTION,
    1058             :                                 nullptr, ArgumentsAreUTF8, &ownedReport, ap)) {
    1059           0 :         return false;
    1060             :     }
    1061             : 
    1062           0 :     toStringResult_ = ownedReport.message();
    1063           0 :     reportp = &ownedReport;
    1064           0 :     return true;
    1065             : }
    1066             : 
    1067             : JSObject*
    1068           0 : js::CopyErrorObject(JSContext* cx, Handle<ErrorObject*> err)
    1069             : {
    1070           0 :     js::ScopedJSFreePtr<JSErrorReport> copyReport;
    1071           0 :     if (JSErrorReport* errorReport = err->getErrorReport()) {
    1072           0 :         copyReport = CopyErrorReport(cx, errorReport);
    1073           0 :         if (!copyReport)
    1074           0 :             return nullptr;
    1075             :     }
    1076             : 
    1077           0 :     RootedString message(cx, err->getMessage());
    1078           0 :     if (message && !cx->compartment()->wrap(cx, &message))
    1079           0 :         return nullptr;
    1080           0 :     RootedString fileName(cx, err->fileName(cx));
    1081           0 :     if (!cx->compartment()->wrap(cx, &fileName))
    1082           0 :         return nullptr;
    1083           0 :     RootedObject stack(cx, err->stack());
    1084           0 :     if (!cx->compartment()->wrap(cx, &stack))
    1085           0 :         return nullptr;
    1086           0 :     uint32_t lineNumber = err->lineNumber();
    1087           0 :     uint32_t columnNumber = err->columnNumber();
    1088           0 :     JSExnType errorType = err->type();
    1089             : 
    1090             :     // Create the Error object.
    1091           0 :     return ErrorObject::create(cx, errorType, stack, fileName,
    1092           0 :                                lineNumber, columnNumber, &copyReport, message);
    1093             : }
    1094             : 
    1095             : JS_PUBLIC_API(bool)
    1096           0 : JS::CreateError(JSContext* cx, JSExnType type, HandleObject stack, HandleString fileName,
    1097             :                     uint32_t lineNumber, uint32_t columnNumber, JSErrorReport* report,
    1098             :                     HandleString message, MutableHandleValue rval)
    1099             : {
    1100           0 :     assertSameCompartment(cx, stack, fileName, message);
    1101           0 :     AssertObjectIsSavedFrameOrWrapper(cx, stack);
    1102             : 
    1103           0 :     js::ScopedJSFreePtr<JSErrorReport> rep;
    1104           0 :     if (report)
    1105           0 :         rep = CopyErrorReport(cx, report);
    1106             : 
    1107             :     RootedObject obj(cx,
    1108           0 :         js::ErrorObject::create(cx, type, stack, fileName,
    1109           0 :                                 lineNumber, columnNumber, &rep, message));
    1110           0 :     if (!obj)
    1111           0 :         return false;
    1112             : 
    1113           0 :     rval.setObject(*obj);
    1114           0 :     return true;
    1115             : }
    1116             : 
    1117             : const char*
    1118           0 : js::ValueToSourceForError(JSContext* cx, HandleValue val, JSAutoByteString& bytes)
    1119             : {
    1120           0 :     if (val.isUndefined())
    1121           0 :         return "undefined";
    1122             : 
    1123           0 :     if (val.isNull())
    1124           0 :         return "null";
    1125             : 
    1126           0 :     AutoClearPendingException acpe(cx);
    1127             : 
    1128           0 :     RootedString str(cx, JS_ValueToSource(cx, val));
    1129           0 :     if (!str)
    1130           0 :         return "<<error converting value to string>>";
    1131             : 
    1132           0 :     StringBuffer sb(cx);
    1133           0 :     if (val.isObject()) {
    1134           0 :         RootedObject valObj(cx, val.toObjectOrNull());
    1135             :         ESClass cls;
    1136           0 :         if (!GetBuiltinClass(cx, valObj, &cls))
    1137           0 :             return "<<error determining class of value>>";
    1138             :         const char* s;
    1139           0 :         if (cls == ESClass::Array)
    1140           0 :             s = "the array ";
    1141           0 :         else if (cls == ESClass::ArrayBuffer)
    1142           0 :             s = "the array buffer ";
    1143           0 :         else if (JS_IsArrayBufferViewObject(valObj))
    1144           0 :             s = "the typed array ";
    1145             :         else
    1146           0 :             s = "the object ";
    1147           0 :         if (!sb.append(s, strlen(s)))
    1148           0 :             return "<<error converting value to string>>";
    1149           0 :     } else if (val.isNumber()) {
    1150           0 :         if (!sb.append("the number "))
    1151           0 :             return "<<error converting value to string>>";
    1152           0 :     } else if (val.isString()) {
    1153           0 :         if (!sb.append("the string "))
    1154           0 :             return "<<error converting value to string>>";
    1155             :     } else {
    1156           0 :         MOZ_ASSERT(val.isBoolean() || val.isSymbol());
    1157           0 :         return bytes.encodeLatin1(cx, str);
    1158             :     }
    1159           0 :     if (!sb.append(str))
    1160           0 :         return "<<error converting value to string>>";
    1161           0 :     str = sb.finishString();
    1162           0 :     if (!str)
    1163           0 :         return "<<error converting value to string>>";
    1164           0 :     return bytes.encodeLatin1(cx, str);
    1165             : }
    1166             : 
    1167             : bool
    1168           0 : js::GetInternalError(JSContext* cx, unsigned errorNumber, MutableHandleValue error)
    1169             : {
    1170           0 :     FixedInvokeArgs<1> args(cx);
    1171           0 :     args[0].set(Int32Value(errorNumber));
    1172           0 :     return CallSelfHostedFunction(cx, "GetInternalError", NullHandleValue, args, error);
    1173             : }
    1174             : 
    1175             : bool
    1176           0 : js::GetTypeError(JSContext* cx, unsigned errorNumber, MutableHandleValue error)
    1177             : {
    1178           0 :     FixedInvokeArgs<1> args(cx);
    1179           0 :     args[0].set(Int32Value(errorNumber));
    1180           0 :     return CallSelfHostedFunction(cx, "GetTypeError", NullHandleValue, args, error);
    1181             : }

Generated by: LCOV version 1.13