LCOV - code coverage report
Current view: directory - js/src - jsexn.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 575 457 79.5 %
Date: 2012-06-02 Functions: 29 28 96.6 %

       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                 : 
      69                 : #include "jsinferinlines.h"
      70                 : #include "jsobjinlines.h"
      71                 : 
      72                 : #include "vm/Stack-inl.h"
      73                 : #include "vm/String-inl.h"
      74                 : #include "vm/StringBuffer-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(JSContext *cx, 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        19922066 : typedef struct JSStackTraceElem {
     115                 :     js::HeapPtrString   funName;
     116                 :     size_t              argc;
     117                 :     const char          *filename;
     118                 :     unsigned               ulineno;
     119                 : } JSStackTraceElem;
     120                 : 
     121                 : typedef struct JSExnPrivate {
     122                 :     /* A copy of the JSErrorReport originally generated. */
     123                 :     JSErrorReport       *errorReport;
     124                 :     js::HeapPtrString   message;
     125                 :     js::HeapPtrString   filename;
     126                 :     unsigned               lineno;
     127                 :     size_t              stackDepth;
     128                 :     int                exnType;
     129                 :     JSStackTraceElem    stackElems[1];
     130                 : } JSExnPrivate;
     131                 : 
     132                 : static JSString *
     133                 : StackTraceToString(JSContext *cx, JSExnPrivate *priv);
     134                 : 
     135                 : static JSErrorReport *
     136           11604 : CopyErrorReport(JSContext *cx, JSErrorReport *report)
     137                 : {
     138                 :     /*
     139                 :      * We use a single malloc block to make a deep copy of JSErrorReport with
     140                 :      * the following layout:
     141                 :      *   JSErrorReport
     142                 :      *   array of copies of report->messageArgs
     143                 :      *   jschar array with characters for all messageArgs
     144                 :      *   jschar array with characters for ucmessage
     145                 :      *   jschar array with characters for uclinebuf and uctokenptr
     146                 :      *   char array with characters for linebuf and tokenptr
     147                 :      *   char array with characters for filename
     148                 :      * Such layout together with the properties enforced by the following
     149                 :      * asserts does not need any extra alignment padding.
     150                 :      */
     151                 :     JS_STATIC_ASSERT(sizeof(JSErrorReport) % sizeof(const char *) == 0);
     152                 :     JS_STATIC_ASSERT(sizeof(const char *) % sizeof(jschar) == 0);
     153                 : 
     154                 :     size_t filenameSize;
     155                 :     size_t linebufSize;
     156                 :     size_t uclinebufSize;
     157                 :     size_t ucmessageSize;
     158                 :     size_t i, argsArraySize, argsCopySize, argSize;
     159                 :     size_t mallocSize;
     160                 :     JSErrorReport *copy;
     161                 :     uint8_t *cursor;
     162                 : 
     163                 : #define JS_CHARS_SIZE(jschars) ((js_strlen(jschars) + 1) * sizeof(jschar))
     164                 : 
     165           11604 :     filenameSize = report->filename ? strlen(report->filename) + 1 : 0;
     166           11604 :     linebufSize = report->linebuf ? strlen(report->linebuf) + 1 : 0;
     167           11604 :     uclinebufSize = report->uclinebuf ? JS_CHARS_SIZE(report->uclinebuf) : 0;
     168           11604 :     ucmessageSize = 0;
     169           11604 :     argsArraySize = 0;
     170           11604 :     argsCopySize = 0;
     171           11604 :     if (report->ucmessage) {
     172           11604 :         ucmessageSize = JS_CHARS_SIZE(report->ucmessage);
     173           11604 :         if (report->messageArgs) {
     174           26381 :             for (i = 0; report->messageArgs[i]; ++i)
     175           16525 :                 argsCopySize += JS_CHARS_SIZE(report->messageArgs[i]);
     176                 : 
     177                 :             /* Non-null messageArgs should have at least one non-null arg. */
     178            9856 :             JS_ASSERT(i != 0);
     179            9856 :             argsArraySize = (i + 1) * sizeof(const jschar *);
     180                 :         }
     181                 :     }
     182                 : 
     183                 :     /*
     184                 :      * The mallocSize can not overflow since it represents the sum of the
     185                 :      * sizes of already allocated objects.
     186                 :      */
     187                 :     mallocSize = sizeof(JSErrorReport) + argsArraySize + argsCopySize +
     188           11604 :                  ucmessageSize + uclinebufSize + linebufSize + filenameSize;
     189           11604 :     cursor = (uint8_t *)cx->malloc_(mallocSize);
     190           11604 :     if (!cursor)
     191               0 :         return NULL;
     192                 : 
     193           11604 :     copy = (JSErrorReport *)cursor;
     194           11604 :     memset(cursor, 0, sizeof(JSErrorReport));
     195           11604 :     cursor += sizeof(JSErrorReport);
     196                 : 
     197           11604 :     if (argsArraySize != 0) {
     198            9856 :         copy->messageArgs = (const jschar **)cursor;
     199            9856 :         cursor += argsArraySize;
     200           26381 :         for (i = 0; report->messageArgs[i]; ++i) {
     201           16525 :             copy->messageArgs[i] = (const jschar *)cursor;
     202           16525 :             argSize = JS_CHARS_SIZE(report->messageArgs[i]);
     203           16525 :             js_memcpy(cursor, report->messageArgs[i], argSize);
     204           16525 :             cursor += argSize;
     205                 :         }
     206            9856 :         copy->messageArgs[i] = NULL;
     207            9856 :         JS_ASSERT(cursor == (uint8_t *)copy->messageArgs[0] + argsCopySize);
     208                 :     }
     209                 : 
     210           11604 :     if (report->ucmessage) {
     211           11604 :         copy->ucmessage = (const jschar *)cursor;
     212           11604 :         js_memcpy(cursor, report->ucmessage, ucmessageSize);
     213           11604 :         cursor += ucmessageSize;
     214                 :     }
     215                 : 
     216           11604 :     if (report->uclinebuf) {
     217             602 :         copy->uclinebuf = (const jschar *)cursor;
     218             602 :         js_memcpy(cursor, report->uclinebuf, uclinebufSize);
     219             602 :         cursor += uclinebufSize;
     220             602 :         if (report->uctokenptr) {
     221                 :             copy->uctokenptr = copy->uclinebuf + (report->uctokenptr -
     222             602 :                                                   report->uclinebuf);
     223                 :         }
     224                 :     }
     225                 : 
     226           11604 :     if (report->linebuf) {
     227             602 :         copy->linebuf = (const char *)cursor;
     228             602 :         js_memcpy(cursor, report->linebuf, linebufSize);
     229             602 :         cursor += linebufSize;
     230             602 :         if (report->tokenptr) {
     231                 :             copy->tokenptr = copy->linebuf + (report->tokenptr -
     232             602 :                                               report->linebuf);
     233                 :         }
     234                 :     }
     235                 : 
     236           11604 :     if (report->filename) {
     237           11604 :         copy->filename = (const char *)cursor;
     238           11604 :         js_memcpy(cursor, report->filename, filenameSize);
     239                 :     }
     240           11604 :     JS_ASSERT(cursor + filenameSize == (uint8_t *)copy + mallocSize);
     241                 : 
     242                 :     /* HOLD called by the destination error object. */
     243           11604 :     copy->originPrincipals = report->originPrincipals;
     244                 : 
     245                 :     /* Copy non-pointer members. */
     246           11604 :     copy->lineno = report->lineno;
     247           11604 :     copy->errorNumber = report->errorNumber;
     248                 : 
     249                 :     /* Note that this is before it gets flagged with JSREPORT_EXCEPTION */
     250           11604 :     copy->flags = report->flags;
     251                 : 
     252                 : #undef JS_CHARS_SIZE
     253           11604 :     return copy;
     254                 : }
     255                 : 
     256                 : static HeapValue *
     257           14998 : GetStackTraceValueBuffer(JSExnPrivate *priv)
     258                 : {
     259                 :     /*
     260                 :      * We use extra memory after JSExnPrivateInfo.stackElems to store jsvals
     261                 :      * that helps to produce more informative stack traces. The following
     262                 :      * assert allows us to assume that no gap after stackElems is necessary to
     263                 :      * align the buffer properly.
     264                 :      */
     265                 :     JS_STATIC_ASSERT(sizeof(JSStackTraceElem) % sizeof(jsval) == 0);
     266                 : 
     267           14998 :     return reinterpret_cast<HeapValue *>(priv->stackElems + priv->stackDepth);
     268                 : }
     269                 : 
     270                 : struct SuppressErrorsGuard
     271                 : {
     272                 :     JSContext *cx;
     273                 :     JSErrorReporter prevReporter;
     274                 :     JSExceptionState *prevState;
     275                 : 
     276           13029 :     SuppressErrorsGuard(JSContext *cx)
     277                 :       : cx(cx),
     278           13029 :         prevReporter(JS_SetErrorReporter(cx, NULL)),
     279           26058 :         prevState(JS_SaveExceptionState(cx))
     280           13029 :     {}
     281                 : 
     282           13029 :     ~SuppressErrorsGuard()
     283                 :     {
     284           13029 :         JS_RestoreExceptionState(cx, prevState);
     285           13029 :         JS_SetErrorReporter(cx, prevReporter);
     286           13029 :     }
     287                 : };
     288                 : 
     289                 : struct AppendArg {
     290                 :     Vector<Value> &values;
     291         4361204 :     AppendArg(Vector<Value> &values) : values(values) {}
     292         5322670 :     bool operator()(unsigned, Value *vp) {
     293         5322670 :         return values.append(*vp);
     294                 :     }
     295                 : };
     296                 : 
     297                 : static void
     298                 : SetExnPrivate(JSContext *cx, JSObject *exnObject, JSExnPrivate *priv);
     299                 : 
     300                 : static bool
     301           13029 : InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message,
     302                 :                JSString *filename, unsigned lineno, JSErrorReport *report, int exnType)
     303                 : {
     304           13029 :     JS_ASSERT(exnObject->isError());
     305           13029 :     JS_ASSERT(!exnObject->getPrivate());
     306                 : 
     307           13029 :     JSCheckAccessOp checkAccess = cx->runtime->securityCallbacks->checkObjectAccess;
     308                 : 
     309           26058 :     Vector<JSStackTraceElem> frames(cx);
     310           26058 :     Vector<Value> values(cx);
     311                 :     {
     312           26058 :         SuppressErrorsGuard seg(cx);
     313         4383688 :         for (FrameRegsIter i(cx); !i.done(); ++i) {
     314                 :             /*
     315                 :              * An exception object stores stack values from 'fp' which may be
     316                 :              * in a different compartment from 'exnObject'. Engine compartment
     317                 :              * invariants require such values to be wrapped. A simpler solution
     318                 :              * is to just cut off the backtrace at compartment boundaries.
     319                 :              * Also, avoid exposing values from different security principals.
     320                 :              */
     321         4374042 :             StackFrame *fp = i.fp();
     322         4374042 :             if (fp->compartment() != cx->compartment)
     323            3383 :                 break;
     324         4370659 :             if (checkAccess && fp->isNonEvalFunctionFrame()) {
     325         4268897 :                 Value v = NullValue();
     326         4268897 :                 jsid callerid = ATOM_TO_JSID(cx->runtime->atomState.callerAtom);
     327         4268897 :                 if (!checkAccess(cx, &fp->callee(), callerid, JSACC_READ, &v))
     328               0 :                     break;
     329                 :             }
     330                 : 
     331         4370659 :             if (!frames.growBy(1))
     332               0 :                 return false;
     333         4370659 :             JSStackTraceElem &frame = frames.back();
     334         4370659 :             if (fp->isNonEvalFunctionFrame()) {
     335         4361204 :                 frame.funName.init(fp->fun()->atom ? fp->fun()->atom : cx->runtime->emptyString);
     336         4361204 :                 frame.argc = fp->numActualArgs();
     337         4361204 :                 if (!fp->forEachCanonicalActualArg(AppendArg(values)))
     338               0 :                     return false;
     339                 :             } else {
     340            9455 :                 frame.funName.init(NULL);
     341            9455 :                 frame.argc = 0;
     342                 :             }
     343         4370659 :             if (fp->isScriptFrame()) {
     344         4370659 :                 frame.filename = fp->script()->filename;
     345         4370659 :                 frame.ulineno = PCToLineNumber(fp->script(), i.pc());
     346                 :             } else {
     347               0 :                 frame.ulineno = 0;
     348               0 :                 frame.filename = NULL;
     349                 :             }
     350                 :         }
     351                 :     }
     352                 : 
     353                 :     /* Do not need overflow check: the vm stack is already bigger. */
     354                 :     JS_STATIC_ASSERT(sizeof(JSStackTraceElem) <= sizeof(StackFrame));
     355                 : 
     356                 :     size_t nbytes = offsetof(JSExnPrivate, stackElems) +
     357           13029 :                     frames.length() * sizeof(JSStackTraceElem) +
     358           13029 :                     values.length() * sizeof(HeapValue);
     359                 : 
     360           13029 :     JSExnPrivate *priv = (JSExnPrivate *)cx->malloc_(nbytes);
     361           13029 :     if (!priv)
     362               0 :         return false;
     363                 : 
     364                 :     /* Initialize to zero so that write barriers don't witness undefined values. */
     365           13029 :     memset(priv, 0, nbytes);
     366                 : 
     367           13029 :     if (report) {
     368                 :         /*
     369                 :          * Construct a new copy of the error report struct. We can't use the
     370                 :          * error report struct that was passed in, because it's allocated on
     371                 :          * the stack, and also because it may point to transient data in the
     372                 :          * TokenStream.
     373                 :          */
     374           11586 :         priv->errorReport = CopyErrorReport(cx, report);
     375           11586 :         if (!priv->errorReport) {
     376               0 :             cx->free_(priv);
     377               0 :             return false;
     378                 :         }
     379                 :     } else {
     380            1443 :         priv->errorReport = NULL;
     381                 :     }
     382                 : 
     383           13029 :     priv->message.init(message);
     384           13029 :     priv->filename.init(filename);
     385           13029 :     priv->lineno = lineno;
     386           13029 :     priv->stackDepth = frames.length();
     387           13029 :     priv->exnType = exnType;
     388                 : 
     389           13029 :     JSStackTraceElem *framesDest = priv->stackElems;
     390           13029 :     HeapValue *valuesDest = reinterpret_cast<HeapValue *>(framesDest + frames.length());
     391           13029 :     JS_ASSERT(valuesDest == GetStackTraceValueBuffer(priv));
     392                 : 
     393           13029 :     PodCopy(framesDest, frames.begin(), frames.length());
     394         5335699 :     for (size_t i = 0; i < values.length(); ++i)
     395         5322670 :         valuesDest[i].init(cx->compartment, values[i]);
     396                 : 
     397           13029 :     SetExnPrivate(cx, exnObject, priv);
     398           13029 :     return true;
     399                 : }
     400                 : 
     401                 : static inline JSExnPrivate *
     402          157029 : GetExnPrivate(JSObject *obj)
     403                 : {
     404          157029 :     JS_ASSERT(obj->isError());
     405          157029 :     return (JSExnPrivate *) obj->getPrivate();
     406                 : }
     407                 : 
     408                 : static void
     409           87351 : exn_trace(JSTracer *trc, JSObject *obj)
     410                 : {
     411                 :     JSExnPrivate *priv;
     412                 :     JSStackTraceElem *elem;
     413                 :     size_t vcount, i;
     414                 :     HeapValue *vp;
     415                 : 
     416           87351 :     priv = GetExnPrivate(obj);
     417           87351 :     if (priv) {
     418            1914 :         if (priv->message)
     419            1912 :             MarkString(trc, &priv->message, "exception message");
     420            1914 :         if (priv->filename)
     421            1914 :             MarkString(trc, &priv->filename, "exception filename");
     422                 : 
     423            1914 :         elem = priv->stackElems;
     424            5787 :         for (vcount = i = 0; i != priv->stackDepth; ++i, ++elem) {
     425            3873 :             if (elem->funName)
     426            1899 :                 MarkString(trc, &elem->funName, "stack trace function name");
     427            3873 :             if (IS_GC_MARKING_TRACER(trc) && elem->filename)
     428            3305 :                 js_MarkScriptFilename(elem->filename);
     429            3873 :             vcount += elem->argc;
     430                 :         }
     431            1914 :         vp = GetStackTraceValueBuffer(priv);
     432            2444 :         for (i = 0; i != vcount; ++i, ++vp)
     433             530 :             MarkValue(trc, vp, "stack trace argument");
     434                 :     }
     435           87351 : }
     436                 : 
     437                 : /* NB: An error object's private must be set through this function. */
     438                 : static void
     439           13047 : SetExnPrivate(JSContext *cx, JSObject *exnObject, JSExnPrivate *priv)
     440                 : {
     441           13047 :     JS_ASSERT(!exnObject->getPrivate());
     442           13047 :     JS_ASSERT(exnObject->isError());
     443           13047 :     if (JSErrorReport *report = priv->errorReport) {
     444           11604 :         if (JSPrincipals *prin = report->originPrincipals)
     445            1857 :             JS_HoldPrincipals(prin);
     446                 :     }
     447           13047 :     exnObject->setPrivate(priv);
     448           13047 : }
     449                 : 
     450                 : static void
     451           48783 : exn_finalize(JSContext *cx, JSObject *obj)
     452                 : {
     453           48783 :     if (JSExnPrivate *priv = GetExnPrivate(obj)) {
     454           13047 :         if (JSErrorReport *report = priv->errorReport) {
     455                 :             /* HOLD called by SetExnPrivate. */
     456           11604 :             if (JSPrincipals *prin = report->originPrincipals)
     457            1857 :                 JS_DropPrincipals(cx->runtime, prin);
     458           11604 :             cx->free_(report);
     459                 :         }
     460           13047 :         cx->free_(priv);
     461                 :     }
     462           48783 : }
     463                 : 
     464                 : static JSBool
     465           19635 : exn_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
     466                 :             JSObject **objp)
     467                 : {
     468                 :     JSExnPrivate *priv;
     469                 :     JSString *str;
     470                 :     JSAtom *atom;
     471                 :     JSString *stack;
     472                 :     const char *prop;
     473                 :     jsval v;
     474                 :     unsigned attrs;
     475                 : 
     476           19635 :     *objp = NULL;
     477           19635 :     priv = GetExnPrivate(obj);
     478           19635 :     if (priv && JSID_IS_ATOM(id)) {
     479           15127 :         str = JSID_TO_STRING(id);
     480                 : 
     481           15127 :         atom = cx->runtime->atomState.messageAtom;
     482           15127 :         if (str == atom) {
     483            2336 :             prop = js_message_str;
     484                 : 
     485                 :             /*
     486                 :              * Per ES5 15.11.1.1, if Error is called with no argument or with
     487                 :              * undefined as the argument, it returns an Error object with no
     488                 :              * own message property.
     489                 :              */
     490            2336 :             if (!priv->message)
     491               0 :                 return true;
     492                 : 
     493            2336 :             v = STRING_TO_JSVAL(priv->message);
     494            2336 :             attrs = 0;
     495            2336 :             goto define;
     496                 :         }
     497                 : 
     498           12791 :         atom = cx->runtime->atomState.fileNameAtom;
     499           12791 :         if (str == atom) {
     500              57 :             prop = js_fileName_str;
     501              57 :             v = STRING_TO_JSVAL(priv->filename);
     502              57 :             attrs = JSPROP_ENUMERATE;
     503              57 :             goto define;
     504                 :         }
     505                 : 
     506           12734 :         atom = cx->runtime->atomState.lineNumberAtom;
     507           12734 :         if (str == atom) {
     508            1231 :             prop = js_lineNumber_str;
     509            1231 :             v = INT_TO_JSVAL(priv->lineno);
     510            1231 :             attrs = JSPROP_ENUMERATE;
     511            1231 :             goto define;
     512                 :         }
     513                 : 
     514           11503 :         atom = cx->runtime->atomState.stackAtom;
     515           11503 :         if (str == atom) {
     516              55 :             stack = StackTraceToString(cx, priv);
     517              55 :             if (!stack)
     518               0 :                 return false;
     519                 : 
     520              55 :             prop = js_stack_str;
     521              55 :             v = STRING_TO_JSVAL(stack);
     522              55 :             attrs = JSPROP_ENUMERATE;
     523              55 :             goto define;
     524                 :         }
     525                 :     }
     526           15956 :     return true;
     527                 : 
     528                 :   define:
     529            3679 :     if (!JS_DefineProperty(cx, obj, prop, v, NULL, NULL, attrs))
     530               0 :         return false;
     531            3679 :     *objp = obj;
     532            3679 :     return true;
     533                 : }
     534                 : 
     535                 : JSErrorReport *
     536            1864 : js_ErrorFromException(JSContext *cx, jsval exn)
     537                 : {
     538                 :     JSObject *obj;
     539                 :     JSExnPrivate *priv;
     540                 : 
     541            1864 :     if (JSVAL_IS_PRIMITIVE(exn))
     542             520 :         return NULL;
     543            1344 :     obj = JSVAL_TO_OBJECT(exn);
     544            1344 :     if (!obj->isError())
     545             102 :         return NULL;
     546            1242 :     priv = GetExnPrivate(obj);
     547            1242 :     if (!priv)
     548               0 :         return NULL;
     549            1242 :     return priv->errorReport;
     550                 : }
     551                 : 
     552                 : static JSString *
     553             106 : ValueToShortSource(JSContext *cx, const Value &v)
     554                 : {
     555                 :     JSString *str;
     556                 : 
     557                 :     /* Avoid toSource bloat and fallibility for object types. */
     558             106 :     if (!v.isObject())
     559              80 :         return js_ValueToSource(cx, v);
     560                 : 
     561              26 :     JSObject *obj = &v.toObject();
     562              52 :     AutoCompartment ac(cx, obj);
     563              26 :     if (!ac.enter())
     564               0 :         return NULL;
     565                 : 
     566              26 :     if (obj->isFunction()) {
     567                 :         /*
     568                 :          * XXX Avoid function decompilation bloat for now.
     569                 :          */
     570               1 :         str = JS_GetFunctionId(obj->toFunction());
     571               1 :         if (!str && !(str = js_ValueToSource(cx, v))) {
     572                 :             /*
     573                 :              * Continue to soldier on if the function couldn't be
     574                 :              * converted into a string.
     575                 :              */
     576               0 :             JS_ClearPendingException(cx);
     577               0 :             str = JS_NewStringCopyZ(cx, "[unknown function]");
     578                 :         }
     579                 :     } else {
     580                 :         /*
     581                 :          * XXX Avoid toString on objects, it takes too long and uses too much
     582                 :          * memory, for too many classes (see Mozilla bug 166743).
     583                 :          */
     584                 :         char buf[100];
     585              25 :         JS_snprintf(buf, sizeof buf, "[object %s]", obj->getClass()->name);
     586              25 :         str = JS_NewStringCopyZ(cx, buf);
     587                 :     }
     588                 : 
     589              26 :     ac.leave();
     590                 : 
     591              26 :     if (!str || !cx->compartment->wrap(cx, &str))
     592               0 :         return NULL;
     593              26 :     return str;
     594                 : }
     595                 : 
     596                 : static JSString *
     597              55 : StackTraceToString(JSContext *cx, JSExnPrivate *priv)
     598                 : {
     599                 :     jschar *stackbuf;
     600                 :     size_t stacklen, stackmax;
     601                 :     JSStackTraceElem *elem, *endElem;
     602                 :     HeapValue *values;
     603                 :     size_t i;
     604                 :     JSString *str;
     605                 :     const char *cp;
     606                 :     char ulnbuf[11];
     607                 : 
     608                 :     /* After this point, failing control flow must goto bad. */
     609              55 :     stackbuf = NULL;
     610              55 :     stacklen = stackmax = 0;
     611                 : 
     612                 : /* Limit the stackbuf length to a reasonable value to avoid overflow checks. */
     613                 : #define STACK_LENGTH_LIMIT JS_BIT(20)
     614                 : 
     615                 : #define APPEND_CHAR_TO_STACK(c)                                               \
     616                 :     JS_BEGIN_MACRO                                                            \
     617                 :         if (stacklen == stackmax) {                                           \
     618                 :             void *ptr_;                                                       \
     619                 :             if (stackmax >= STACK_LENGTH_LIMIT)                               \
     620                 :                 goto done;                                                    \
     621                 :             stackmax = stackmax ? 2 * stackmax : 64;                          \
     622                 :             ptr_ = cx->realloc_(stackbuf, (stackmax+1) * sizeof(jschar));      \
     623                 :             if (!ptr_)                                                        \
     624                 :                 goto bad;                                                     \
     625                 :             stackbuf = (jschar *) ptr_;                                       \
     626                 :         }                                                                     \
     627                 :         stackbuf[stacklen++] = (c);                                           \
     628                 :     JS_END_MACRO
     629                 : 
     630                 : #define APPEND_STRING_TO_STACK(str)                                           \
     631                 :     JS_BEGIN_MACRO                                                            \
     632                 :         JSString *str_ = str;                                                 \
     633                 :         size_t length_ = str_->length();                                      \
     634                 :         const jschar *chars_ = str_->getChars(cx);                            \
     635                 :         if (!chars_)                                                          \
     636                 :             goto bad;                                                         \
     637                 :                                                                               \
     638                 :         if (length_ > stackmax - stacklen) {                                  \
     639                 :             void *ptr_;                                                       \
     640                 :             if (stackmax >= STACK_LENGTH_LIMIT ||                             \
     641                 :                 length_ >= STACK_LENGTH_LIMIT - stacklen) {                   \
     642                 :                 goto done;                                                    \
     643                 :             }                                                                 \
     644                 :             stackmax = RoundUpPow2(stacklen + length_);                       \
     645                 :             ptr_ = cx->realloc_(stackbuf, (stackmax+1) * sizeof(jschar));     \
     646                 :             if (!ptr_)                                                        \
     647                 :                 goto bad;                                                     \
     648                 :             stackbuf = (jschar *) ptr_;                                       \
     649                 :         }                                                                     \
     650                 :         js_strncpy(stackbuf + stacklen, chars_, length_);                     \
     651                 :         stacklen += length_;                                                  \
     652                 :     JS_END_MACRO
     653                 : 
     654              55 :     values = GetStackTraceValueBuffer(priv);
     655              55 :     elem = priv->stackElems;
     656             365 :     for (endElem = elem + priv->stackDepth; elem != endElem; elem++) {
     657             310 :         if (elem->funName) {
     658             301 :             APPEND_STRING_TO_STACK(elem->funName);
     659             301 :             APPEND_CHAR_TO_STACK('(');
     660             407 :             for (i = 0; i != elem->argc; i++, values++) {
     661             106 :                 if (i > 0)
     662              42 :                     APPEND_CHAR_TO_STACK(',');
     663             106 :                 str = ValueToShortSource(cx, *values);
     664             106 :                 if (!str)
     665               0 :                     goto bad;
     666             106 :                 APPEND_STRING_TO_STACK(str);
     667                 :             }
     668             301 :             APPEND_CHAR_TO_STACK(')');
     669                 :         }
     670             310 :         APPEND_CHAR_TO_STACK('@');
     671             310 :         if (elem->filename) {
     672           15184 :             for (cp = elem->filename; *cp; cp++)
     673           14874 :                 APPEND_CHAR_TO_STACK(*cp);
     674                 :         }
     675             310 :         APPEND_CHAR_TO_STACK(':');
     676             310 :         JS_snprintf(ulnbuf, sizeof ulnbuf, "%u", elem->ulineno);
     677            1306 :         for (cp = ulnbuf; *cp; cp++)
     678             996 :             APPEND_CHAR_TO_STACK(*cp);
     679             310 :         APPEND_CHAR_TO_STACK('\n');
     680                 :     }
     681                 : #undef APPEND_CHAR_TO_STACK
     682                 : #undef APPEND_STRING_TO_STACK
     683                 : #undef STACK_LENGTH_LIMIT
     684                 : 
     685                 :   done:
     686              55 :     if (stacklen == 0) {
     687              18 :         JS_ASSERT(!stackbuf);
     688              18 :         return cx->runtime->emptyString;
     689                 :     }
     690              37 :     if (stacklen < stackmax) {
     691                 :         /*
     692                 :          * Realloc can fail when shrinking on some FreeBSD versions, so
     693                 :          * don't use JS_realloc here; simply let the oversized allocation
     694                 :          * be owned by the string in that rare case.
     695                 :          */
     696              37 :         void *shrunk = cx->realloc_(stackbuf, (stacklen+1) * sizeof(jschar));
     697              37 :         if (shrunk)
     698              37 :             stackbuf = (jschar *) shrunk;
     699                 :     }
     700                 : 
     701              37 :     stackbuf[stacklen] = 0;
     702              37 :     str = js_NewString(cx, stackbuf, stacklen);
     703              37 :     if (str)
     704              37 :         return str;
     705                 : 
     706                 :   bad:
     707               0 :     if (stackbuf)
     708               0 :         cx->free_(stackbuf);
     709               0 :     return NULL;
     710                 : }
     711                 : 
     712                 : /* XXXbe Consolidate the ugly truth that we don't treat filename as UTF-8
     713                 :          with these two functions. */
     714                 : static JSString *
     715            1443 : FilenameToString(JSContext *cx, const char *filename)
     716                 : {
     717            1443 :     return JS_NewStringCopyZ(cx, filename);
     718                 : }
     719                 : 
     720                 : static JSBool
     721            1443 : Exception(JSContext *cx, unsigned argc, Value *vp)
     722                 : {
     723            1443 :     CallArgs args = CallArgsFromVp(argc, vp);
     724                 : 
     725                 :     /*
     726                 :      * ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when
     727                 :      * called as functions, without operator new.  But as we do not give
     728                 :      * each constructor a distinct JSClass, whose .name member is used by
     729                 :      * NewNativeClassInstance to find the class prototype, we must get the
     730                 :      * class prototype ourselves.
     731                 :      */
     732                 :     Value protov;
     733            1443 :     if (!args.callee().getProperty(cx, cx->runtime->atomState.classPrototypeAtom, &protov))
     734               0 :         return false;
     735                 : 
     736            1443 :     if (!protov.isObject()) {
     737               0 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_PROTOTYPE, "Error");
     738               0 :         return false;
     739                 :     }
     740                 : 
     741            1443 :     JSObject *errProto = &protov.toObject();
     742            1443 :     JSObject *obj = NewObjectWithGivenProto(cx, &ErrorClass, errProto, NULL);
     743            1443 :     if (!obj)
     744               0 :         return false;
     745                 : 
     746                 :     /* Set the 'message' property. */
     747                 :     JSString *message;
     748            1443 :     if (args.hasDefined(0)) {
     749             464 :         message = ToString(cx, args[0]);
     750             464 :         if (!message)
     751               0 :             return false;
     752             464 :         args[0].setString(message);
     753                 :     } else {
     754             979 :         message = NULL;
     755                 :     }
     756                 : 
     757                 :     /* Find the scripted caller. */
     758            1443 :     FrameRegsIter iter(cx);
     759            2886 :     while (!iter.done() && !iter.fp()->isScriptFrame())
     760               0 :         ++iter;
     761                 : 
     762                 :     /* Set the 'fileName' property. */
     763                 :     JSString *filename;
     764            1443 :     if (args.length() > 1) {
     765               0 :         filename = ToString(cx, args[1]);
     766               0 :         if (!filename)
     767               0 :             return false;
     768               0 :         args[1].setString(filename);
     769                 :     } else {
     770            1443 :         if (!iter.done()) {
     771            1443 :             filename = FilenameToString(cx, iter.fp()->script()->filename);
     772            1443 :             if (!filename)
     773               0 :                 return false;
     774                 :         } else {
     775               0 :             filename = cx->runtime->emptyString;
     776                 :         }
     777                 :     }
     778                 : 
     779                 :     /* Set the 'lineNumber' property. */
     780                 :     uint32_t lineno;
     781            1443 :     if (args.length() > 2) {
     782               0 :         if (!ToUint32(cx, args[2], &lineno))
     783               0 :             return false;
     784                 :     } else {
     785            1443 :         lineno = iter.done() ? 0 : PCToLineNumber(iter.fp()->script(), iter.pc());
     786                 :     }
     787                 : 
     788            1443 :     int exnType = args.callee().toFunction()->getExtendedSlot(0).toInt32();
     789            1443 :     if (!InitExnPrivate(cx, obj, message, filename, lineno, NULL, exnType))
     790               0 :         return false;
     791                 : 
     792            1443 :     args.rval().setObject(*obj);
     793            1443 :     return true;
     794                 : }
     795                 : 
     796                 : /* ES5 15.11.4.4 (NB: with subsequent errata). */
     797                 : static JSBool
     798            7833 : exn_toString(JSContext *cx, unsigned argc, Value *vp)
     799                 : {
     800            7833 :     JS_CHECK_RECURSION(cx, return false);
     801            7815 :     CallArgs args = CallArgsFromVp(argc, vp);
     802                 : 
     803                 :     /* Step 2. */
     804            7815 :     if (!args.thisv().isObject()) {
     805               0 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_PROTOTYPE, "Error");
     806               0 :         return false;
     807                 :     }
     808                 : 
     809                 :     /* Step 1. */
     810            7815 :     JSObject &obj = args.thisv().toObject();
     811                 : 
     812                 :     /* Step 3. */
     813                 :     Value nameVal;
     814            7815 :     if (!obj.getProperty(cx, cx->runtime->atomState.nameAtom, &nameVal))
     815               0 :         return false;
     816                 : 
     817                 :     /* Step 4. */
     818                 :     JSString *name;
     819            7815 :     if (nameVal.isUndefined()) {
     820               9 :         name = CLASS_ATOM(cx, Error);
     821                 :     } else {
     822            7806 :         name = ToString(cx, nameVal);
     823            7806 :         if (!name)
     824            5286 :             return false;
     825                 :     }
     826                 : 
     827                 :     /* Step 5. */
     828                 :     Value msgVal;
     829            2529 :     if (!obj.getProperty(cx, cx->runtime->atomState.messageAtom, &msgVal))
     830               0 :         return false;
     831                 : 
     832                 :     /* Step 6. */
     833                 :     JSString *message;
     834            2529 :     if (msgVal.isUndefined()) {
     835               9 :         message = cx->runtime->emptyString;
     836                 :     } else {
     837            2520 :         message = ToString(cx, msgVal);
     838            2520 :         if (!message)
     839               0 :             return false;
     840                 :     }
     841                 : 
     842                 :     /* Step 7. */
     843            2529 :     if (name->empty() && message->empty()) {
     844               9 :         args.rval().setString(CLASS_ATOM(cx, Error));
     845               9 :         return true;
     846                 :     }
     847                 : 
     848                 :     /* Step 8. */
     849            2520 :     if (name->empty()) {
     850               9 :         args.rval().setString(message);
     851               9 :         return true;
     852                 :     }
     853                 : 
     854                 :     /* Step 9. */
     855            2511 :     if (message->empty()) {
     856              27 :         args.rval().setString(name);
     857              27 :         return true;
     858                 :     }
     859                 : 
     860                 :     /* Step 10. */
     861            4968 :     StringBuffer sb(cx);
     862            2484 :     if (!sb.append(name) || !sb.append(": ") || !sb.append(message))
     863               0 :         return false;
     864                 : 
     865            2484 :     JSString *str = sb.finishString();
     866            2484 :     if (!str)
     867               0 :         return false;
     868            2484 :     args.rval().setString(str);
     869            2484 :     return true;
     870                 : }
     871                 : 
     872                 : #if JS_HAS_TOSOURCE
     873                 : /*
     874                 :  * Return a string that may eval to something similar to the original object.
     875                 :  */
     876                 : static JSBool
     877               0 : exn_toSource(JSContext *cx, unsigned argc, Value *vp)
     878                 : {
     879               0 :     JS_CHECK_RECURSION(cx, return false);
     880               0 :     CallArgs args = CallArgsFromVp(argc, vp);
     881                 : 
     882               0 :     JSObject *obj = ToObject(cx, &args.thisv());
     883               0 :     if (!obj)
     884               0 :         return false;
     885                 : 
     886                 :     Value nameVal;
     887                 :     JSString *name;
     888               0 :     if (!obj->getProperty(cx, cx->runtime->atomState.nameAtom, &nameVal) ||
     889                 :         !(name = ToString(cx, nameVal)))
     890                 :     {
     891               0 :         return false;
     892                 :     }
     893                 : 
     894                 :     Value messageVal;
     895                 :     JSString *message;
     896               0 :     if (!obj->getProperty(cx, cx->runtime->atomState.messageAtom, &messageVal) ||
     897                 :         !(message = js_ValueToSource(cx, messageVal)))
     898                 :     {
     899               0 :         return false;
     900                 :     }
     901                 : 
     902                 :     Value filenameVal;
     903                 :     JSString *filename;
     904               0 :     if (!obj->getProperty(cx, cx->runtime->atomState.fileNameAtom, &filenameVal) ||
     905                 :         !(filename = js_ValueToSource(cx, filenameVal)))
     906                 :     {
     907               0 :         return false;
     908                 :     }
     909                 : 
     910                 :     Value linenoVal;
     911                 :     uint32_t lineno;
     912               0 :     if (!obj->getProperty(cx, cx->runtime->atomState.lineNumberAtom, &linenoVal) ||
     913               0 :         !ToUint32(cx, linenoVal, &lineno))
     914                 :     {
     915               0 :         return false;
     916                 :     }
     917                 : 
     918               0 :     StringBuffer sb(cx);
     919               0 :     if (!sb.append("(new ") || !sb.append(name) || !sb.append("("))
     920               0 :         return false;
     921                 : 
     922               0 :     if (!sb.append(message))
     923               0 :         return false;
     924                 : 
     925               0 :     if (!filename->empty()) {
     926               0 :         if (!sb.append(", ") || !sb.append(filename))
     927               0 :             return false;
     928                 :     }
     929               0 :     if (lineno != 0) {
     930                 :         /* We have a line, but no filename, add empty string */
     931               0 :         if (filename->empty() && !sb.append(", \"\""))
     932               0 :                 return false;
     933                 : 
     934               0 :         JSString *linenumber = ToString(cx, linenoVal);
     935               0 :         if (!linenumber)
     936               0 :             return false;
     937               0 :         if (!sb.append(", ") || !sb.append(linenumber))
     938               0 :             return false;
     939                 :     }
     940                 : 
     941               0 :     if (!sb.append("))"))
     942               0 :         return false;
     943                 : 
     944               0 :     JSString *str = sb.finishString();
     945               0 :     if (!str)
     946               0 :         return false;
     947               0 :     args.rval().setString(str);
     948               0 :     return true;
     949                 : }
     950                 : #endif
     951                 : 
     952                 : static JSFunctionSpec exception_methods[] = {
     953                 : #if JS_HAS_TOSOURCE
     954                 :     JS_FN(js_toSource_str,   exn_toSource,           0,0),
     955                 : #endif
     956                 :     JS_FN(js_toString_str,   exn_toString,           0,0),
     957                 :     JS_FS_END
     958                 : };
     959                 : 
     960                 : /* JSProto_ ordering for exceptions shall match JSEXN_ constants. */
     961                 : JS_STATIC_ASSERT(JSEXN_ERR == 0);
     962                 : JS_STATIC_ASSERT(JSProto_Error + JSEXN_INTERNALERR  == JSProto_InternalError);
     963                 : JS_STATIC_ASSERT(JSProto_Error + JSEXN_EVALERR      == JSProto_EvalError);
     964                 : JS_STATIC_ASSERT(JSProto_Error + JSEXN_RANGEERR     == JSProto_RangeError);
     965                 : JS_STATIC_ASSERT(JSProto_Error + JSEXN_REFERENCEERR == JSProto_ReferenceError);
     966                 : JS_STATIC_ASSERT(JSProto_Error + JSEXN_SYNTAXERR    == JSProto_SyntaxError);
     967                 : JS_STATIC_ASSERT(JSProto_Error + JSEXN_TYPEERR      == JSProto_TypeError);
     968                 : JS_STATIC_ASSERT(JSProto_Error + JSEXN_URIERR       == JSProto_URIError);
     969                 : 
     970                 : static JSObject *
     971           35752 : InitErrorClass(JSContext *cx, GlobalObject *global, int type, JSObject &proto)
     972                 : {
     973           35752 :     JSProtoKey key = GetExceptionProtoKey(type);
     974           35752 :     JSAtom *name = cx->runtime->atomState.classAtoms[key];
     975           35752 :     JSObject *errorProto = global->createBlankPrototypeInheriting(cx, &ErrorClass, proto);
     976           35752 :     if (!errorProto)
     977               0 :         return NULL;
     978                 : 
     979           35752 :     Value empty = StringValue(cx->runtime->emptyString);
     980           35752 :     jsid nameId = ATOM_TO_JSID(cx->runtime->atomState.nameAtom);
     981           35752 :     jsid messageId = ATOM_TO_JSID(cx->runtime->atomState.messageAtom);
     982           35752 :     jsid fileNameId = ATOM_TO_JSID(cx->runtime->atomState.fileNameAtom);
     983           35752 :     jsid lineNumberId = ATOM_TO_JSID(cx->runtime->atomState.lineNumberAtom);
     984          178760 :     if (!DefineNativeProperty(cx, errorProto, nameId, StringValue(name),
     985           35752 :                               JS_PropertyStub, JS_StrictPropertyStub, 0, 0, 0) ||
     986                 :         !DefineNativeProperty(cx, errorProto, messageId, empty,
     987           35752 :                               JS_PropertyStub, JS_StrictPropertyStub, 0, 0, 0) ||
     988                 :         !DefineNativeProperty(cx, errorProto, fileNameId, empty,
     989           35752 :                               JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE, 0, 0) ||
     990                 :         !DefineNativeProperty(cx, errorProto, lineNumberId, Int32Value(0),
     991           35752 :                               JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE, 0, 0))
     992                 :     {
     993               0 :         return NULL;
     994                 :     }
     995                 : 
     996                 :     /* Create the corresponding constructor. */
     997                 :     JSFunction *ctor = global->createConstructor(cx, Exception, &ErrorClass, name, 1,
     998           35752 :                                                  JSFunction::ExtendedFinalizeKind);
     999           35752 :     if (!ctor)
    1000               0 :         return NULL;
    1001           35752 :     ctor->setExtendedSlot(0, Int32Value(int32_t(type)));
    1002                 : 
    1003           35752 :     if (!LinkConstructorAndPrototype(cx, ctor, errorProto))
    1004               0 :         return NULL;
    1005                 : 
    1006           35752 :     if (!DefineConstructorAndPrototype(cx, global, key, ctor, errorProto))
    1007               0 :         return NULL;
    1008                 : 
    1009           35752 :     JS_ASSERT(!errorProto->getPrivate());
    1010                 : 
    1011           35752 :     return errorProto;
    1012                 : }
    1013                 : 
    1014                 : JSObject *
    1015            4469 : js_InitExceptionClasses(JSContext *cx, JSObject *obj)
    1016                 : {
    1017            4469 :     JS_ASSERT(obj->isGlobal());
    1018            4469 :     JS_ASSERT(obj->isNative());
    1019                 : 
    1020            4469 :     GlobalObject *global = &obj->asGlobal();
    1021                 : 
    1022            4469 :     JSObject *objectProto = global->getOrCreateObjectPrototype(cx);
    1023            4469 :     if (!objectProto)
    1024               0 :         return NULL;
    1025                 : 
    1026                 :     /* Initialize the base Error class first. */
    1027            4469 :     JSObject *errorProto = InitErrorClass(cx, global, JSEXN_ERR, *objectProto);
    1028            4469 :     if (!errorProto)
    1029               0 :         return NULL;
    1030                 : 
    1031                 :     /* |Error.prototype| alone has method properties. */
    1032            4469 :     if (!DefinePropertiesAndBrand(cx, errorProto, NULL, exception_methods))
    1033               0 :         return NULL;
    1034                 : 
    1035                 :     /* Define all remaining *Error constructors. */
    1036           35752 :     for (int i = JSEXN_ERR + 1; i < JSEXN_LIMIT; i++) {
    1037           31283 :         if (!InitErrorClass(cx, global, i, *errorProto))
    1038               0 :             return NULL;
    1039                 :     }
    1040                 : 
    1041            4469 :     return errorProto;
    1042                 : }
    1043                 : 
    1044                 : const JSErrorFormatString*
    1045           29243 : js_GetLocalizedErrorMessage(JSContext* cx, void *userRef, const char *locale,
    1046                 :                             const unsigned errorNumber)
    1047                 : {
    1048           29243 :     const JSErrorFormatString *errorString = NULL;
    1049                 : 
    1050           29243 :     if (cx->localeCallbacks && cx->localeCallbacks->localeGetErrorMessage) {
    1051                 :         errorString = cx->localeCallbacks
    1052               0 :                         ->localeGetErrorMessage(userRef, locale, errorNumber);
    1053                 :     }
    1054           29243 :     if (!errorString)
    1055           29243 :         errorString = js_GetErrorMessage(userRef, locale, errorNumber);
    1056           29243 :     return errorString;
    1057                 : }
    1058                 : 
    1059                 : #if defined ( DEBUG_mccabe ) && defined ( PRINTNAMES )
    1060                 : /* For use below... get character strings for error name and exception name */
    1061                 : static struct exnname { char *name; char *exception; } errortoexnname[] = {
    1062                 : #define MSG_DEF(name, number, count, exception, format) \
    1063                 :     {#name, #exception},
    1064                 : #include "js.msg"
    1065                 : #undef MSG_DEF
    1066                 : };
    1067                 : #endif /* DEBUG */
    1068                 : 
    1069                 : JSBool
    1070           19020 : js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp,
    1071                 :                     JSErrorCallback callback, void *userRef)
    1072                 : {
    1073                 :     JSErrNum errorNumber;
    1074                 :     const JSErrorFormatString *errorString;
    1075                 :     JSExnType exn;
    1076                 :     jsval tv[4];
    1077                 :     JSBool ok;
    1078                 :     JSObject *errProto, *errObject;
    1079                 :     JSString *messageStr, *filenameStr;
    1080                 : 
    1081                 :     /*
    1082                 :      * Tell our caller to report immediately if this report is just a warning.
    1083                 :      */
    1084           19020 :     JS_ASSERT(reportp);
    1085           19020 :     if (JSREPORT_IS_WARNING(reportp->flags))
    1086            7337 :         return JS_FALSE;
    1087                 : 
    1088                 :     /* Find the exception index associated with this error. */
    1089           11683 :     errorNumber = (JSErrNum) reportp->errorNumber;
    1090           11683 :     if (!callback || callback == js_GetErrorMessage)
    1091           10870 :         errorString = js_GetLocalizedErrorMessage(cx, NULL, NULL, errorNumber);
    1092                 :     else
    1093             813 :         errorString = callback(userRef, NULL, errorNumber);
    1094           11683 :     exn = errorString ? (JSExnType) errorString->exnType : JSEXN_NONE;
    1095           11683 :     JS_ASSERT(exn < JSEXN_LIMIT);
    1096                 : 
    1097                 : #if defined( DEBUG_mccabe ) && defined ( PRINTNAMES )
    1098                 :     /* Print the error name and the associated exception name to stderr */
    1099                 :     fprintf(stderr, "%s\t%s\n",
    1100                 :             errortoexnname[errorNumber].name,
    1101                 :             errortoexnname[errorNumber].exception);
    1102                 : #endif
    1103                 : 
    1104                 :     /*
    1105                 :      * Return false (no exception raised) if no exception is associated
    1106                 :      * with the given error number.
    1107                 :      */
    1108           11683 :     if (exn == JSEXN_NONE)
    1109               0 :         return JS_FALSE;
    1110                 : 
    1111                 :     /*
    1112                 :      * Prevent runaway recursion, via cx->generatingError.  If an out-of-memory
    1113                 :      * error occurs, no exception object will be created, but we don't assume
    1114                 :      * that OOM is the only kind of error that subroutines of this function
    1115                 :      * called below might raise.
    1116                 :      */
    1117           11683 :     if (cx->generatingError)
    1118              97 :         return JS_FALSE;
    1119                 : 
    1120                 :     MUST_FLOW_THROUGH("out");
    1121           11586 :     cx->generatingError = JS_TRUE;
    1122                 : 
    1123                 :     /* Protect the newly-created strings below from nesting GCs. */
    1124           11586 :     PodArrayZero(tv);
    1125           23172 :     AutoArrayRooter tvr(cx, ArrayLength(tv), tv);
    1126                 : 
    1127                 :     /*
    1128                 :      * Try to get an appropriate prototype by looking up the corresponding
    1129                 :      * exception constructor name in the scope chain of the current context's
    1130                 :      * top stack frame, or in the global object if no frame is active.
    1131                 :      */
    1132           11586 :     ok = js_GetClassPrototype(cx, NULL, GetExceptionProtoKey(exn), &errProto);
    1133           11586 :     if (!ok)
    1134               0 :         goto out;
    1135           11586 :     tv[0] = OBJECT_TO_JSVAL(errProto);
    1136                 : 
    1137           11586 :     errObject = NewObjectWithGivenProto(cx, &ErrorClass, errProto, NULL);
    1138           11586 :     if (!errObject) {
    1139               0 :         ok = JS_FALSE;
    1140               0 :         goto out;
    1141                 :     }
    1142           11586 :     tv[1] = OBJECT_TO_JSVAL(errObject);
    1143                 : 
    1144           11586 :     messageStr = JS_NewStringCopyZ(cx, message);
    1145           11586 :     if (!messageStr) {
    1146               0 :         ok = JS_FALSE;
    1147               0 :         goto out;
    1148                 :     }
    1149           11586 :     tv[2] = STRING_TO_JSVAL(messageStr);
    1150                 : 
    1151           11586 :     filenameStr = JS_NewStringCopyZ(cx, reportp->filename);
    1152           11586 :     if (!filenameStr) {
    1153               0 :         ok = JS_FALSE;
    1154               0 :         goto out;
    1155                 :     }
    1156           11586 :     tv[3] = STRING_TO_JSVAL(filenameStr);
    1157                 : 
    1158                 :     ok = InitExnPrivate(cx, errObject, messageStr, filenameStr,
    1159           11586 :                         reportp->lineno, reportp, exn);
    1160           11586 :     if (!ok)
    1161               0 :         goto out;
    1162                 : 
    1163           11586 :     JS_SetPendingException(cx, OBJECT_TO_JSVAL(errObject));
    1164                 : 
    1165                 :     /* Flag the error report passed in to indicate an exception was raised. */
    1166           11586 :     reportp->flags |= JSREPORT_EXCEPTION;
    1167                 : 
    1168                 : out:
    1169           11586 :     cx->generatingError = JS_FALSE;
    1170           11586 :     return ok;
    1171                 : }
    1172                 : 
    1173                 : JSBool
    1174           12971 : js_ReportUncaughtException(JSContext *cx)
    1175                 : {
    1176                 :     jsval exn;
    1177                 :     JSObject *exnObject;
    1178                 :     jsval roots[5];
    1179                 :     JSErrorReport *reportp, report;
    1180                 :     JSString *str;
    1181                 :     const char *bytes;
    1182                 : 
    1183           12971 :     if (!JS_IsExceptionPending(cx))
    1184           11412 :         return true;
    1185                 : 
    1186            1559 :     if (!JS_GetPendingException(cx, &exn))
    1187               0 :         return false;
    1188                 : 
    1189            1559 :     PodArrayZero(roots);
    1190            3118 :     AutoArrayRooter tvr(cx, ArrayLength(roots), roots);
    1191                 : 
    1192                 :     /*
    1193                 :      * Because ToString below could error and an exception object could become
    1194                 :      * unrooted, we must root exnObject.  Later, if exnObject is non-null, we
    1195                 :      * need to root other intermediates, so allocate an operand stack segment
    1196                 :      * to protect all of these values.
    1197                 :      */
    1198            1559 :     if (JSVAL_IS_PRIMITIVE(exn)) {
    1199             475 :         exnObject = NULL;
    1200                 :     } else {
    1201            1084 :         exnObject = JSVAL_TO_OBJECT(exn);
    1202            1084 :         roots[0] = exn;
    1203                 :     }
    1204                 : 
    1205            1559 :     JS_ClearPendingException(cx);
    1206            1559 :     reportp = js_ErrorFromException(cx, exn);
    1207                 : 
    1208                 :     /* XXX L10N angels cry once again. see also everywhere else */
    1209            1559 :     str = ToString(cx, exn);
    1210            3118 :     JSAutoByteString bytesStorage;
    1211            1559 :     if (!str) {
    1212               9 :         bytes = "unknown (can't convert to string)";
    1213                 :     } else {
    1214            1550 :         roots[1] = StringValue(str);
    1215            1550 :         if (!bytesStorage.encode(cx, str))
    1216               0 :             return false;
    1217            1550 :         bytes = bytesStorage.ptr();
    1218                 :     }
    1219                 : 
    1220            3118 :     JSAutoByteString filename;
    1221            1559 :     if (!reportp && exnObject && exnObject->isError()) {
    1222              11 :         if (!JS_GetProperty(cx, exnObject, js_message_str, &roots[2]))
    1223               0 :             return false;
    1224              11 :         if (JSVAL_IS_STRING(roots[2])) {
    1225              11 :             bytesStorage.clear();
    1226              11 :             if (!bytesStorage.encode(cx, str))
    1227               0 :                 return false;
    1228              11 :             bytes = bytesStorage.ptr();
    1229                 :         }
    1230                 : 
    1231              11 :         if (!JS_GetProperty(cx, exnObject, js_fileName_str, &roots[3]))
    1232               0 :             return false;
    1233              11 :         str = ToString(cx, roots[3]);
    1234              11 :         if (!str || !filename.encode(cx, str))
    1235               0 :             return false;
    1236                 : 
    1237              11 :         if (!JS_GetProperty(cx, exnObject, js_lineNumber_str, &roots[4]))
    1238               0 :             return false;
    1239                 :         uint32_t lineno;
    1240              11 :         if (!ToUint32(cx, roots[4], &lineno))
    1241               0 :             return false;
    1242                 : 
    1243              11 :         reportp = &report;
    1244              11 :         PodZero(&report);
    1245              11 :         report.filename = filename.ptr();
    1246              11 :         report.lineno = (unsigned) lineno;
    1247              11 :         if (JSVAL_IS_STRING(roots[2])) {
    1248              11 :             JSFixedString *fixed = JSVAL_TO_STRING(roots[2])->ensureFixed(cx);
    1249              11 :             if (!fixed)
    1250               0 :                 return false;
    1251              11 :             report.ucmessage = fixed->chars();
    1252                 :         }
    1253                 :     }
    1254                 : 
    1255            1559 :     if (!reportp) {
    1256                 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
    1257             575 :                              JSMSG_UNCAUGHT_EXCEPTION, bytes);
    1258                 :     } else {
    1259                 :         /* Flag the error as an exception. */
    1260             984 :         reportp->flags |= JSREPORT_EXCEPTION;
    1261                 : 
    1262                 :         /* Pass the exception object. */
    1263             984 :         JS_SetPendingException(cx, exn);
    1264             984 :         js_ReportErrorAgain(cx, bytes, reportp);
    1265             984 :         JS_ClearPendingException(cx);
    1266                 :     }
    1267                 : 
    1268            1559 :     return true;
    1269                 : }
    1270                 : 
    1271                 : extern JSObject *
    1272              18 : js_CopyErrorObject(JSContext *cx, JSObject *errobj, JSObject *scope)
    1273                 : {
    1274              18 :     assertSameCompartment(cx, scope);
    1275              18 :     JSExnPrivate *priv = GetExnPrivate(errobj);
    1276                 : 
    1277              18 :     uint32_t stackDepth = priv->stackDepth;
    1278              18 :     size_t valueCount = 0;
    1279              18 :     for (uint32_t i = 0; i < stackDepth; i++)
    1280               0 :         valueCount += priv->stackElems[i].argc;
    1281                 : 
    1282                 :     size_t size = offsetof(JSExnPrivate, stackElems) +
    1283                 :                   stackDepth * sizeof(JSStackTraceElem) +
    1284              18 :                   valueCount * sizeof(jsval);
    1285                 : 
    1286              18 :     JSExnPrivate *copy = (JSExnPrivate *)cx->malloc_(size);
    1287              18 :     if (!copy)
    1288               0 :         return NULL;
    1289                 : 
    1290                 :     struct AutoFree {
    1291                 :         JSContext *cx;
    1292                 :         JSExnPrivate *p;
    1293              18 :         ~AutoFree() {
    1294              18 :             if (p) {
    1295               0 :                 cx->free_(p->errorReport);
    1296               0 :                 cx->free_(p);
    1297                 :             }
    1298              18 :         }
    1299              36 :     } autoFree = {cx, copy};
    1300                 : 
    1301                 :     // Copy each field. Don't bother copying the stack elements.
    1302              18 :     if (priv->errorReport) {
    1303              18 :         copy->errorReport = CopyErrorReport(cx, priv->errorReport);
    1304              18 :         if (!copy->errorReport)
    1305               0 :             return NULL;
    1306                 :     } else {
    1307               0 :         copy->errorReport = NULL;
    1308                 :     }
    1309              18 :     copy->message.init(priv->message);
    1310              18 :     if (!cx->compartment->wrap(cx, &copy->message))
    1311               0 :         return NULL;
    1312              36 :     JS::Anchor<JSString *> messageAnchor(copy->message);
    1313              18 :     copy->filename.init(priv->filename);
    1314              18 :     if (!cx->compartment->wrap(cx, &copy->filename))
    1315               0 :         return NULL;
    1316              36 :     JS::Anchor<JSString *> filenameAnchor(copy->filename);
    1317              18 :     copy->lineno = priv->lineno;
    1318              18 :     copy->stackDepth = 0;
    1319              18 :     copy->exnType = priv->exnType;
    1320                 : 
    1321                 :     // Create the Error object.
    1322              18 :     JSObject *proto = scope->global().getOrCreateCustomErrorPrototype(cx, copy->exnType);
    1323              18 :     if (!proto)
    1324               0 :         return NULL;
    1325              18 :     JSObject *copyobj = NewObjectWithGivenProto(cx, &ErrorClass, proto, NULL);
    1326              18 :     SetExnPrivate(cx, copyobj, copy);
    1327              18 :     autoFree.p = NULL;
    1328              18 :     return copyobj;
    1329                 : }

Generated by: LCOV version 1.7