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