LCOV - app.info - js/src/jsexn.cpp

LCOV - code coverage report
| Current view: | directory - js/src - jsexn.cpp (source / functions) |  | Found | Hit | Coverage |
| Test: | app.info | Lines: | 603 | 443 | 73.5 %
| Date: | 2012-04-07 | Functions: | 28 | 26 | 92.9 %

           1                 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
           2                 :  * vim: set ts=8 sw=4 et tw=78:
           3                 :  *
           4                 :  * ***** BEGIN LICENSE BLOCK *****
           5                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
           6                 :  *
           7                 :  * The contents of this file are subject to the Mozilla Public License Version
           8                 :  * 1.1 (the "License"); you may not use this file except in compliance with
           9                 :  * the License. You may obtain a copy of the License at
          10                 :  * http://www.mozilla.org/MPL/
          11                 :  *
          12                 :  * Software distributed under the License is distributed on an "AS IS" basis,
          13                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
          14                 :  * for the specific language governing rights and limitations under the
          15                 :  * License.
          16                 :  *
          17                 :  * The Original Code is Mozilla Communicator client code, released
          18                 :  * March 31, 1998.
          19                 :  *
          20                 :  * The Initial Developer of the Original Code is
          21                 :  * Netscape Communications Corporation.
          22                 :  * Portions created by the Initial Developer are Copyright (C) 1998
          23                 :  * the Initial Developer. All Rights Reserved.
          24                 :  *
          25                 :  * Contributor(s):
          26                 :  *
          27                 :  * Alternatively, the contents of this file may be used under the terms of
          28                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
          29                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
          30                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
          31                 :  * of those above. If you wish to allow use of your version of this file only
          32                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
          33                 :  * use your version of this file under the terms of the MPL, indicate your
          34                 :  * decision by deleting the provisions above and replace them with the notice
          35                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
          36                 :  * the provisions above, a recipient may use your version of this file under
          37                 :  * the terms of any one of the MPL, the GPL or the LGPL.
          38                 :  *
          39                 :  * ***** END LICENSE BLOCK ***** */
          40                 :
          41                 : /*
          42                 :  * JS standard exception implementation.
          43                 :  */
          44                 : #include <stdlib.h>
          45                 : #include <string.h>
          46                 :
          47                 : #include "mozilla/Util.h"
          48                 :
          49                 : #include "jstypes.h"
          50                 : #include "jsutil.h"
          51                 : #include "jsprf.h"
          52                 : #include "jsapi.h"
          53                 : #include "jscntxt.h"
          54                 : #include "jsversion.h"
          55                 : #include "jsexn.h"
          56                 : #include "jsfun.h"
          57                 : #include "jsgc.h"
          58                 : #include "jsgcmark.h"
          59                 : #include "jsinterp.h"
          60                 : #include "jsnum.h"
          61                 : #include "jsobj.h"
          62                 : #include "jsopcode.h"
          63                 : #include "jsscope.h"
          64                 : #include "jsscript.h"
          65                 : #include "jswrapper.h"
          66                 :
          67                 : #include "vm/GlobalObject.h"
          68                 : #include "vm/StringBuffer.h"
          69                 :
          70                 : #include "jsinferinlines.h"
          71                 : #include "jsobjinlines.h"
          72                 :
          73                 : #include "vm/Stack-inl.h"
          74                 : #include "vm/String-inl.h"
          75                 :
          76                 : using namespace mozilla;
          77                 : using namespace js;
          78                 : using namespace js::gc;
          79                 : using namespace js::types;
          80                 :
          81                 : /* Forward declarations for ErrorClass's initializer. */
          82                 : static JSBool
          83                 : Exception(JSContext *cx, unsigned argc, Value *vp);
          84                 :
          85                 : static void
          86                 : exn_trace(JSTracer *trc, JSObject *obj);
          87                 :
          88                 : static void
          89                 : exn_finalize(FreeOp *fop, JSObject *obj);
          90                 :
          91                 : static JSBool
          92                 : exn_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
          93                 :             JSObject **objp);
          94                 :
          95                 : Class js::ErrorClass = {
          96                 :     js_Error_str,
          97                 :     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_NEW_RESOLVE |
          98                 :     JSCLASS_HAS_CACHED_PROTO(JSProto_Error),
          99                 :     JS_PropertyStub,         /* addProperty */
         100                 :     JS_PropertyStub,         /* delProperty */
         101                 :     JS_PropertyStub,         /* getProperty */
         102                 :     JS_StrictPropertyStub,   /* setProperty */
         103                 :     JS_EnumerateStub,
         104                 :     (JSResolveOp)exn_resolve,
         105                 :     JS_ConvertStub,
         106                 :     exn_finalize,
         107                 :     NULL,                 /* checkAccess */
         108                 :     NULL,                 /* call        */
         109                 :     NULL,                 /* construct   */
         110                 :     NULL,                 /* hasInstance */
         111                 :     exn_trace
         112                 : };
         113                 :
         114                 : template <typename T>
         115        13192204 : struct JSStackTraceElemImpl {
         116                 :     T                   funName;
         117                 :     size_t              argc;
         118                 :     const char          *filename;
         119                 :     unsigned            ulineno;
         120                 : };
         121                 :
         122                 : typedef JSStackTraceElemImpl<HeapPtrString> JSStackTraceElem;
         123                 : typedef JSStackTraceElemImpl<JSString *>    JSStackTraceStackElem;
         124                 :
         125                 : typedef struct JSExnPrivate {
         126                 :     /* A copy of the JSErrorReport originally generated. */
         127                 :     JSErrorReport       *errorReport;
         128                 :     js::HeapPtrString   message;
         129                 :     js::HeapPtrString   filename;
         130                 :     unsigned            lineno;
         131                 :     size_t              stackDepth;
         132                 :     int                 exnType;
         133                 :     JSStackTraceElem    stackElems[1];
         134                 : } JSExnPrivate;
         135                 :
         136                 : static JSString *
         137                 : StackTraceToString(JSContext *cx, JSExnPrivate *priv);
         138                 :
         139                 : static JSErrorReport *
         140           10933 : CopyErrorReport(JSContext *cx, JSErrorReport *report)
         141                 : {
         142                 :     /*
         143                 :      * We use a single malloc block to make a deep copy of JSErrorReport with
         144                 :      * the following layout:
         145                 :      *   JSErrorReport
         146                 :      *   array of copies of report->messageArgs
         147                 :      *   jschar array with characters for all messageArgs
         148                 :      *   jschar array with characters for ucmessage
         149                 :      *   jschar array with characters for uclinebuf and uctokenptr
         150                 :      *   char array with characters for linebuf and tokenptr
         151                 :      *   char array with characters for filename
         152                 :      * Such layout together with the properties enforced by the following
         153                 :      * asserts does not need any extra alignment padding.
         154                 :      */
         155                 :     JS_STATIC_ASSERT(sizeof(JSErrorReport) % sizeof(const char *) == 0);
         156                 :     JS_STATIC_ASSERT(sizeof(const char *) % sizeof(jschar) == 0);
         157                 :
         158                 :     size_t filenameSize;
         159                 :     size_t linebufSize;
         160                 :     size_t uclinebufSize;
         161                 :     size_t ucmessageSize;
         162                 :     size_t i, argsArraySize, argsCopySize, argSize;
         163                 :     size_t mallocSize;
         164                 :     JSErrorReport *copy;
         165                 :     uint8_t *cursor;
         166                 :
         167                 : #define JS_CHARS_SIZE(jschars) ((js_strlen(jschars) + 1) * sizeof(jschar))
         168                 :
         169           10933 :     filenameSize = report->filename ? strlen(report->filename) + 1 : 0;
         170           10933 :     linebufSize = report->linebuf ? strlen(report->linebuf) + 1 : 0;
         171           10933 :     uclinebufSize = report->uclinebuf ? JS_CHARS_SIZE(report->uclinebuf) : 0;
         172           10933 :     ucmessageSize = 0;
         173           10933 :     argsArraySize = 0;
         174           10933 :     argsCopySize = 0;
         175           10933 :     if (report->ucmessage) {
         176           10933 :         ucmessageSize = JS_CHARS_SIZE(report->ucmessage);
         177           10933 :         if (report->messageArgs) {
         178           24759 :             for (i = 0; report->messageArgs[i]; ++i)
         179           15389 :                 argsCopySize += JS_CHARS_SIZE(report->messageArgs[i]);
         180                 :
         181                 :             /* Non-null messageArgs should have at least one non-null arg. */
         182            9370 :             JS_ASSERT(i != 0);
         183            9370 :             argsArraySize = (i + 1) * sizeof(const jschar *);
         184                 :         }
         185                 :     }
         186                 :
         187                 :     /*
         188                 :      * The mallocSize can not overflow since it represents the sum of the
         189                 :      * sizes of already allocated objects.
         190                 :      */
         191                 :     mallocSize = sizeof(JSErrorReport) + argsArraySize + argsCopySize +
         192           10933 :                  ucmessageSize + uclinebufSize + linebufSize + filenameSize;
         193           10933 :     cursor = (uint8_t *)cx->malloc_(mallocSize);
         194           10933 :     if (!cursor)
         195               0 :         return NULL;
         196                 :
         197           10933 :     copy = (JSErrorReport *)cursor;
         198           10933 :     memset(cursor, 0, sizeof(JSErrorReport));
         199           10933 :     cursor += sizeof(JSErrorReport);
         200                 :
         201           10933 :     if (argsArraySize != 0) {
         202            9370 :         copy->messageArgs = (const jschar **)cursor;
         203            9370 :         cursor += argsArraySize;
         204           24759 :         for (i = 0; report->messageArgs[i]; ++i) {
         205           15389 :             copy->messageArgs[i] = (const jschar *)cursor;
         206           15389 :             argSize = JS_CHARS_SIZE(report->messageArgs[i]);
         207           15389 :             js_memcpy(cursor, report->messageArgs[i], argSize);
         208           15389 :             cursor += argSize;
         209                 :         }
         210            9370 :         copy->messageArgs[i] = NULL;
         211            9370 :         JS_ASSERT(cursor == (uint8_t *)copy->messageArgs[0] + argsCopySize);
         212                 :     }
         213                 :
         214           10933 :     if (report->ucmessage) {
         215           10933 :         copy->ucmessage = (const jschar *)cursor;
         216           10933 :         js_memcpy(cursor, report->ucmessage, ucmessageSize);
         217           10933 :         cursor += ucmessageSize;
         218                 :     }
         219                 :
         220           10933 :     if (report->uclinebuf) {
         221             598 :         copy->uclinebuf = (const jschar *)cursor;
         222             598 :         js_memcpy(cursor, report->uclinebuf, uclinebufSize);
         223             598 :         cursor += uclinebufSize;
         224             598 :         if (report->uctokenptr) {
         225                 :             copy->uctokenptr = copy->uclinebuf + (report->uctokenptr -
         226             598 :                                                   report->uclinebuf);
         227                 :         }
         228                 :     }
         229                 :
         230           10933 :     if (report->linebuf) {
         231             598 :         copy->linebuf = (const char *)cursor;
         232             598 :         js_memcpy(cursor, report->linebuf, linebufSize);
         233             598 :         cursor += linebufSize;
         234             598 :         if (report->tokenptr) {
         235                 :             copy->tokenptr = copy->linebuf + (report->tokenptr -
         236             598 :                                               report->linebuf);
         237                 :         }
         238                 :     }
         239                 :
         240           10933 :     if (report->filename) {
         241           10933 :         copy->filename = (const char *)cursor;
         242           10933 :         js_memcpy(cursor, report->filename, filenameSize);
         243                 :     }
         244           10933 :     JS_ASSERT(cursor + filenameSize == (uint8_t *)copy + mallocSize);
         245                 :
         246                 :     /* HOLD called by the destination error object. */
         247           10933 :     copy->originPrincipals = report->originPrincipals;
         248                 :
         249                 :     /* Copy non-pointer members. */
         250           10933 :     copy->lineno = report->lineno;
         251           10933 :     copy->errorNumber = report->errorNumber;
         252                 :
         253                 :     /* Note that this is before it gets flagged with JSREPORT_EXCEPTION */
         254           10933 :     copy->flags = report->flags;
         255                 :
         256                 : #undef JS_CHARS_SIZE
         257           10933 :     return copy;
         258                 : }
         259                 :
         260                 : static HeapValue *
         261           13786 : GetStackTraceValueBuffer(JSExnPrivate *priv)
         262                 : {
         263                 :     /*
         264                 :      * We use extra memory after JSExnPrivateInfo.stackElems to store jsvals
         265                 :      * that helps to produce more informative stack traces. The following
         266                 :      * assert allows us to assume that no gap after stackElems is necessary to
         267                 :      * align the buffer properly.
         268                 :      */
         269                 :     JS_STATIC_ASSERT(sizeof(JSStackTraceElem) % sizeof(jsval) == 0);
         270                 :
         271           13786 :     return reinterpret_cast<HeapValue *>(priv->stackElems + priv->stackDepth);
         272                 : }
         273                 :
         274                 : struct SuppressErrorsGuard
         275                 : {
         276                 :     JSContext *cx;
         277                 :     JSErrorReporter prevReporter;
         278                 :     JSExceptionState *prevState;
         279                 :
         280           11745 :     SuppressErrorsGuard(JSContext *cx)
         281                 :       : cx(cx),
         282           11745 :         prevReporter(JS_SetErrorReporter(cx, NULL)),
         283           23490 :         prevState(JS_SaveExceptionState(cx))
         284           11745 :     {}
         285                 :
         286           11745 :     ~SuppressErrorsGuard()
         287                 :     {
         288           11745 :         JS_RestoreExceptionState(cx, prevState);
         289           11745 :         JS_SetErrorReporter(cx, prevReporter);
         290           11745 :     }
         291                 : };
         292                 :
         293                 : struct AppendWrappedArg {
         294                 :     JSContext *cx;
         295                 :     AutoValueVector &values;
         296         5763850 :     AppendWrappedArg(JSContext *cx, AutoValueVector &values)
         297                 :       : cx(cx),
         298         5763850 :         values(values)
         299         5763850 :     {}
         300                 :
         301         5349190 :     bool operator()(unsigned, Value *vp) {
         302         5349190 :         Value v = *vp;
         303                 :
         304                 :         /*
         305                 :          * Try to wrap.
         306                 :          *
         307                 :          * If wrap() fails, there's a good chance that it's because we're
         308                 :          * already in the process of throwing a native stack limit exception.
         309                 :          *
         310                 :          * This causes wrap() to throw, but it can't actually create an exception
         311                 :          * because we're already making one here, and cx->generatingError is true.
         312                 :          * So it returns false without an exception set on the stack. If we propagate
         313                 :          * that, it constitutes an uncatchable exception.
         314                 :          *
         315                 :          * So we just ignore exceptions. If wrap actually does set a pending
         316                 :          * exception, or if the caller sloppily left an exception on cx (which the
         317                 :          * e4x parser does), it doesn't matter - it will be overwritten shortly.
         318                 :          *
         319                 :          * NB: In the sloppy e4x case, one might thing we should clear the
         320                 :          * exception before calling wrap(). But wrap() has to be ok with pending
         321                 :          * exceptions, since it wraps exception objects during cross-compartment
         322                 :          * unwinding.
         323                 :          */
         324         5349190 :         if (!cx->compartment->wrap(cx, &v))
         325          464472 :             v = JSVAL_VOID;
         326                 :
         327                 :         /* Append the value. */
         328         5349190 :         return values.append(v);
         329                 :     }
         330                 : };
         331                 :
         332                 : static void
         333                 : SetExnPrivate(JSContext *cx, JSObject *exnObject, JSExnPrivate *priv);
         334                 :
         335                 : static bool
         336           11745 : InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message,
         337                 :                JSString *filename, unsigned lineno, JSErrorReport *report, int exnType)
         338                 : {
         339           11745 :     JS_ASSERT(exnObject->isError());
         340           11745 :     JS_ASSERT(!exnObject->getPrivate());
         341                 :
         342           11745 :     JSCheckAccessOp checkAccess = cx->runtime->securityCallbacks->checkObjectAccess;
         343                 :
         344           23490 :     Vector<JSStackTraceStackElem> frames(cx);
         345           23490 :     AutoValueVector values(cx);
         346                 :     {
         347           23490 :         SuppressErrorsGuard seg(cx);
         348         5788831 :         for (FrameRegsIter i(cx); !i.done(); ++i) {
         349         5777086 :             StackFrame *fp = i.fp();
         350                 :
         351                 :             /*
         352                 :              * Ask the crystal CAPS ball whether we can see values across
         353                 :              * compartment boundaries.
         354                 :              *
         355                 :              * NB: 'fp' may point to cross-compartment values that require wrapping.
         356                 :              */
         357         5777086 :             if (checkAccess && fp->isNonEvalFunctionFrame()) {
         358         5625394 :                 Value v = NullValue();
         359         5625394 :                 jsid callerid = ATOM_TO_JSID(cx->runtime->atomState.callerAtom);
         360         5625394 :                 if (!checkAccess(cx, &fp->callee(), callerid, JSACC_READ, &v))
         361               0 :                     break;
         362                 :             }
         363                 :
         364         5777086 :             if (!frames.growBy(1))
         365               0 :                 return false;
         366         5777086 :             JSStackTraceStackElem &frame = frames.back();
         367         5777086 :             if (fp->isNonEvalFunctionFrame()) {
         368         5763850 :                 frame.funName = fp->fun()->atom ? fp->fun()->atom : cx->runtime->emptyString;
         369         5763850 :                 frame.argc = fp->numActualArgs();
         370         5763850 :                 if (!fp->forEachCanonicalActualArg(AppendWrappedArg(cx, values)))
         371               0 :                     return false;
         372                 :             } else {
         373           13236 :                 frame.funName = NULL;
         374           13236 :                 frame.argc = 0;
         375                 :             }
         376         5777086 :             if (fp->isScriptFrame()) {
         377         5777086 :                 frame.filename = SaveScriptFilename(cx, fp->script()->filename);
         378         5777086 :                 if (!frame.filename)
         379               0 :                     return false;
         380         5777086 :                 frame.ulineno = PCToLineNumber(fp->script(), i.pc());
         381                 :             } else {
         382               0 :                 frame.ulineno = 0;
         383               0 :                 frame.filename = NULL;
         384                 :             }
         385                 :         }
         386                 :     }
         387                 :
         388                 :     /* Do not need overflow check: the vm stack is already bigger. */
         389                 :     JS_STATIC_ASSERT(sizeof(JSStackTraceElem) <= sizeof(StackFrame));
         390                 :
         391                 :     size_t nbytes = offsetof(JSExnPrivate, stackElems) +
         392           11745 :                     frames.length() * sizeof(JSStackTraceElem) +
         393           11745 :                     values.length() * sizeof(HeapValue);
         394                 :
         395           11745 :     JSExnPrivate *priv = (JSExnPrivate *)cx->malloc_(nbytes);
         396           11745 :     if (!priv)
         397               0 :         return false;
         398                 :
         399                 :     /* Initialize to zero so that write barriers don't witness undefined values. */
         400           11745 :     memset(priv, 0, nbytes);
         401                 :
         402           11745 :     if (report) {
         403                 :         /*
         404                 :          * Construct a new copy of the error report struct. We can't use the
         405                 :          * error report struct that was passed in, because it's allocated on
         406                 :          * the stack, and also because it may point to transient data in the
         407                 :          * TokenStream.
         408                 :          */
         409           10915 :         priv->errorReport = CopyErrorReport(cx, report);
         410           10915 :         if (!priv->errorReport) {
         411               0 :             cx->free_(priv);
         412               0 :             return false;
         413                 :         }
         414                 :     } else {
         415             830 :         priv->errorReport = NULL;
         416                 :     }
         417                 :
         418           11745 :     priv->message.init(message);
         419           11745 :     priv->filename.init(filename);
         420           11745 :     priv->lineno = lineno;
         421           11745 :     priv->stackDepth = frames.length();
         422           11745 :     priv->exnType = exnType;
         423                 :
         424           11745 :     JSStackTraceElem *framesDest = priv->stackElems;
         425           11745 :     HeapValue *valuesDest = reinterpret_cast<HeapValue *>(framesDest + frames.length());
         426           11745 :     JS_ASSERT(valuesDest == GetStackTraceValueBuffer(priv));
         427                 :
         428         5788831 :     for (size_t i = 0; i < frames.length(); ++i) {
         429         5777086 :         framesDest[i].funName.init(frames[i].funName);
         430         5777086 :         framesDest[i].argc = frames[i].argc;
         431         5777086 :         framesDest[i].filename = frames[i].filename;
         432         5777086 :         framesDest[i].ulineno = frames[i].ulineno;
         433                 :     }
         434         5360935 :     for (size_t i = 0; i < values.length(); ++i)
         435         5349190 :         valuesDest[i].init(cx->compartment, values[i]);
         436                 :
         437           11745 :     SetExnPrivate(cx, exnObject, priv);
         438           11745 :     return true;
         439                 : }
         440                 :
         441                 : static inline JSExnPrivate *
         442           89191 : GetExnPrivate(JSObject *obj)
         443                 : {
         444           89191 :     JS_ASSERT(obj->isError());
         445           89191 :     return (JSExnPrivate *) obj->getPrivate();
         446                 : }
         447                 :
         448                 : static void
         449           35261 : exn_trace(JSTracer *trc, JSObject *obj)
         450                 : {
         451                 :     JSExnPrivate *priv;
         452                 :     JSStackTraceElem *elem;
         453                 :     size_t vcount, i;
         454                 :     HeapValue *vp;
         455                 :
         456           35261 :     priv = GetExnPrivate(obj);
         457           35261 :     if (priv) {
         458            2023 :         if (priv->message)
         459            2001 :             MarkString(trc, &priv->message, "exception message");
         460            2023 :         if (priv->filename)
         461            2023 :             MarkString(trc, &priv->filename, "exception filename");
         462                 :
         463            2023 :         elem = priv->stackElems;
         464            6095 :         for (vcount = i = 0; i != priv->stackDepth; ++i, ++elem) {
         465            4072 :             if (elem->funName)
         466            1821 :                 MarkString(trc, &elem->funName, "stack trace function name");
         467            4072 :             if (IS_GC_MARKING_TRACER(trc) && elem->filename)
         468            3780 :                 MarkScriptFilename(elem->filename);
         469            4072 :             vcount += elem->argc;
         470                 :         }
         471            2023 :         vp = GetStackTraceValueBuffer(priv);
         472            2405 :         for (i = 0; i != vcount; ++i, ++vp)
         473             382 :             MarkValue(trc, vp, "stack trace argument");
         474                 :     }
         475           35261 : }
         476                 :
         477                 : /* NB: An error object's private must be set through this function. */
         478                 : static void
         479           11763 : SetExnPrivate(JSContext *cx, JSObject *exnObject, JSExnPrivate *priv)
         480                 : {
         481           11763 :     JS_ASSERT(!exnObject->getPrivate());
         482           11763 :     JS_ASSERT(exnObject->isError());
         483           11763 :     if (JSErrorReport *report = priv->errorReport) {
         484           10933 :         if (JSPrincipals *prin = report->originPrincipals)
         485               5 :             JS_HoldPrincipals(prin);
         486                 :     }
         487           11763 :     exnObject->setPrivate(priv);
         488           11763 : }
         489                 :
         490                 : static void
         491           38867 : exn_finalize(FreeOp *fop, JSObject *obj)
         492                 : {
         493           38867 :     if (JSExnPrivate *priv = GetExnPrivate(obj)) {
         494           11763 :         if (JSErrorReport *report = priv->errorReport) {
         495                 :             /* HOLD called by SetExnPrivate. */
         496           10933 :             if (JSPrincipals *prin = report->originPrincipals)
         497               5 :                 JS_DropPrincipals(fop->runtime(), prin);
         498           10933 :             fop->free_(report);
         499                 :         }
         500           11763 :         fop->free_(priv);
         501                 :     }
         502           38867 : }
         503                 :
         504                 : static JSBool
         505           14375 : exn_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
         506                 :             JSObject **objp)
         507                 : {
         508                 :     JSExnPrivate *priv;
         509                 :     JSString *str;
         510                 :     JSAtom *atom;
         511                 :     JSString *stack;
         512                 :     const char *prop;
         513                 :     jsval v;
         514                 :     unsigned attrs;
         515                 :
         516           14375 :     *objp = NULL;
         517           14375 :     priv = GetExnPrivate(obj);
         518           14375 :     if (priv && JSID_IS_ATOM(id)) {
         519           11587 :         str = JSID_TO_STRING(id);
         520                 :
         521           11587 :         atom = cx->runtime->atomState.messageAtom;
         522           11587 :         if (str == atom) {
         523            1652 :             prop = js_message_str;
         524                 :
         525                 :             /*
         526                 :              * Per ES5 15.11.1.1, if Error is called with no argument or with
         527                 :              * undefined as the argument, it returns an Error object with no
         528                 :              * own message property.
         529                 :              */
         530            1652 :             if (!priv->message)
         531               9 :                 return true;
         532                 :
         533            1643 :             v = STRING_TO_JSVAL(priv->message);
         534            1643 :             attrs = 0;
         535            1643 :             goto define;
         536                 :         }
         537                 :
         538            9935 :         atom = cx->runtime->atomState.fileNameAtom;
         539            9935 :         if (str == atom) {
         540              45 :             prop = js_fileName_str;
         541              45 :             v = STRING_TO_JSVAL(priv->filename);
         542              45 :             attrs = JSPROP_ENUMERATE;
         543              45 :             goto define;
         544                 :         }
         545                 :
         546            9890 :         atom = cx->runtime->atomState.lineNumberAtom;
         547            9890 :         if (str == atom) {
         548             684 :             prop = js_lineNumber_str;
         549             684 :             v = INT_TO_JSVAL(priv->lineno);
         550             684 :             attrs = JSPROP_ENUMERATE;
         551             684 :             goto define;
         552                 :         }
         553                 :
         554            9206 :         atom = cx->runtime->atomState.stackAtom;
         555            9206 :         if (str == atom) {
         556              18 :             stack = StackTraceToString(cx, priv);
         557              18 :             if (!stack)
         558               0 :                 return false;
         559                 :
         560              18 :             prop = js_stack_str;
         561              18 :             v = STRING_TO_JSVAL(stack);
         562              18 :             attrs = JSPROP_ENUMERATE;
         563              18 :             goto define;
         564                 :         }
         565                 :     }
         566           11976 :     return true;
         567                 :
         568                 :   define:
         569            2390 :     if (!JS_DefineProperty(cx, obj, prop, v, NULL, NULL, attrs))
         570               0 :         return false;
         571            2390 :     *objp = obj;
         572            2390 :     return true;
         573                 : }
         574                 :
         575                 : JSErrorReport *
         576             850 : js_ErrorFromException(JSContext *cx, jsval exn)
         577                 : {
         578                 :     JSObject *obj;
         579                 :     JSExnPrivate *priv;
         580                 :
         581             850 :     if (JSVAL_IS_PRIMITIVE(exn))
         582             153 :         return NULL;
         583             697 :     obj = JSVAL_TO_OBJECT(exn);
         584             697 :     if (!obj->isError())
         585              27 :         return NULL;
         586             670 :     priv = GetExnPrivate(obj);
         587             670 :     if (!priv)
         588               0 :         return NULL;
         589             670 :     return priv->errorReport;
         590                 : }
         591                 :
         592                 : static JSString *
         593               0 : ValueToShortSource(JSContext *cx, const Value &v)
         594                 : {
         595                 :     JSString *str;
         596                 :
         597                 :     /* Avoid toSource bloat and fallibility for object types. */
         598               0 :     if (!v.isObject())
         599               0 :         return js_ValueToSource(cx, v);
         600                 :
         601               0 :     JSObject *obj = &v.toObject();
         602               0 :     AutoCompartment ac(cx, obj);
         603               0 :     if (!ac.enter())
         604               0 :         return NULL;
         605                 :
         606               0 :     if (obj->isFunction()) {
         607                 :         /*
         608                 :          * XXX Avoid function decompilation bloat for now.
         609                 :          */
         610               0 :         str = JS_GetFunctionId(obj->toFunction());
         611               0 :         if (!str && !(str = js_ValueToSource(cx, v))) {
         612                 :             /*
         613                 :              * Continue to soldier on if the function couldn't be
         614                 :              * converted into a string.
         615                 :              */
         616               0 :             JS_ClearPendingException(cx);
         617               0 :             str = JS_NewStringCopyZ(cx, "[unknown function]");
         618                 :         }
         619                 :     } else {
         620                 :         /*
         621                 :          * XXX Avoid toString on objects, it takes too long and uses too much
         622                 :          * memory, for too many classes (see Mozilla bug 166743).
         623                 :          */
         624                 :         char buf[100];
         625               0 :         JS_snprintf(buf, sizeof buf, "[object %s]", js::UnwrapObject(obj, false)->getClass()->name);
         626               0 :         str = JS_NewStringCopyZ(cx, buf);
         627                 :     }
         628                 :
         629               0 :     ac.leave();
         630                 :
         631               0 :     if (!str || !cx->compartment->wrap(cx, &str))
         632               0 :         return NULL;
         633               0 :     return str;
         634                 : }
         635                 :
         636                 : static JSString *
         637              18 : StackTraceToString(JSContext *cx, JSExnPrivate *priv)
         638                 : {
         639                 :     jschar *stackbuf;
         640                 :     size_t stacklen, stackmax;
         641                 :     JSStackTraceElem *elem, *endElem;
         642                 :     HeapValue *values;
         643                 :     size_t i;
         644                 :     JSString *str;
         645                 :     const char *cp;
         646                 :     char ulnbuf[11];
         647                 :
         648                 :     /* After this point, failing control flow must goto bad. */
         649              18 :     stackbuf = NULL;
         650              18 :     stacklen = stackmax = 0;
         651                 :
         652                 : /* Limit the stackbuf length to a reasonable value to avoid overflow checks. */
         653                 : #define STACK_LENGTH_LIMIT JS_BIT(20)
         654                 :
         655                 : #define APPEND_CHAR_TO_STACK(c)                                               \
         656                 :     JS_BEGIN_MACRO                                                            \
         657                 :         if (stacklen == stackmax) {                                           \
         658                 :             void *ptr_;                                                       \
         659                 :             if (stackmax >= STACK_LENGTH_LIMIT)                               \
         660                 :                 goto done;                                                    \
         661                 :             stackmax = stackmax ? 2 * stackmax : 64;                          \
         662                 :             ptr_ = cx->realloc_(stackbuf, (stackmax+1) * sizeof(jschar));      \
         663                 :             if (!ptr_)                                                        \
         664                 :                 goto bad;                                                     \
         665                 :             stackbuf = (jschar *) ptr_;                                       \
         666                 :         }                                                                     \
         667                 :         stackbuf[stacklen++] = (c);                                           \
         668                 :     JS_END_MACRO
         669                 :
         670                 : #define APPEND_STRING_TO_STACK(str)                                           \
         671                 :     JS_BEGIN_MACRO                                                            \
         672                 :         JSString *str_ = str;                                                 \
         673                 :         size_t length_ = str_->length();                                      \
         674                 :         const jschar *chars_ = str_->getChars(cx);                            \
         675                 :         if (!chars_)                                                          \
         676                 :             goto bad;                                                         \
         677                 :                                                                               \
         678                 :         if (length_ > stackmax - stacklen) {                                  \
         679                 :             void *ptr_;                                                       \
         680                 :             if (stackmax >= STACK_LENGTH_LIMIT ||                             \
         681                 :                 length_ >= STACK_LENGTH_LIMIT - stacklen) {                   \
         682                 :                 goto done;                                                    \
         683                 :             }                                                                 \
         684                 :             stackmax = RoundUpPow2(stacklen + length_);                       \
         685                 :             ptr_ = cx->realloc_(stackbuf, (stackmax+1) * sizeof(jschar));     \
         686                 :             if (!ptr_)                                                        \
         687                 :                 goto bad;                                                     \
         688                 :             stackbuf = (jschar *) ptr_;                                       \
         689                 :         }                                                                     \
         690                 :         js_strncpy(stackbuf + stacklen, chars_, length_);                     \
         691                 :         stacklen += length_;                                                  \
         692                 :     JS_END_MACRO
         693                 :
         694              18 :     values = GetStackTraceValueBuffer(priv);
         695              18 :     elem = priv->stackElems;
         696              18 :     for (endElem = elem + priv->stackDepth; elem != endElem; elem++) {
         697               0 :         if (elem->funName) {
         698               0 :             APPEND_STRING_TO_STACK(elem->funName);
         699               0 :             APPEND_CHAR_TO_STACK('(');
         700               0 :             for (i = 0; i != elem->argc; i++, values++) {
         701               0 :                 if (i > 0)
         702               0 :                     APPEND_CHAR_TO_STACK(',');
         703               0 :                 str = ValueToShortSource(cx, *values);
         704               0 :                 if (!str)
         705               0 :                     goto bad;
         706               0 :                 APPEND_STRING_TO_STACK(str);
         707                 :             }
         708               0 :             APPEND_CHAR_TO_STACK(')');
         709                 :         }
         710               0 :         APPEND_CHAR_TO_STACK('@');
         711               0 :         if (elem->filename) {
         712               0 :             for (cp = elem->filename; *cp; cp++)
         713               0 :                 APPEND_CHAR_TO_STACK(*cp);
         714                 :         }
         715               0 :         APPEND_CHAR_TO_STACK(':');
         716               0 :         JS_snprintf(ulnbuf, sizeof ulnbuf, "%u", elem->ulineno);
         717               0 :         for (cp = ulnbuf; *cp; cp++)
         718               0 :             APPEND_CHAR_TO_STACK(*cp);
         719               0 :         APPEND_CHAR_TO_STACK('\n');
         720                 :     }
         721                 : #undef APPEND_CHAR_TO_STACK
         722                 : #undef APPEND_STRING_TO_STACK
         723                 : #undef STACK_LENGTH_LIMIT
         724                 :
         725                 :   done:
         726              18 :     if (stacklen == 0) {
         727              18 :         JS_ASSERT(!stackbuf);
         728              18 :         return cx->runtime->emptyString;
         729                 :     }
         730               0 :     if (stacklen < stackmax) {
         731                 :         /*
         732                 :          * Realloc can fail when shrinking on some FreeBSD versions, so
         733                 :          * don't use JS_realloc here; simply let the oversized allocation
         734                 :          * be owned by the string in that rare case.
         735                 :          */
         736               0 :         void *shrunk = cx->realloc_(stackbuf, (stacklen+1) * sizeof(jschar));
         737               0 :         if (shrunk)
         738               0 :             stackbuf = (jschar *) shrunk;
         739                 :     }
         740                 :
         741               0 :     stackbuf[stacklen] = 0;
         742               0 :     str = js_NewString(cx, stackbuf, stacklen);
         743               0 :     if (str)
         744               0 :         return str;
         745                 :
         746                 :   bad:
         747               0 :     if (stackbuf)
         748               0 :         cx->free_(stackbuf);
         749               0 :     return NULL;
         750                 : }
         751                 :
         752                 : /* XXXbe Consolidate the ugly truth that we don't treat filename as UTF-8
         753                 :          with these two functions. */
         754                 : static JSString *
         755             830 : FilenameToString(JSContext *cx, const char *filename)
         756                 : {
         757             830 :     return JS_NewStringCopyZ(cx, filename);
         758                 : }
         759                 :
         760                 : static JSBool
         761             830 : Exception(JSContext *cx, unsigned argc, Value *vp)
         762                 : {
         763             830 :     CallArgs args = CallArgsFromVp(argc, vp);
         764                 :
         765                 :     /*
         766                 :      * ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when
         767                 :      * called as functions, without operator new.  But as we do not give
         768                 :      * each constructor a distinct JSClass, whose .name member is used by
         769                 :      * NewNativeClassInstance to find the class prototype, we must get the
         770                 :      * class prototype ourselves.
         771                 :      */
         772                 :     Value protov;
         773             830 :     if (!args.callee().getProperty(cx, cx->runtime->atomState.classPrototypeAtom, &protov))
         774               0 :         return false;
         775                 :
         776             830 :     if (!protov.isObject()) {
         777               0 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_PROTOTYPE, "Error");
         778               0 :         return false;
         779                 :     }
         780                 :
         781             830 :     JSObject *errProto = &protov.toObject();
         782             830 :     JSObject *obj = NewObjectWithGivenProto(cx, &ErrorClass, errProto, NULL);
         783             830 :     if (!obj)
         784               0 :         return false;
         785                 :
         786                 :     /* Set the 'message' property. */
         787                 :     JSString *message;
         788             830 :     if (args.hasDefined(0)) {
         789             126 :         message = ToString(cx, args[0]);
         790             126 :         if (!message)
         791               0 :             return false;
         792             126 :         args[0].setString(message);
         793                 :     } else {
         794             704 :         message = NULL;
         795                 :     }
         796                 :
         797                 :     /* Find the scripted caller. */
         798             830 :     FrameRegsIter iter(cx);
         799            1660 :     while (!iter.done() && !iter.fp()->isScriptFrame())
         800               0 :         ++iter;
         801                 :
         802                 :     /* Set the 'fileName' property. */
         803                 :     JSString *filename;
         804             830 :     if (args.length() > 1) {
         805               0 :         filename = ToString(cx, args[1]);
         806               0 :         if (!filename)
         807               0 :             return false;
         808               0 :         args[1].setString(filename);
         809                 :     } else {
         810             830 :         if (!iter.done()) {
         811             830 :             filename = FilenameToString(cx, iter.fp()->script()->filename);
         812             830 :             if (!filename)
         813               0 :                 return false;
         814                 :         } else {
         815               0 :             filename = cx->runtime->emptyString;
         816                 :         }
         817                 :     }
         818                 :
         819                 :     /* Set the 'lineNumber' property. */
         820                 :     uint32_t lineno;
         821             830 :     if (args.length() > 2) {
         822               0 :         if (!ToUint32(cx, args[2], &lineno))
         823               0 :             return false;
         824                 :     } else {
         825             830 :         lineno = iter.done() ? 0 : PCToLineNumber(iter.fp()->script(), iter.pc());
         826                 :     }
         827                 :
         828             830 :     int exnType = args.callee().toFunction()->getExtendedSlot(0).toInt32();
         829             830 :     if (!InitExnPrivate(cx, obj, message, filename, lineno, NULL, exnType))
         830               0 :         return false;
         831                 :
         832             830 :     args.rval().setObject(*obj);
         833             830 :     return true;
         834                 : }
         835                 :
         836                 : /* ES5 15.11.4.4 (NB: with subsequent errata). */
         837                 : static JSBool
         838            6923 : exn_toString(JSContext *cx, unsigned argc, Value *vp)
         839                 : {
         840            6923 :     JS_CHECK_RECURSION(cx, return false);
         841            6905 :     CallArgs args = CallArgsFromVp(argc, vp);
         842                 :
         843                 :     /* Step 2. */
         844            6905 :     if (!args.thisv().isObject()) {
         845               0 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_PROTOTYPE, "Error");
         846               0 :         return false;
         847                 :     }
         848                 :
         849                 :     /* Step 1. */
         850            6905 :     JSObject &obj = args.thisv().toObject();
         851                 :
         852                 :     /* Step 3. */
         853                 :     Value nameVal;
         854            6905 :     if (!obj.getProperty(cx, cx->runtime->atomState.nameAtom, &nameVal))
         855               0 :         return false;
         856                 :
         857                 :     /* Step 4. */
         858                 :     JSString *name;
         859            6905 :     if (nameVal.isUndefined()) {
         860               9 :         name = CLASS_ATOM(cx, Error);
         861                 :     } else {
         862            6896 :         name = ToString(cx, nameVal);
         863            6896 :         if (!name)
         864            5289 :             return false;
         865                 :     }
         866                 :
         867                 :     /* Step 5. */
         868                 :     Value msgVal;
         869            1616 :     if (!obj.getProperty(cx, cx->runtime->atomState.messageAtom, &msgVal))
         870               0 :         return false;
         871                 :
         872                 :     /* Step 6. */
         873                 :     JSString *message;
         874            1616 :     if (msgVal.isUndefined()) {
         875               9 :         message = cx->runtime->emptyString;
         876                 :     } else {
         877            1607 :         message = ToString(cx, msgVal);
         878            1607 :         if (!message)
         879               0 :             return false;
         880                 :     }
         881                 :
         882                 :     /* Step 7. */
         883            1616 :     if (name->empty() && message->empty()) {
         884               9 :         args.rval().setString(CLASS_ATOM(cx, Error));
         885               9 :         return true;
         886                 :     }
         887                 :
         888                 :     /* Step 8. */
         889            1607 :     if (name->empty()) {
         890               9 :         args.rval().setString(message);
         891               9 :         return true;
         892                 :     }
         893                 :
         894                 :     /* Step 9. */
         895            1598 :     if (message->empty()) {
         896              36 :         args.rval().setString(name);
         897              36 :         return true;
         898                 :     }
         899                 :
         900                 :     /* Step 10. */
         901            3124 :     StringBuffer sb(cx);
         902            1562 :     if (!sb.append(name) || !sb.append(": ") || !sb.append(message))
         903               0 :         return false;
         904                 :
         905            1562 :     JSString *str = sb.finishString();
         906            1562 :     if (!str)
         907               0 :         return false;
         908            1562 :     args.rval().setString(str);
         909            1562 :     return true;
         910                 : }
         911                 :
         912                 : #if JS_HAS_TOSOURCE
         913                 : /*
         914                 :  * Return a string that may eval to something similar to the original object.
         915                 :  */
         916                 : static JSBool
         917               0 : exn_toSource(JSContext *cx, unsigned argc, Value *vp)
         918                 : {
         919               0 :     JS_CHECK_RECURSION(cx, return false);
         920               0 :     CallArgs args = CallArgsFromVp(argc, vp);
         921                 :
         922               0 :     JSObject *obj = ToObject(cx, &args.thisv());
         923               0 :     if (!obj)
         924               0 :         return false;
         925                 :
         926                 :     Value nameVal;
         927                 :     JSString *name;
         928               0 :     if (!obj->getProperty(cx, cx->runtime->atomState.nameAtom, &nameVal) ||
         929                 :         !(name = ToString(cx, nameVal)))
         930                 :     {
         931               0 :         return false;
         932                 :     }
         933                 :
         934                 :     Value messageVal;
         935                 :     JSString *message;
         936               0 :     if (!obj->getProperty(cx, cx->runtime->atomState.messageAtom, &messageVal) ||
         937                 :         !(message = js_ValueToSource(cx, messageVal)))
         938                 :     {
         939               0 :         return false;
         940                 :     }
         941                 :
         942                 :     Value filenameVal;
         943                 :     JSString *filename;
         944               0 :     if (!obj->getProperty(cx, cx->runtime->atomState.fileNameAtom, &filenameVal) ||
         945                 :         !(filename = js_ValueToSource(cx, filenameVal)))
         946                 :     {
         947               0 :         return false;
         948                 :     }
         949                 :
         950                 :     Value linenoVal;
         951                 :     uint32_t lineno;
         952               0 :     if (!obj->getProperty(cx, cx->runtime->atomState.lineNumberAtom, &linenoVal) ||
         953               0 :         !ToUint32(cx, linenoVal, &lineno))
         954                 :     {
         955               0 :         return false;
         956                 :     }
         957                 :
         958               0 :     StringBuffer sb(cx);
         959               0 :     if (!sb.append("(new ") || !sb.append(name) || !sb.append("("))
         960               0 :         return false;
         961                 :
         962               0 :     if (!sb.append(message))
         963               0 :         return false;
         964                 :
         965               0 :     if (!filename->empty()) {
         966               0 :         if (!sb.append(", ") || !sb.append(filename))
         967               0 :             return false;
         968                 :     }
         969               0 :     if (lineno != 0) {
         970                 :         /* We have a line, but no filename, add empty string */
         971               0 :         if (filename->empty() && !sb.append(", \"\""))
         972               0 :                 return false;
         973                 :
         974               0 :         JSString *linenumber = ToString(cx, linenoVal);
         975               0 :         if (!linenumber)
         976               0 :             return false;
         977               0 :         if (!sb.append(", ") || !sb.append(linenumber))
         978               0 :             return false;
         979                 :     }
         980                 :
         981               0 :     if (!sb.append("))"))
         982               0 :         return false;
         983                 :
         984               0 :     JSString *str = sb.finishString();
         985               0 :     if (!str)
         986               0 :         return false;
         987               0 :     args.rval().setString(str);
         988               0 :     return true;
         989                 : }
         990                 : #endif
         991                 :
         992                 : static JSFunctionSpec exception_methods[] = {
         993                 : #if JS_HAS_TOSOURCE
         994                 :     JS_FN(js_toSource_str,   exn_toSource,           0,0),
         995                 : #endif
         996                 :     JS_FN(js_toString_str,   exn_toString,           0,0),
         997                 :     JS_FS_END
         998                 : };
         999                 :
        1000                 : /* JSProto_ ordering for exceptions shall match JSEXN_ constants. */
        1001                 : JS_STATIC_ASSERT(JSEXN_ERR == 0);
        1002                 : JS_STATIC_ASSERT(JSProto_Error + JSEXN_INTERNALERR  == JSProto_InternalError);
        1003                 : JS_STATIC_ASSERT(JSProto_Error + JSEXN_EVALERR      == JSProto_EvalError);
        1004                 : JS_STATIC_ASSERT(JSProto_Error + JSEXN_RANGEERR     == JSProto_RangeError);
        1005                 : JS_STATIC_ASSERT(JSProto_Error + JSEXN_REFERENCEERR == JSProto_ReferenceError);
        1006                 : JS_STATIC_ASSERT(JSProto_Error + JSEXN_SYNTAXERR    == JSProto_SyntaxError);
        1007                 : JS_STATIC_ASSERT(JSProto_Error + JSEXN_TYPEERR      == JSProto_TypeError);
        1008                 : JS_STATIC_ASSERT(JSProto_Error + JSEXN_URIERR       == JSProto_URIError);
        1009                 :
        1010                 : static JSObject *
        1011           27104 : InitErrorClass(JSContext *cx, GlobalObject *global, int type, JSObject &proto)
        1012                 : {
        1013           27104 :     JSProtoKey key = GetExceptionProtoKey(type);
        1014           27104 :     JSAtom *name = cx->runtime->atomState.classAtoms[key];
        1015           27104 :     JSObject *errorProto = global->createBlankPrototypeInheriting(cx, &ErrorClass, proto);
        1016           27104 :     if (!errorProto)
        1017               0 :         return NULL;
        1018                 :
        1019           27104 :     Value empty = StringValue(cx->runtime->emptyString);
        1020           27104 :     jsid nameId = ATOM_TO_JSID(cx->runtime->atomState.nameAtom);
        1021           27104 :     jsid messageId = ATOM_TO_JSID(cx->runtime->atomState.messageAtom);
        1022           27104 :     jsid fileNameId = ATOM_TO_JSID(cx->runtime->atomState.fileNameAtom);
        1023           27104 :     jsid lineNumberId = ATOM_TO_JSID(cx->runtime->atomState.lineNumberAtom);
        1024          135520 :     if (!DefineNativeProperty(cx, errorProto, nameId, StringValue(name),
        1025           27104 :                               JS_PropertyStub, JS_StrictPropertyStub, 0, 0, 0) ||
        1026                 :         !DefineNativeProperty(cx, errorProto, messageId, empty,
        1027           27104 :                               JS_PropertyStub, JS_StrictPropertyStub, 0, 0, 0) ||
        1028                 :         !DefineNativeProperty(cx, errorProto, fileNameId, empty,
        1029           27104 :                               JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE, 0, 0) ||
        1030                 :         !DefineNativeProperty(cx, errorProto, lineNumberId, Int32Value(0),
        1031           27104 :                               JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE, 0, 0))
        1032                 :     {
        1033               0 :         return NULL;
        1034                 :     }
        1035                 :
        1036                 :     /* Create the corresponding constructor. */
        1037                 :     JSFunction *ctor = global->createConstructor(cx, Exception, name, 1,
        1038           27104 :                                                  JSFunction::ExtendedFinalizeKind);
        1039           27104 :     if (!ctor)
        1040               0 :         return NULL;
        1041           27104 :     ctor->setExtendedSlot(0, Int32Value(int32_t(type)));
        1042                 :
        1043           27104 :     if (!LinkConstructorAndPrototype(cx, ctor, errorProto))
        1044               0 :         return NULL;
        1045                 :
        1046           27104 :     if (!DefineConstructorAndPrototype(cx, global, key, ctor, errorProto))
        1047               0 :         return NULL;
        1048                 :
        1049           27104 :     JS_ASSERT(!errorProto->getPrivate());
        1050                 :
        1051           27104 :     return errorProto;
        1052                 : }
        1053                 :
        1054                 : JSObject *
        1055            3388 : js_InitExceptionClasses(JSContext *cx, JSObject *obj)
        1056                 : {
        1057            3388 :     JS_ASSERT(obj->isGlobal());
        1058            3388 :     JS_ASSERT(obj->isNative());
        1059                 :
        1060            3388 :     GlobalObject *global = &obj->asGlobal();
        1061                 :
        1062            3388 :     JSObject *objectProto = global->getOrCreateObjectPrototype(cx);
        1063            3388 :     if (!objectProto)
        1064               0 :         return NULL;
        1065                 :
        1066                 :     /* Initialize the base Error class first. */
        1067            3388 :     JSObject *errorProto = InitErrorClass(cx, global, JSEXN_ERR, *objectProto);
        1068            3388 :     if (!errorProto)
        1069               0 :         return NULL;
        1070                 :
        1071                 :     /* |Error.prototype| alone has method properties. */
        1072            3388 :     if (!DefinePropertiesAndBrand(cx, errorProto, NULL, exception_methods))
        1073               0 :         return NULL;
        1074                 :
        1075                 :     /* Define all remaining *Error constructors. */
        1076           27104 :     for (int i = JSEXN_ERR + 1; i < JSEXN_LIMIT; i++) {
        1077           23716 :         if (!InitErrorClass(cx, global, i, *errorProto))
        1078               0 :             return NULL;
        1079                 :     }
        1080                 :
        1081            3388 :     return errorProto;
        1082                 : }
        1083                 :
        1084                 : const JSErrorFormatString*
        1085          950416 : js_GetLocalizedErrorMessage(JSContext* cx, void *userRef, const char *locale,
        1086                 :                             const unsigned errorNumber)
        1087                 : {
        1088          950416 :     const JSErrorFormatString *errorString = NULL;
        1089                 :
        1090          950416 :     if (cx->localeCallbacks && cx->localeCallbacks->localeGetErrorMessage) {
        1091                 :         errorString = cx->localeCallbacks
        1092               0 :                         ->localeGetErrorMessage(userRef, locale, errorNumber);
        1093                 :     }
        1094          950416 :     if (!errorString)
        1095          950416 :         errorString = js_GetErrorMessage(userRef, locale, errorNumber);
        1096          950416 :     return errorString;
        1097                 : }
        1098                 :
        1099                 : #if defined ( DEBUG_mccabe ) && defined ( PRINTNAMES )
        1100                 : /* For use below... get character strings for error name and exception name */
        1101                 : static struct exnname { char *name; char *exception; } errortoexnname[] = {
        1102                 : #define MSG_DEF(name, number, count, exception, format) \
        1103                 :     {#name, #exception},
        1104                 : #include "js.msg"
        1105                 : #undef MSG_DEF
        1106                 : };
        1107                 : #endif /* DEBUG */
        1108                 :
        1109                 : JSBool
        1110          475459 : js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp,
        1111                 :                     JSErrorCallback callback, void *userRef)
        1112                 : {
        1113                 :     JSErrNum errorNumber;
        1114                 :     const JSErrorFormatString *errorString;
        1115                 :     JSExnType exn;
        1116                 :     jsval tv[4];
        1117                 :     JSObject *errProto, *errObject;
        1118                 :     JSString *messageStr, *filenameStr;
        1119                 :
        1120                 :     /*
        1121                 :      * Tell our caller to report immediately if this report is just a warning.
        1122                 :      */
        1123          475459 :     JS_ASSERT(reportp);
        1124          475459 :     if (JSREPORT_IS_WARNING(reportp->flags))
        1125              72 :         return false;
        1126                 :
        1127                 :     /* Find the exception index associated with this error. */
        1128          475387 :     errorNumber = (JSErrNum) reportp->errorNumber;
        1129          475387 :     if (!callback || callback == js_GetErrorMessage)
        1130          475117 :         errorString = js_GetLocalizedErrorMessage(cx, NULL, NULL, errorNumber);
        1131                 :     else
        1132             270 :         errorString = callback(userRef, NULL, errorNumber);
        1133          475387 :     exn = errorString ? (JSExnType) errorString->exnType : JSEXN_NONE;
        1134          475387 :     JS_ASSERT(exn < JSEXN_LIMIT);
        1135                 :
        1136                 : #if defined( DEBUG_mccabe ) && defined ( PRINTNAMES )
        1137                 :     /* Print the error name and the associated exception name to stderr */
        1138                 :     fprintf(stderr, "%s\t%s\n",
        1139                 :             errortoexnname[errorNumber].name,
        1140                 :             errortoexnname[errorNumber].exception);
        1141                 : #endif
        1142                 :
        1143                 :     /*
        1144                 :      * Return false (no exception raised) if no exception is associated
        1145                 :      * with the given error number.
        1146                 :      */
        1147          475387 :     if (exn == JSEXN_NONE)
        1148               0 :         return false;
        1149                 :
        1150                 :     /* Prevent infinite recursion. */
        1151          475387 :     if (cx->generatingError)
        1152          464472 :         return false;
        1153           21830 :     AutoScopedAssign<bool> asa(&cx->generatingError, true);
        1154                 :
        1155                 :     /* Protect the newly-created strings below from nesting GCs. */
        1156           10915 :     PodArrayZero(tv);
        1157           21830 :     AutoArrayRooter tvr(cx, ArrayLength(tv), tv);
        1158                 :
        1159                 :     /*
        1160                 :      * Try to get an appropriate prototype by looking up the corresponding
        1161                 :      * exception constructor name in the scope chain of the current context's
        1162                 :      * top stack frame, or in the global object if no frame is active.
        1163                 :      */
        1164           10915 :     if (!js_GetClassPrototype(cx, NULL, GetExceptionProtoKey(exn), &errProto))
        1165               0 :         return false;
        1166           10915 :     tv[0] = OBJECT_TO_JSVAL(errProto);
        1167                 :
        1168           10915 :     if (!(errObject = NewObjectWithGivenProto(cx, &ErrorClass, errProto, NULL)))
        1169               0 :         return false;
        1170           10915 :     tv[1] = OBJECT_TO_JSVAL(errObject);
        1171                 :
        1172           10915 :     if (!(messageStr = JS_NewStringCopyZ(cx, message)))
        1173               0 :         return false;
        1174           10915 :     tv[2] = STRING_TO_JSVAL(messageStr);
        1175                 :
        1176           10915 :     if (!(filenameStr = JS_NewStringCopyZ(cx, reportp->filename)))
        1177               0 :         return false;
        1178           10915 :     tv[3] = STRING_TO_JSVAL(filenameStr);
        1179                 :
        1180           10915 :     if (!InitExnPrivate(cx, errObject, messageStr, filenameStr,
        1181           10915 :                         reportp->lineno, reportp, exn)) {
        1182               0 :         return false;
        1183                 :     }
        1184                 :
        1185           10915 :     JS_SetPendingException(cx, OBJECT_TO_JSVAL(errObject));
        1186                 :
        1187                 :     /* Flag the error report passed in to indicate an exception was raised. */
        1188           10915 :     reportp->flags |= JSREPORT_EXCEPTION;
        1189           10915 :     return true;
        1190                 : }
        1191                 :
        1192                 : static bool
        1193              27 : IsDuckTypedErrorObject(JSContext *cx, JSObject *exnObject, const char **filename_strp)
        1194                 : {
        1195                 :     JSBool found;
        1196              27 :     if (!JS_HasProperty(cx, exnObject, js_message_str, &found) || !found)
        1197               0 :         return false;
        1198                 :
        1199              27 :     const char *filename_str = *filename_strp;
        1200              27 :     if (!JS_HasProperty(cx, exnObject, filename_str, &found) || !found) {
        1201                 :         /* DOMException duck quacks "filename" (all lowercase) */
        1202               0 :         filename_str = "filename";
        1203               0 :         if (!JS_HasProperty(cx, exnObject, filename_str, &found) || !found)
        1204               0 :             return false;
        1205                 :     }
        1206                 :
        1207              27 :     if (!JS_HasProperty(cx, exnObject, js_lineNumber_str, &found) || !found)
        1208               0 :         return false;
        1209                 :
        1210              27 :     *filename_strp = filename_str;
        1211              27 :     return true;
        1212                 : }
        1213                 :
        1214                 : JSBool
        1215             854 : js_ReportUncaughtException(JSContext *cx)
        1216                 : {
        1217                 :     jsval exn;
        1218                 :     JSObject *exnObject;
        1219                 :     jsval roots[6];
        1220                 :     JSErrorReport *reportp, report;
        1221                 :     JSString *str;
        1222                 :
        1223             854 :     if (!JS_IsExceptionPending(cx))
        1224               4 :         return true;
        1225                 :
        1226             850 :     if (!JS_GetPendingException(cx, &exn))
        1227               0 :         return false;
        1228                 :
        1229             850 :     PodArrayZero(roots);
        1230            1700 :     AutoArrayRooter tvr(cx, ArrayLength(roots), roots);
        1231                 :
        1232                 :     /*
        1233                 :      * Because ToString below could error and an exception object could become
        1234                 :      * unrooted, we must root exnObject.  Later, if exnObject is non-null, we
        1235                 :      * need to root other intermediates, so allocate an operand stack segment
        1236                 :      * to protect all of these values.
        1237                 :      */
        1238             850 :     if (JSVAL_IS_PRIMITIVE(exn)) {
        1239             153 :         exnObject = NULL;
        1240                 :     } else {
        1241             697 :         exnObject = JSVAL_TO_OBJECT(exn);
        1242             697 :         roots[0] = exn;
        1243                 :     }
        1244                 :
        1245             850 :     JS_ClearPendingException(cx);
        1246             850 :     reportp = js_ErrorFromException(cx, exn);
        1247                 :
        1248                 :     /* XXX L10N angels cry once again. see also everywhere else */
        1249             850 :     str = ToString(cx, exn);
        1250             850 :     if (str)
        1251             841 :         roots[1] = StringValue(str);
        1252                 :
        1253             850 :     const char *filename_str = js_fileName_str;
        1254            1700 :     JSAutoByteString filename;
        1255             913 :     if (!reportp && exnObject &&
        1256              36 :         (exnObject->isError() ||
        1257              27 :          IsDuckTypedErrorObject(cx, exnObject, &filename_str)))
        1258                 :     {
        1259              36 :         JSString *name = NULL;
        1260              72 :         if (JS_GetProperty(cx, exnObject, js_name_str, &roots[2]) &&
        1261              36 :             JSVAL_IS_STRING(roots[2]))
        1262                 :         {
        1263              36 :             name = JSVAL_TO_STRING(roots[2]);
        1264                 :         }
        1265                 :
        1266              36 :         JSString *msg = NULL;
        1267              72 :         if (JS_GetProperty(cx, exnObject, js_message_str, &roots[3]) &&
        1268              36 :             JSVAL_IS_STRING(roots[3]))
        1269                 :         {
        1270              36 :             msg = JSVAL_TO_STRING(roots[3]);
        1271                 :         }
        1272                 :
        1273              36 :         if (name && msg) {
        1274              36 :             JSString *colon = JS_NewStringCopyZ(cx, ": ");
        1275              36 :             if (!colon)
        1276               0 :                 return false;
        1277              36 :             JSString *nameColon = JS_ConcatStrings(cx, name, colon);
        1278              36 :             if (!nameColon)
        1279               0 :                 return false;
        1280              36 :             str = JS_ConcatStrings(cx, nameColon, msg);
        1281              36 :             if (!str)
        1282               0 :                 return false;
        1283               0 :         } else if (name) {
        1284               0 :             str = name;
        1285               0 :         } else if (msg) {
        1286               0 :             str = msg;
        1287                 :         }
        1288                 :
        1289              36 :         if (JS_GetProperty(cx, exnObject, filename_str, &roots[4])) {
        1290              36 :             JSString *tmp = ToString(cx, roots[4]);
        1291              36 :             if (tmp)
        1292              36 :                 filename.encode(cx, tmp);
        1293                 :         }
        1294                 :
        1295                 :         uint32_t lineno;
        1296              72 :         if (!JS_GetProperty(cx, exnObject, js_lineNumber_str, &roots[5]) ||
        1297              36 :             !ToUint32(cx, roots[5], &lineno))
        1298                 :         {
        1299               0 :             lineno = 0;
        1300                 :         }
        1301                 :
        1302              36 :         reportp = &report;
        1303              36 :         PodZero(&report);
        1304              36 :         report.filename = filename.ptr();
        1305              36 :         report.lineno = (unsigned) lineno;
        1306              36 :         if (str) {
        1307              36 :             if (JSFixedString *fixed = str->ensureFixed(cx))
        1308              36 :                 report.ucmessage = fixed->chars();
        1309                 :         }
        1310                 :     }
        1311                 :
        1312            1700 :     JSAutoByteString bytesStorage;
        1313             850 :     const char *bytes = NULL;
        1314             850 :     if (str)
        1315             841 :         bytes = bytesStorage.encode(cx, str);
        1316             850 :     if (!bytes)
        1317               9 :         bytes = "unknown (can't convert to string)";
        1318                 :
        1319             850 :     if (!reportp) {
        1320                 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
        1321             153 :                              JSMSG_UNCAUGHT_EXCEPTION, bytes);
        1322                 :     } else {
        1323                 :         /* Flag the error as an exception. */
        1324             697 :         reportp->flags |= JSREPORT_EXCEPTION;
        1325                 :
        1326                 :         /* Pass the exception object. */
        1327             697 :         JS_SetPendingException(cx, exn);
        1328             697 :         js_ReportErrorAgain(cx, bytes, reportp);
        1329             697 :         JS_ClearPendingException(cx);
        1330                 :     }
        1331                 :
        1332             850 :     return true;
        1333                 : }
        1334                 :
        1335                 : extern JSObject *
        1336              18 : js_CopyErrorObject(JSContext *cx, JSObject *errobj, JSObject *scope)
        1337                 : {
        1338              18 :     assertSameCompartment(cx, scope);
        1339              18 :     JSExnPrivate *priv = GetExnPrivate(errobj);
        1340                 :
        1341              18 :     uint32_t stackDepth = priv->stackDepth;
        1342              18 :     size_t valueCount = 0;
        1343              45 :     for (uint32_t i = 0; i < stackDepth; i++)
        1344              27 :         valueCount += priv->stackElems[i].argc;
        1345                 :
        1346                 :     size_t size = offsetof(JSExnPrivate, stackElems) +
        1347                 :                   stackDepth * sizeof(JSStackTraceElem) +
        1348              18 :                   valueCount * sizeof(jsval);
        1349                 :
        1350              18 :     JSExnPrivate *copy = (JSExnPrivate *)cx->malloc_(size);
        1351              18 :     if (!copy)
        1352               0 :         return NULL;
        1353                 :
        1354                 :     struct AutoFree {
        1355                 :         JSContext *cx;
        1356                 :         JSExnPrivate *p;
        1357              18 :         ~AutoFree() {
        1358              18 :             if (p) {
        1359               0 :                 cx->free_(p->errorReport);
        1360               0 :                 cx->free_(p);
        1361                 :             }
        1362              18 :         }
        1363              36 :     } autoFree = {cx, copy};
        1364                 :
        1365                 :     // Copy each field. Don't bother copying the stack elements.
        1366              18 :     if (priv->errorReport) {
        1367              18 :         copy->errorReport = CopyErrorReport(cx, priv->errorReport);
        1368              18 :         if (!copy->errorReport)
        1369               0 :             return NULL;
        1370                 :     } else {
        1371               0 :         copy->errorReport = NULL;
        1372                 :     }
        1373              18 :     copy->message.init(priv->message);
        1374              18 :     if (!cx->compartment->wrap(cx, &copy->message))
        1375               0 :         return NULL;
        1376              36 :     JS::Anchor<JSString *> messageAnchor(copy->message);
        1377              18 :     copy->filename.init(priv->filename);
        1378              18 :     if (!cx->compartment->wrap(cx, &copy->filename))
        1379               0 :         return NULL;
        1380              36 :     JS::Anchor<JSString *> filenameAnchor(copy->filename);
        1381              18 :     copy->lineno = priv->lineno;
        1382              18 :     copy->stackDepth = 0;
        1383              18 :     copy->exnType = priv->exnType;
        1384                 :
        1385                 :     // Create the Error object.
        1386              18 :     JSObject *proto = scope->global().getOrCreateCustomErrorPrototype(cx, copy->exnType);
        1387              18 :     if (!proto)
        1388               0 :         return NULL;
        1389              18 :     JSObject *copyobj = NewObjectWithGivenProto(cx, &ErrorClass, proto, NULL);
        1390              18 :     SetExnPrivate(cx, copyobj, copy);
        1391              18 :     autoFree.p = NULL;
        1392              18 :     return copyobj;
        1393                 : }

Generated by: LCOV version 1.7