PageRenderTime 674ms CodeModel.GetById 121ms app.highlight 429ms RepoModel.GetById 108ms app.codeStats 1ms

/js/lib/Socket.IO-node/support/expresso/deps/jscoverage/js/jsexn.cpp

http://github.com/onedayitwillmake/RealtimeMultiplayerNodeJs
C++ | 1376 lines | 1005 code | 157 blank | 214 comment | 202 complexity | 6a55f62b7471d33996ea5349db8fde33 MD5 | raw file
   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
  45#include "jsstddef.h"
  46#include <stdlib.h>
  47#include <string.h>
  48#include "jstypes.h"
  49#include "jsbit.h"
  50#include "jsutil.h" /* Added by JSIFY */
  51#include "jsprf.h"
  52#include "jsapi.h"
  53#include "jscntxt.h"
  54#include "jsversion.h"
  55#include "jsdbgapi.h"
  56#include "jsexn.h"
  57#include "jsfun.h"
  58#include "jsinterp.h"
  59#include "jsnum.h"
  60#include "jsobj.h"
  61#include "jsopcode.h"
  62#include "jsscope.h"
  63#include "jsscript.h"
  64#include "jsstaticcheck.h"
  65
  66/* Forward declarations for js_ErrorClass's initializer. */
  67static JSBool
  68Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
  69
  70static void
  71exn_finalize(JSContext *cx, JSObject *obj);
  72
  73static void
  74exn_trace(JSTracer *trc, JSObject *obj);
  75
  76static void
  77exn_finalize(JSContext *cx, JSObject *obj);
  78
  79static JSBool
  80exn_enumerate(JSContext *cx, JSObject *obj);
  81
  82static JSBool
  83exn_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
  84            JSObject **objp);
  85
  86JSClass js_ErrorClass = {
  87    js_Error_str,
  88    JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_MARK_IS_TRACE |
  89    JSCLASS_HAS_CACHED_PROTO(JSProto_Error),
  90    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
  91    exn_enumerate,    (JSResolveOp)exn_resolve, JS_ConvertStub, exn_finalize,
  92    NULL,             NULL,             NULL,             Exception,
  93    NULL,             NULL,             JS_CLASS_TRACE(exn_trace), NULL
  94};
  95
  96typedef struct JSStackTraceElem {
  97    JSString            *funName;
  98    size_t              argc;
  99    const char          *filename;
 100    uintN               ulineno;
 101} JSStackTraceElem;
 102
 103typedef struct JSExnPrivate {
 104    /* A copy of the JSErrorReport originally generated. */
 105    JSErrorReport       *errorReport;
 106    JSString            *message;
 107    JSString            *filename;
 108    uintN               lineno;
 109    size_t              stackDepth;
 110    JSStackTraceElem    stackElems[1];
 111} JSExnPrivate;
 112
 113static JSString *
 114StackTraceToString(JSContext *cx, JSExnPrivate *priv);
 115
 116static JSErrorReport *
 117CopyErrorReport(JSContext *cx, JSErrorReport *report)
 118{
 119    /*
 120     * We use a single malloc block to make a deep copy of JSErrorReport with
 121     * the following layout:
 122     *   JSErrorReport
 123     *   array of copies of report->messageArgs
 124     *   jschar array with characters for all messageArgs
 125     *   jschar array with characters for ucmessage
 126     *   jschar array with characters for uclinebuf and uctokenptr
 127     *   char array with characters for linebuf and tokenptr
 128     *   char array with characters for filename
 129     * Such layout together with the properties enforced by the following
 130     * asserts does not need any extra alignment padding.
 131     */
 132    JS_STATIC_ASSERT(sizeof(JSErrorReport) % sizeof(const char *) == 0);
 133    JS_STATIC_ASSERT(sizeof(const char *) % sizeof(jschar) == 0);
 134
 135    size_t filenameSize;
 136    size_t linebufSize;
 137    size_t uclinebufSize;
 138    size_t ucmessageSize;
 139    size_t i, argsArraySize, argsCopySize, argSize;
 140    size_t mallocSize;
 141    JSErrorReport *copy;
 142    uint8 *cursor;
 143
 144#define JS_CHARS_SIZE(jschars) ((js_strlen(jschars) + 1) * sizeof(jschar))
 145
 146    filenameSize = report->filename ? strlen(report->filename) + 1 : 0;
 147    linebufSize = report->linebuf ? strlen(report->linebuf) + 1 : 0;
 148    uclinebufSize = report->uclinebuf ? JS_CHARS_SIZE(report->uclinebuf) : 0;
 149    ucmessageSize = 0;
 150    argsArraySize = 0;
 151    argsCopySize = 0;
 152    if (report->ucmessage) {
 153        ucmessageSize = JS_CHARS_SIZE(report->ucmessage);
 154        if (report->messageArgs) {
 155            for (i = 0; report->messageArgs[i]; ++i)
 156                argsCopySize += JS_CHARS_SIZE(report->messageArgs[i]);
 157
 158            /* Non-null messageArgs should have at least one non-null arg. */
 159            JS_ASSERT(i != 0);
 160            argsArraySize = (i + 1) * sizeof(const jschar *);
 161        }
 162    }
 163
 164    /*
 165     * The mallocSize can not overflow since it represents the sum of the
 166     * sizes of already allocated objects.
 167     */
 168    mallocSize = sizeof(JSErrorReport) + argsArraySize + argsCopySize +
 169                 ucmessageSize + uclinebufSize + linebufSize + filenameSize;
 170    cursor = (uint8 *)JS_malloc(cx, mallocSize);
 171    if (!cursor)
 172        return NULL;
 173
 174    copy = (JSErrorReport *)cursor;
 175    memset(cursor, 0, sizeof(JSErrorReport));
 176    cursor += sizeof(JSErrorReport);
 177
 178    if (argsArraySize != 0) {
 179        copy->messageArgs = (const jschar **)cursor;
 180        cursor += argsArraySize;
 181        for (i = 0; report->messageArgs[i]; ++i) {
 182            copy->messageArgs[i] = (const jschar *)cursor;
 183            argSize = JS_CHARS_SIZE(report->messageArgs[i]);
 184            memcpy(cursor, report->messageArgs[i], argSize);
 185            cursor += argSize;
 186        }
 187        copy->messageArgs[i] = NULL;
 188        JS_ASSERT(cursor == (uint8 *)copy->messageArgs[0] + argsCopySize);
 189    }
 190
 191    if (report->ucmessage) {
 192        copy->ucmessage = (const jschar *)cursor;
 193        memcpy(cursor, report->ucmessage, ucmessageSize);
 194        cursor += ucmessageSize;
 195    }
 196
 197    if (report->uclinebuf) {
 198        copy->uclinebuf = (const jschar *)cursor;
 199        memcpy(cursor, report->uclinebuf, uclinebufSize);
 200        cursor += uclinebufSize;
 201        if (report->uctokenptr) {
 202            copy->uctokenptr = copy->uclinebuf + (report->uctokenptr -
 203                                                  report->uclinebuf);
 204        }
 205    }
 206
 207    if (report->linebuf) {
 208        copy->linebuf = (const char *)cursor;
 209        memcpy(cursor, report->linebuf, linebufSize);
 210        cursor += linebufSize;
 211        if (report->tokenptr) {
 212            copy->tokenptr = copy->linebuf + (report->tokenptr -
 213                                              report->linebuf);
 214        }
 215    }
 216
 217    if (report->filename) {
 218        copy->filename = (const char *)cursor;
 219        memcpy(cursor, report->filename, filenameSize);
 220    }
 221    JS_ASSERT(cursor + filenameSize == (uint8 *)copy + mallocSize);
 222
 223    /* Copy non-pointer members. */
 224    copy->lineno = report->lineno;
 225    copy->errorNumber = report->errorNumber;
 226
 227    /* Note that this is before it gets flagged with JSREPORT_EXCEPTION */
 228    copy->flags = report->flags;
 229
 230#undef JS_CHARS_SIZE
 231    return copy;
 232}
 233
 234static jsval *
 235GetStackTraceValueBuffer(JSExnPrivate *priv)
 236{
 237    /*
 238     * We use extra memory after JSExnPrivateInfo.stackElems to store jsvals
 239     * that helps to produce more informative stack traces. The following
 240     * assert allows us to assume that no gap after stackElems is necessary to
 241     * align the buffer properly.
 242     */
 243    JS_STATIC_ASSERT(sizeof(JSStackTraceElem) % sizeof(jsval) == 0);
 244
 245    return (jsval *)(priv->stackElems + priv->stackDepth);
 246}
 247
 248static JSBool
 249InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message,
 250               JSString *filename, uintN lineno, JSErrorReport *report)
 251{
 252    JSSecurityCallbacks *callbacks;
 253    JSCheckAccessOp checkAccess;
 254    JSErrorReporter older;
 255    JSExceptionState *state;
 256    jsval callerid, v;
 257    JSStackFrame *fp, *fpstop;
 258    size_t stackDepth, valueCount, size;
 259    JSBool overflow;
 260    JSExnPrivate *priv;
 261    JSStackTraceElem *elem;
 262    jsval *values;
 263
 264    JS_ASSERT(OBJ_GET_CLASS(cx, exnObject) == &js_ErrorClass);
 265
 266    /*
 267     * Prepare stack trace data.
 268     *
 269     * Set aside any error reporter for cx and save its exception state
 270     * so we can suppress any checkAccess failures.  Such failures should stop
 271     * the backtrace procedure, not result in a failure of this constructor.
 272     */
 273    callbacks = JS_GetSecurityCallbacks(cx);
 274    checkAccess = callbacks
 275                  ? callbacks->checkObjectAccess
 276                  : NULL;
 277    older = JS_SetErrorReporter(cx, NULL);
 278    state = JS_SaveExceptionState(cx);
 279
 280    callerid = ATOM_KEY(cx->runtime->atomState.callerAtom);
 281    stackDepth = 0;
 282    valueCount = 0;
 283    for (fp = cx->fp; fp; fp = fp->down) {
 284        if (fp->fun && fp->argv) {
 285            v = JSVAL_NULL;
 286            if (checkAccess &&
 287                !checkAccess(cx, fp->callee, callerid, JSACC_READ, &v)) {
 288                break;
 289            }
 290            valueCount += fp->argc;
 291        }
 292        ++stackDepth;
 293    }
 294    JS_RestoreExceptionState(cx, state);
 295    JS_SetErrorReporter(cx, older);
 296    fpstop = fp;
 297
 298    size = offsetof(JSExnPrivate, stackElems);
 299    overflow = (stackDepth > ((size_t)-1 - size) / sizeof(JSStackTraceElem));
 300    size += stackDepth * sizeof(JSStackTraceElem);
 301    overflow |= (valueCount > ((size_t)-1 - size) / sizeof(jsval));
 302    size += valueCount * sizeof(jsval);
 303    if (overflow) {
 304        js_ReportAllocationOverflow(cx);
 305        return JS_FALSE;
 306    }
 307    priv = (JSExnPrivate *)JS_malloc(cx, size);
 308    if (!priv)
 309        return JS_FALSE;
 310
 311    /*
 312     * We initialize errorReport with a copy of report after setting the
 313     * private slot, to prevent GC accessing a junk value we clear the field
 314     * here.
 315     */
 316    priv->errorReport = NULL;
 317    priv->message = message;
 318    priv->filename = filename;
 319    priv->lineno = lineno;
 320    priv->stackDepth = stackDepth;
 321
 322    values = GetStackTraceValueBuffer(priv);
 323    elem = priv->stackElems;
 324    for (fp = cx->fp; fp != fpstop; fp = fp->down) {
 325        if (!fp->fun) {
 326            elem->funName = NULL;
 327            elem->argc = 0;
 328        } else {
 329            elem->funName = fp->fun->atom
 330                            ? ATOM_TO_STRING(fp->fun->atom)
 331                            : cx->runtime->emptyString;
 332            elem->argc = fp->argc;
 333            memcpy(values, fp->argv, fp->argc * sizeof(jsval));
 334            values += fp->argc;
 335        }
 336        elem->ulineno = 0;
 337        elem->filename = NULL;
 338        if (fp->script) {
 339            elem->filename = fp->script->filename;
 340            if (fp->regs)
 341                elem->ulineno = js_FramePCToLineNumber(cx, fp);
 342        }
 343        ++elem;
 344    }
 345    JS_ASSERT(priv->stackElems + stackDepth == elem);
 346    JS_ASSERT(GetStackTraceValueBuffer(priv) + valueCount == values);
 347
 348    STOBJ_SET_SLOT(exnObject, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(priv));
 349
 350    if (report) {
 351        /*
 352         * Construct a new copy of the error report struct. We can't use the
 353         * error report struct that was passed in, because it's allocated on
 354         * the stack, and also because it may point to transient data in the
 355         * JSTokenStream.
 356         */
 357        priv->errorReport = CopyErrorReport(cx, report);
 358        if (!priv->errorReport) {
 359            /* The finalizer realeases priv since it is in the private slot. */
 360            return JS_FALSE;
 361        }
 362    }
 363
 364    return JS_TRUE;
 365}
 366
 367static JSExnPrivate *
 368GetExnPrivate(JSContext *cx, JSObject *obj)
 369{
 370    jsval privateValue;
 371    JSExnPrivate *priv;
 372
 373    JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_ErrorClass);
 374    privateValue = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
 375    if (JSVAL_IS_VOID(privateValue))
 376        return NULL;
 377    priv = (JSExnPrivate *)JSVAL_TO_PRIVATE(privateValue);
 378    JS_ASSERT(priv);
 379    return priv;
 380}
 381
 382static void
 383exn_trace(JSTracer *trc, JSObject *obj)
 384{
 385    JSExnPrivate *priv;
 386    JSStackTraceElem *elem;
 387    size_t vcount, i;
 388    jsval *vp, v;
 389
 390    priv = GetExnPrivate(trc->context, obj);
 391    if (priv) {
 392        if (priv->message)
 393            JS_CALL_STRING_TRACER(trc, priv->message, "exception message");
 394        if (priv->filename)
 395            JS_CALL_STRING_TRACER(trc, priv->filename, "exception filename");
 396
 397        elem = priv->stackElems;
 398        for (vcount = i = 0; i != priv->stackDepth; ++i, ++elem) {
 399            if (elem->funName) {
 400                JS_CALL_STRING_TRACER(trc, elem->funName,
 401                                      "stack trace function name");
 402            }
 403            if (IS_GC_MARKING_TRACER(trc) && elem->filename)
 404                js_MarkScriptFilename(elem->filename);
 405            vcount += elem->argc;
 406        }
 407        vp = GetStackTraceValueBuffer(priv);
 408        for (i = 0; i != vcount; ++i, ++vp) {
 409            v = *vp;
 410            JS_CALL_VALUE_TRACER(trc, v, "stack trace argument");
 411        }
 412    }
 413}
 414
 415static void
 416exn_finalize(JSContext *cx, JSObject *obj)
 417{
 418    JSExnPrivate *priv;
 419
 420    priv = GetExnPrivate(cx, obj);
 421    if (priv) {
 422        if (priv->errorReport)
 423            JS_free(cx, priv->errorReport);
 424        JS_free(cx, priv);
 425    }
 426}
 427
 428static JSBool
 429exn_enumerate(JSContext *cx, JSObject *obj)
 430{
 431    JSAtomState *atomState;
 432    uintN i;
 433    JSAtom *atom;
 434    JSObject *pobj;
 435    JSProperty *prop;
 436
 437    JS_STATIC_ASSERT(sizeof(JSAtomState) <= (size_t)(uint16)-1);
 438    static const uint16 offsets[] = {
 439        (uint16)offsetof(JSAtomState, messageAtom),
 440        (uint16)offsetof(JSAtomState, fileNameAtom),
 441        (uint16)offsetof(JSAtomState, lineNumberAtom),
 442        (uint16)offsetof(JSAtomState, stackAtom),
 443    };
 444
 445    atomState = &cx->runtime->atomState;
 446    for (i = 0; i != JS_ARRAY_LENGTH(offsets); ++i) {
 447        atom = *(JSAtom **)((uint8 *)atomState + offsets[i]);
 448        if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop))
 449            return JS_FALSE;
 450        if (prop)
 451            OBJ_DROP_PROPERTY(cx, pobj, prop);
 452    }
 453    return JS_TRUE;
 454}
 455
 456static JSBool
 457exn_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
 458            JSObject **objp)
 459{
 460    JSExnPrivate *priv;
 461    JSString *str;
 462    JSAtom *atom;
 463    JSString *stack;
 464    const char *prop;
 465    jsval v;
 466
 467    *objp = NULL;
 468    priv = GetExnPrivate(cx, obj);
 469    if (priv && JSVAL_IS_STRING(id)) {
 470        str = JSVAL_TO_STRING(id);
 471
 472        atom = cx->runtime->atomState.messageAtom;
 473        if (str == ATOM_TO_STRING(atom)) {
 474            prop = js_message_str;
 475            v = STRING_TO_JSVAL(priv->message);
 476            goto define;
 477        }
 478
 479        atom = cx->runtime->atomState.fileNameAtom;
 480        if (str == ATOM_TO_STRING(atom)) {
 481            prop = js_fileName_str;
 482            v = STRING_TO_JSVAL(priv->filename);
 483            goto define;
 484        }
 485
 486        atom = cx->runtime->atomState.lineNumberAtom;
 487        if (str == ATOM_TO_STRING(atom)) {
 488            prop = js_lineNumber_str;
 489            v = INT_TO_JSVAL(priv->lineno);
 490            goto define;
 491        }
 492
 493        atom = cx->runtime->atomState.stackAtom;
 494        if (str == ATOM_TO_STRING(atom)) {
 495            stack = StackTraceToString(cx, priv);
 496            if (!stack)
 497                return JS_FALSE;
 498
 499            /* Allow to GC all things that were used to build stack trace. */
 500            priv->stackDepth = 0;
 501            prop = js_stack_str;
 502            v = STRING_TO_JSVAL(stack);
 503            goto define;
 504        }
 505    }
 506    return JS_TRUE;
 507
 508  define:
 509    if (!JS_DefineProperty(cx, obj, prop, v, NULL, NULL, JSPROP_ENUMERATE))
 510        return JS_FALSE;
 511    *objp = obj;
 512    return JS_TRUE;
 513}
 514
 515JSErrorReport *
 516js_ErrorFromException(JSContext *cx, jsval exn)
 517{
 518    JSObject *obj;
 519    JSExnPrivate *priv;
 520
 521    if (JSVAL_IS_PRIMITIVE(exn))
 522        return NULL;
 523    obj = JSVAL_TO_OBJECT(exn);
 524    if (OBJ_GET_CLASS(cx, obj) != &js_ErrorClass)
 525        return NULL;
 526    priv = GetExnPrivate(cx, obj);
 527    if (!priv)
 528        return NULL;
 529    return priv->errorReport;
 530}
 531
 532struct JSExnSpec {
 533    int protoIndex;
 534    const char *name;
 535    JSProtoKey key;
 536    JSNative native;
 537};
 538
 539/*
 540 * All *Error constructors share the same JSClass, js_ErrorClass.  But each
 541 * constructor function for an *Error class must have a distinct native 'call'
 542 * function pointer, in order for instanceof to work properly across multiple
 543 * standard class sets.  See jsfun.c:fun_hasInstance.
 544 */
 545#define MAKE_EXCEPTION_CTOR(name)                                             \
 546static JSBool                                                                 \
 547name(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)      \
 548{                                                                             \
 549    return Exception(cx, obj, argc, argv, rval);                              \
 550}
 551
 552MAKE_EXCEPTION_CTOR(Error)
 553MAKE_EXCEPTION_CTOR(InternalError)
 554MAKE_EXCEPTION_CTOR(EvalError)
 555MAKE_EXCEPTION_CTOR(RangeError)
 556MAKE_EXCEPTION_CTOR(ReferenceError)
 557MAKE_EXCEPTION_CTOR(SyntaxError)
 558MAKE_EXCEPTION_CTOR(TypeError)
 559MAKE_EXCEPTION_CTOR(URIError)
 560
 561#undef MAKE_EXCEPTION_CTOR
 562
 563static struct JSExnSpec exceptions[] = {
 564    {JSEXN_NONE, js_Error_str,          JSProto_Error,          Error},
 565    {JSEXN_ERR,  js_InternalError_str,  JSProto_InternalError,  InternalError},
 566    {JSEXN_ERR,  js_EvalError_str,      JSProto_EvalError,      EvalError},
 567    {JSEXN_ERR,  js_RangeError_str,     JSProto_RangeError,     RangeError},
 568    {JSEXN_ERR,  js_ReferenceError_str, JSProto_ReferenceError, ReferenceError},
 569    {JSEXN_ERR,  js_SyntaxError_str,    JSProto_SyntaxError,    SyntaxError},
 570    {JSEXN_ERR,  js_TypeError_str,      JSProto_TypeError,      TypeError},
 571    {JSEXN_ERR,  js_URIError_str,       JSProto_URIError,       URIError},
 572    {0,          NULL,                  JSProto_Null,           NULL}
 573};
 574
 575static JSString *
 576ValueToShortSource(JSContext *cx, jsval v)
 577{
 578    JSString *str;
 579
 580    /* Avoid toSource bloat and fallibility for object types. */
 581    if (JSVAL_IS_PRIMITIVE(v)) {
 582        str = js_ValueToSource(cx, v);
 583    } else if (VALUE_IS_FUNCTION(cx, v)) {
 584        /*
 585         * XXX Avoid function decompilation bloat for now.
 586         */
 587        str = JS_GetFunctionId(JS_ValueToFunction(cx, v));
 588        if (!str && !(str = js_ValueToSource(cx, v))) {
 589            /*
 590             * Continue to soldier on if the function couldn't be
 591             * converted into a string.
 592             */
 593            JS_ClearPendingException(cx);
 594            str = JS_NewStringCopyZ(cx, "[unknown function]");
 595        }
 596    } else {
 597        /*
 598         * XXX Avoid toString on objects, it takes too long and uses too much
 599         * memory, for too many classes (see Mozilla bug 166743).
 600         */
 601        char buf[100];
 602        JS_snprintf(buf, sizeof buf, "[object %s]",
 603                    OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v))->name);
 604        str = JS_NewStringCopyZ(cx, buf);
 605    }
 606    return str;
 607}
 608
 609static JSString *
 610StackTraceToString(JSContext *cx, JSExnPrivate *priv)
 611{
 612    jschar *stackbuf;
 613    size_t stacklen, stackmax;
 614    JSStackTraceElem *elem, *endElem;
 615    jsval *values;
 616    size_t i;
 617    JSString *str;
 618    const char *cp;
 619    char ulnbuf[11];
 620
 621    /* After this point, failing control flow must goto bad. */
 622    stackbuf = NULL;
 623    stacklen = stackmax = 0;
 624
 625/* Limit the stackbuf length to a reasonable value to avoid overflow checks. */
 626#define STACK_LENGTH_LIMIT JS_BIT(20)
 627
 628#define APPEND_CHAR_TO_STACK(c)                                               \
 629    JS_BEGIN_MACRO                                                            \
 630        if (stacklen == stackmax) {                                           \
 631            void *ptr_;                                                       \
 632            if (stackmax >= STACK_LENGTH_LIMIT)                               \
 633                goto done;                                                    \
 634            stackmax = stackmax ? 2 * stackmax : 64;                          \
 635            ptr_ = JS_realloc(cx, stackbuf, (stackmax+1) * sizeof(jschar));   \
 636            if (!ptr_)                                                        \
 637                goto bad;                                                     \
 638            stackbuf = (jschar *) ptr_;                                       \
 639        }                                                                     \
 640        stackbuf[stacklen++] = (c);                                           \
 641    JS_END_MACRO
 642
 643#define APPEND_STRING_TO_STACK(str)                                           \
 644    JS_BEGIN_MACRO                                                            \
 645        JSString *str_ = str;                                                 \
 646        jschar *chars_;                                                       \
 647        size_t length_;                                                       \
 648                                                                              \
 649        JSSTRING_CHARS_AND_LENGTH(str_, chars_, length_);                     \
 650        if (length_ > stackmax - stacklen) {                                  \
 651            void *ptr_;                                                       \
 652            if (stackmax >= STACK_LENGTH_LIMIT ||                             \
 653                length_ >= STACK_LENGTH_LIMIT - stacklen) {                   \
 654                goto done;                                                    \
 655            }                                                                 \
 656            stackmax = JS_BIT(JS_CeilingLog2(stacklen + length_));            \
 657            ptr_ = JS_realloc(cx, stackbuf, (stackmax+1) * sizeof(jschar));   \
 658            if (!ptr_)                                                        \
 659                goto bad;                                                     \
 660            stackbuf = (jschar *) ptr_;                                       \
 661        }                                                                     \
 662        js_strncpy(stackbuf + stacklen, chars_, length_);                     \
 663        stacklen += length_;                                                  \
 664    JS_END_MACRO
 665
 666    values = GetStackTraceValueBuffer(priv);
 667    elem = priv->stackElems;
 668    for (endElem = elem + priv->stackDepth; elem != endElem; elem++) {
 669        if (elem->funName) {
 670            APPEND_STRING_TO_STACK(elem->funName);
 671            APPEND_CHAR_TO_STACK('(');
 672            for (i = 0; i != elem->argc; i++, values++) {
 673                if (i > 0)
 674                    APPEND_CHAR_TO_STACK(',');
 675                str = ValueToShortSource(cx, *values);
 676                if (!str)
 677                    goto bad;
 678                APPEND_STRING_TO_STACK(str);
 679            }
 680            APPEND_CHAR_TO_STACK(')');
 681        }
 682        APPEND_CHAR_TO_STACK('@');
 683        if (elem->filename) {
 684            for (cp = elem->filename; *cp; cp++)
 685                APPEND_CHAR_TO_STACK(*cp);
 686        }
 687        APPEND_CHAR_TO_STACK(':');
 688        JS_snprintf(ulnbuf, sizeof ulnbuf, "%u", elem->ulineno);
 689        for (cp = ulnbuf; *cp; cp++)
 690            APPEND_CHAR_TO_STACK(*cp);
 691        APPEND_CHAR_TO_STACK('\n');
 692    }
 693#undef APPEND_CHAR_TO_STACK
 694#undef APPEND_STRING_TO_STACK
 695#undef STACK_LENGTH_LIMIT
 696
 697  done:
 698    if (stacklen == 0) {
 699        JS_ASSERT(!stackbuf);
 700        return cx->runtime->emptyString;
 701    }
 702    if (stacklen < stackmax) {
 703        /*
 704         * Realloc can fail when shrinking on some FreeBSD versions, so
 705         * don't use JS_realloc here; simply let the oversized allocation
 706         * be owned by the string in that rare case.
 707         */
 708        void *shrunk = JS_realloc(cx, stackbuf, (stacklen+1) * sizeof(jschar));
 709        if (shrunk)
 710            stackbuf = (jschar *) shrunk;
 711    }
 712
 713    stackbuf[stacklen] = 0;
 714    str = js_NewString(cx, stackbuf, stacklen);
 715    if (str)
 716        return str;
 717
 718  bad:
 719    if (stackbuf)
 720        JS_free(cx, stackbuf);
 721    return NULL;
 722}
 723
 724/* XXXbe Consolidate the ugly truth that we don't treat filename as UTF-8
 725         with these two functions. */
 726static JSString *
 727FilenameToString(JSContext *cx, const char *filename)
 728{
 729    return JS_NewStringCopyZ(cx, filename);
 730}
 731
 732static const char *
 733StringToFilename(JSContext *cx, JSString *str)
 734{
 735    return js_GetStringBytes(cx, str);
 736}
 737
 738static JSBool
 739Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 740{
 741    uint32 lineno;
 742    JSString *message, *filename;
 743    JSStackFrame *fp;
 744
 745    if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
 746        /*
 747         * ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when
 748         * called as functions, without operator new.  But as we do not give
 749         * each constructor a distinct JSClass, whose .name member is used by
 750         * js_NewObject to find the class prototype, we must get the class
 751         * prototype ourselves.
 752         */
 753        if (!OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(argv[-2]),
 754                              ATOM_TO_JSID(cx->runtime->atomState
 755                                           .classPrototypeAtom),
 756                              rval))
 757            return JS_FALSE;
 758        obj = js_NewObject(cx, &js_ErrorClass, JSVAL_TO_OBJECT(*rval), NULL, 0);
 759        if (!obj)
 760            return JS_FALSE;
 761        *rval = OBJECT_TO_JSVAL(obj);
 762    }
 763
 764    /*
 765     * If it's a new object of class Exception, then null out the private
 766     * data so that the finalizer doesn't attempt to free it.
 767     */
 768    if (OBJ_GET_CLASS(cx, obj) == &js_ErrorClass)
 769        STOBJ_SET_SLOT(obj, JSSLOT_PRIVATE, JSVAL_VOID);
 770
 771    /* Set the 'message' property. */
 772    if (argc != 0) {
 773        message = js_ValueToString(cx, argv[0]);
 774        if (!message)
 775            return JS_FALSE;
 776        argv[0] = STRING_TO_JSVAL(message);
 777    } else {
 778        message = cx->runtime->emptyString;
 779    }
 780
 781    /* Set the 'fileName' property. */
 782    if (argc > 1) {
 783        filename = js_ValueToString(cx, argv[1]);
 784        if (!filename)
 785            return JS_FALSE;
 786        argv[1] = STRING_TO_JSVAL(filename);
 787        fp = NULL;
 788    } else {
 789        fp = JS_GetScriptedCaller(cx, NULL);
 790        if (fp) {
 791            filename = FilenameToString(cx, fp->script->filename);
 792            if (!filename)
 793                return JS_FALSE;
 794        } else {
 795            filename = cx->runtime->emptyString;
 796        }
 797    }
 798
 799    /* Set the 'lineNumber' property. */
 800    if (argc > 2) {
 801        lineno = js_ValueToECMAUint32(cx, &argv[2]);
 802        if (JSVAL_IS_NULL(argv[2]))
 803            return JS_FALSE;
 804    } else {
 805        if (!fp)
 806            fp = JS_GetScriptedCaller(cx, NULL);
 807        lineno = (fp && fp->regs) ? js_FramePCToLineNumber(cx, fp) : 0;
 808    }
 809
 810    return (OBJ_GET_CLASS(cx, obj) != &js_ErrorClass) ||
 811            InitExnPrivate(cx, obj, message, filename, lineno, NULL);
 812}
 813
 814/*
 815 * Convert to string.
 816 *
 817 * This method only uses JavaScript-modifiable properties name, message.  It
 818 * is left to the host to check for private data and report filename and line
 819 * number information along with this message.
 820 */
 821static JSBool
 822exn_toString(JSContext *cx, uintN argc, jsval *vp)
 823{
 824    JSObject *obj;
 825    jsval v;
 826    JSString *name, *message, *result;
 827    jschar *chars, *cp;
 828    size_t name_length, message_length, length;
 829
 830    obj = JS_THIS_OBJECT(cx, vp);
 831    if (!obj ||
 832        !OBJ_GET_PROPERTY(cx, obj,
 833                          ATOM_TO_JSID(cx->runtime->atomState.nameAtom),
 834                          &v)) {
 835        return JS_FALSE;
 836    }
 837    name = JSVAL_IS_STRING(v) ? JSVAL_TO_STRING(v) : cx->runtime->emptyString;
 838    *vp = STRING_TO_JSVAL(name);
 839
 840    if (!JS_GetProperty(cx, obj, js_message_str, &v))
 841        return JS_FALSE;
 842    message = JSVAL_IS_STRING(v) ? JSVAL_TO_STRING(v)
 843                                 : cx->runtime->emptyString;
 844
 845    if (JSSTRING_LENGTH(message) != 0) {
 846        name_length = JSSTRING_LENGTH(name);
 847        message_length = JSSTRING_LENGTH(message);
 848        length = (name_length ? name_length + 2 : 0) + message_length;
 849        cp = chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));
 850        if (!chars)
 851            return JS_FALSE;
 852
 853        if (name_length) {
 854            js_strncpy(cp, JSSTRING_CHARS(name), name_length);
 855            cp += name_length;
 856            *cp++ = ':'; *cp++ = ' ';
 857        }
 858        js_strncpy(cp, JSSTRING_CHARS(message), message_length);
 859        cp += message_length;
 860        *cp = 0;
 861
 862        result = js_NewString(cx, chars, length);
 863        if (!result) {
 864            JS_free(cx, chars);
 865            return JS_FALSE;
 866        }
 867    } else {
 868        result = name;
 869    }
 870
 871    *vp = STRING_TO_JSVAL(result);
 872    return JS_TRUE;
 873}
 874
 875#if JS_HAS_TOSOURCE
 876/*
 877 * Return a string that may eval to something similar to the original object.
 878 */
 879static JSBool
 880exn_toSource(JSContext *cx, uintN argc, jsval *vp)
 881{
 882    JSObject *obj;
 883    JSString *name, *message, *filename, *lineno_as_str, *result;
 884    jsval localroots[3] = {JSVAL_NULL, JSVAL_NULL, JSVAL_NULL};
 885    JSTempValueRooter tvr;
 886    JSBool ok;
 887    uint32 lineno;
 888    size_t lineno_length, name_length, message_length, filename_length, length;
 889    jschar *chars, *cp;
 890
 891    obj = JS_THIS_OBJECT(cx, vp);
 892    if (!obj ||
 893        !OBJ_GET_PROPERTY(cx, obj,
 894                          ATOM_TO_JSID(cx->runtime->atomState.nameAtom),
 895                          vp)) {
 896        return JS_FALSE;
 897    }
 898    name = js_ValueToString(cx, *vp);
 899    if (!name)
 900        return JS_FALSE;
 901    *vp = STRING_TO_JSVAL(name);
 902
 903    MUST_FLOW_THROUGH("out");
 904    JS_PUSH_TEMP_ROOT(cx, 3, localroots, &tvr);
 905
 906#ifdef __GNUC__
 907    message = filename = NULL;
 908#endif
 909    ok = JS_GetProperty(cx, obj, js_message_str, &localroots[0]) &&
 910         (message = js_ValueToSource(cx, localroots[0]));
 911    if (!ok)
 912        goto out;
 913    localroots[0] = STRING_TO_JSVAL(message);
 914
 915    ok = JS_GetProperty(cx, obj, js_fileName_str, &localroots[1]) &&
 916         (filename = js_ValueToSource(cx, localroots[1]));
 917    if (!ok)
 918        goto out;
 919    localroots[1] = STRING_TO_JSVAL(filename);
 920
 921    ok = JS_GetProperty(cx, obj, js_lineNumber_str, &localroots[2]);
 922    if (!ok)
 923        goto out;
 924    lineno = js_ValueToECMAUint32 (cx, &localroots[2]);
 925    ok = !JSVAL_IS_NULL(localroots[2]);
 926    if (!ok)
 927        goto out;
 928
 929    if (lineno != 0) {
 930        lineno_as_str = js_ValueToString(cx, localroots[2]);
 931        if (!lineno_as_str) {
 932            ok = JS_FALSE;
 933            goto out;
 934        }
 935        lineno_length = JSSTRING_LENGTH(lineno_as_str);
 936    } else {
 937        lineno_as_str = NULL;
 938        lineno_length = 0;
 939    }
 940
 941    /* Magic 8, for the characters in ``(new ())''. */
 942    name_length = JSSTRING_LENGTH(name);
 943    message_length = JSSTRING_LENGTH(message);
 944    length = 8 + name_length + message_length;
 945
 946    filename_length = JSSTRING_LENGTH(filename);
 947    if (filename_length != 0) {
 948        /* append filename as ``, {filename}'' */
 949        length += 2 + filename_length;
 950        if (lineno_as_str) {
 951            /* append lineno as ``, {lineno_as_str}'' */
 952            length += 2 + lineno_length;
 953        }
 954    } else {
 955        if (lineno_as_str) {
 956            /*
 957             * no filename, but have line number,
 958             * need to append ``, "", {lineno_as_str}''
 959             */
 960            length += 6 + lineno_length;
 961        }
 962    }
 963
 964    cp = chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));
 965    if (!chars) {
 966        ok = JS_FALSE;
 967        goto out;
 968    }
 969
 970    *cp++ = '('; *cp++ = 'n'; *cp++ = 'e'; *cp++ = 'w'; *cp++ = ' ';
 971    js_strncpy(cp, JSSTRING_CHARS(name), name_length);
 972    cp += name_length;
 973    *cp++ = '(';
 974    if (message_length != 0) {
 975        js_strncpy(cp, JSSTRING_CHARS(message), message_length);
 976        cp += message_length;
 977    }
 978
 979    if (filename_length != 0) {
 980        /* append filename as ``, {filename}'' */
 981        *cp++ = ','; *cp++ = ' ';
 982        js_strncpy(cp, JSSTRING_CHARS(filename), filename_length);
 983        cp += filename_length;
 984    } else {
 985        if (lineno_as_str) {
 986            /*
 987             * no filename, but have line number,
 988             * need to append ``, "", {lineno_as_str}''
 989             */
 990            *cp++ = ','; *cp++ = ' '; *cp++ = '"'; *cp++ = '"';
 991        }
 992    }
 993    if (lineno_as_str) {
 994        /* append lineno as ``, {lineno_as_str}'' */
 995        *cp++ = ','; *cp++ = ' ';
 996        js_strncpy(cp, JSSTRING_CHARS(lineno_as_str), lineno_length);
 997        cp += lineno_length;
 998    }
 999
1000    *cp++ = ')'; *cp++ = ')'; *cp = 0;
1001
1002    result = js_NewString(cx, chars, length);
1003    if (!result) {
1004        JS_free(cx, chars);
1005        ok = JS_FALSE;
1006        goto out;
1007    }
1008    *vp = STRING_TO_JSVAL(result);
1009    ok = JS_TRUE;
1010
1011out:
1012    JS_POP_TEMP_ROOT(cx, &tvr);
1013    return ok;
1014}
1015#endif
1016
1017static JSFunctionSpec exception_methods[] = {
1018#if JS_HAS_TOSOURCE
1019    JS_FN(js_toSource_str,   exn_toSource,           0,0),
1020#endif
1021    JS_FN(js_toString_str,   exn_toString,           0,0),
1022    JS_FS_END
1023};
1024
1025JSObject *
1026js_InitExceptionClasses(JSContext *cx, JSObject *obj)
1027{
1028    JSObject *obj_proto, *protos[JSEXN_LIMIT];
1029    int i;
1030
1031    /*
1032     * If lazy class initialization occurs for any Error subclass, then all
1033     * classes are initialized, starting with Error.  To avoid reentry and
1034     * redundant initialization, we must not pass a null proto parameter to
1035     * js_NewObject below, when called for the Error superclass.  We need to
1036     * ensure that Object.prototype is the proto of Error.prototype.
1037     *
1038     * See the equivalent code to ensure that parent_proto is non-null when
1039     * JS_InitClass calls js_NewObject, in jsapi.c.
1040     */
1041    if (!js_GetClassPrototype(cx, obj, INT_TO_JSID(JSProto_Object),
1042                              &obj_proto)) {
1043        return NULL;
1044    }
1045
1046    if (!js_EnterLocalRootScope(cx))
1047        return NULL;
1048
1049    /* Initialize the prototypes first. */
1050    for (i = 0; exceptions[i].name != 0; i++) {
1051        JSAtom *atom;
1052        JSFunction *fun;
1053        JSString *nameString;
1054        int protoIndex = exceptions[i].protoIndex;
1055
1056        /* Make the prototype for the current constructor name. */
1057        protos[i] = js_NewObject(cx, &js_ErrorClass,
1058                                 (protoIndex != JSEXN_NONE)
1059                                 ? protos[protoIndex]
1060                                 : obj_proto,
1061                                 obj, 0);
1062        if (!protos[i])
1063            break;
1064
1065        /* So exn_finalize knows whether to destroy private data. */
1066        STOBJ_SET_SLOT(protos[i], JSSLOT_PRIVATE, JSVAL_VOID);
1067
1068        /* Make a constructor function for the current name. */
1069        atom = cx->runtime->atomState.classAtoms[exceptions[i].key];
1070        fun = js_DefineFunction(cx, obj, atom, exceptions[i].native, 3, 0);
1071        if (!fun)
1072            break;
1073
1074        /* Make this constructor make objects of class Exception. */
1075        FUN_CLASP(fun) = &js_ErrorClass;
1076
1077        /* Make the prototype and constructor links. */
1078        if (!js_SetClassPrototype(cx, FUN_OBJECT(fun), protos[i],
1079                                  JSPROP_READONLY | JSPROP_PERMANENT)) {
1080            break;
1081        }
1082
1083        /* proto bootstrap bit from JS_InitClass omitted. */
1084        nameString = JS_NewStringCopyZ(cx, exceptions[i].name);
1085        if (!nameString)
1086            break;
1087
1088        /* Add the name property to the prototype. */
1089        if (!JS_DefineProperty(cx, protos[i], js_name_str,
1090                               STRING_TO_JSVAL(nameString),
1091                               NULL, NULL,
1092                               JSPROP_ENUMERATE)) {
1093            break;
1094        }
1095
1096        /* Finally, stash the constructor for later uses. */
1097        if (!js_SetClassObject(cx, obj, exceptions[i].key, FUN_OBJECT(fun)))
1098            break;
1099    }
1100
1101    js_LeaveLocalRootScope(cx);
1102    if (exceptions[i].name)
1103        return NULL;
1104
1105    /*
1106     * Add an empty message property.  (To Exception.prototype only,
1107     * because this property will be the same for all the exception
1108     * protos.)
1109     */
1110    if (!JS_DefineProperty(cx, protos[0], js_message_str,
1111                           STRING_TO_JSVAL(cx->runtime->emptyString),
1112                           NULL, NULL, JSPROP_ENUMERATE)) {
1113        return NULL;
1114    }
1115    if (!JS_DefineProperty(cx, protos[0], js_fileName_str,
1116                           STRING_TO_JSVAL(cx->runtime->emptyString),
1117                           NULL, NULL, JSPROP_ENUMERATE)) {
1118        return NULL;
1119    }
1120    if (!JS_DefineProperty(cx, protos[0], js_lineNumber_str,
1121                           INT_TO_JSVAL(0),
1122                           NULL, NULL, JSPROP_ENUMERATE)) {
1123        return NULL;
1124    }
1125
1126    /*
1127     * Add methods only to Exception.prototype, because ostensibly all
1128     * exception types delegate to that.
1129     */
1130    if (!JS_DefineFunctions(cx, protos[0], exception_methods))
1131        return NULL;
1132
1133    return protos[0];
1134}
1135
1136const JSErrorFormatString*
1137js_GetLocalizedErrorMessage(JSContext* cx, void *userRef, const char *locale,
1138                            const uintN errorNumber)
1139{
1140    const JSErrorFormatString *errorString = NULL;
1141
1142    if (cx->localeCallbacks && cx->localeCallbacks->localeGetErrorMessage) {
1143        errorString = cx->localeCallbacks
1144                        ->localeGetErrorMessage(userRef, locale, errorNumber);
1145    }
1146    if (!errorString)
1147        errorString = js_GetErrorMessage(userRef, locale, errorNumber);
1148    return errorString;
1149}
1150
1151#if defined ( DEBUG_mccabe ) && defined ( PRINTNAMES )
1152/* For use below... get character strings for error name and exception name */
1153static struct exnname { char *name; char *exception; } errortoexnname[] = {
1154#define MSG_DEF(name, number, count, exception, format) \
1155    {#name, #exception},
1156#include "js.msg"
1157#undef MSG_DEF
1158};
1159#endif /* DEBUG */
1160
1161JSBool
1162js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp)
1163{
1164    JSErrNum errorNumber;
1165    const JSErrorFormatString *errorString;
1166    JSExnType exn;
1167    jsval tv[4];
1168    JSTempValueRooter tvr;
1169    JSBool ok;
1170    JSObject *errProto, *errObject;
1171    JSString *messageStr, *filenameStr;
1172
1173    /*
1174     * Tell our caller to report immediately if this report is just a warning.
1175     */
1176    JS_ASSERT(reportp);
1177    if (JSREPORT_IS_WARNING(reportp->flags))
1178        return JS_FALSE;
1179
1180    /* Find the exception index associated with this error. */
1181    errorNumber = (JSErrNum) reportp->errorNumber;
1182    errorString = js_GetLocalizedErrorMessage(cx, NULL, NULL, errorNumber);
1183    exn = errorString ? (JSExnType) errorString->exnType : JSEXN_NONE;
1184    JS_ASSERT(exn < JSEXN_LIMIT);
1185
1186#if defined( DEBUG_mccabe ) && defined ( PRINTNAMES )
1187    /* Print the error name and the associated exception name to stderr */
1188    fprintf(stderr, "%s\t%s\n",
1189            errortoexnname[errorNumber].name,
1190            errortoexnname[errorNumber].exception);
1191#endif
1192
1193    /*
1194     * Return false (no exception raised) if no exception is associated
1195     * with the given error number.
1196     */
1197    if (exn == JSEXN_NONE)
1198        return JS_FALSE;
1199
1200    /*
1201     * Prevent runaway recursion, via cx->generatingError.  If an out-of-memory
1202     * error occurs, no exception object will be created, but we don't assume
1203     * that OOM is the only kind of error that subroutines of this function
1204     * called below might raise.
1205     */
1206    if (cx->generatingError)
1207        return JS_FALSE;
1208
1209    MUST_FLOW_THROUGH("out");
1210    cx->generatingError = JS_TRUE;
1211
1212    /* Protect the newly-created strings below from nesting GCs. */
1213    memset(tv, 0, sizeof tv);
1214    JS_PUSH_TEMP_ROOT(cx, JS_ARRAY_LENGTH(tv), tv, &tvr);
1215
1216    /*
1217     * Try to get an appropriate prototype by looking up the corresponding
1218     * exception constructor name in the scope chain of the current context's
1219     * top stack frame, or in the global object if no frame is active.
1220     */
1221    ok = js_GetClassPrototype(cx, NULL, INT_TO_JSID(exceptions[exn].key),
1222                              &errProto);
1223    if (!ok)
1224        goto out;
1225    tv[0] = OBJECT_TO_JSVAL(errProto);
1226
1227    errObject = js_NewObject(cx, &js_ErrorClass, errProto, NULL, 0);
1228    if (!errObject) {
1229        ok = JS_FALSE;
1230        goto out;
1231    }
1232    tv[1] = OBJECT_TO_JSVAL(errObject);
1233
1234    messageStr = JS_NewStringCopyZ(cx, message);
1235    if (!messageStr) {
1236        ok = JS_FALSE;
1237        goto out;
1238    }
1239    tv[2] = STRING_TO_JSVAL(messageStr);
1240
1241    filenameStr = JS_NewStringCopyZ(cx, reportp->filename);
1242    if (!filenameStr) {
1243        ok = JS_FALSE;
1244        goto out;
1245    }
1246    tv[3] = STRING_TO_JSVAL(filenameStr);
1247
1248    ok = InitExnPrivate(cx, errObject, messageStr, filenameStr,
1249                        reportp->lineno, reportp);
1250    if (!ok)
1251        goto out;
1252
1253    JS_SetPendingException(cx, OBJECT_TO_JSVAL(errObject));
1254
1255    /* Flag the error report passed in to indicate an exception was raised. */
1256    reportp->flags |= JSREPORT_EXCEPTION;
1257
1258out:
1259    JS_POP_TEMP_ROOT(cx, &tvr);
1260    cx->generatingError = JS_FALSE;
1261    return ok;
1262}
1263
1264JSBool
1265js_ReportUncaughtException(JSContext *cx)
1266{
1267    jsval exn;
1268    JSObject *exnObject;
1269    jsval roots[5];
1270    JSTempValueRooter tvr;
1271    JSErrorReport *reportp, report;
1272    JSString *str;
1273    const char *bytes;
1274    JSBool ok;
1275
1276    if (!JS_IsExceptionPending(cx))
1277        return JS_TRUE;
1278
1279    if (!JS_GetPendingException(cx, &exn))
1280        return JS_FALSE;
1281
1282    memset(roots, 0, sizeof roots);
1283    JS_PUSH_TEMP_ROOT(cx, JS_ARRAY_LENGTH(roots), roots, &tvr);
1284
1285    /*
1286     * Because js_ValueToString below could error and an exception object
1287     * could become unrooted, we must root exnObject.  Later, if exnObject is
1288     * non-null, we need to root other intermediates, so allocate an operand
1289     * stack segment to protect all of these values.
1290     */
1291    if (JSVAL_IS_PRIMITIVE(exn)) {
1292        exnObject = NULL;
1293    } else {
1294        exnObject = JSVAL_TO_OBJECT(exn);
1295        roots[0] = exn;
1296    }
1297
1298    JS_ClearPendingException(cx);
1299    reportp = js_ErrorFromException(cx, exn);
1300
1301    /* XXX L10N angels cry once again (see also jsemit.c, /L10N gaffes/) */
1302    str = js_ValueToString(cx, exn);
1303    if (!str) {
1304        bytes = "unknown (can't convert to string)";
1305    } else {
1306        roots[1] = STRING_TO_JSVAL(str);
1307        bytes = js_GetStringBytes(cx, str);
1308        if (!bytes) {
1309            ok = JS_FALSE;
1310            goto out;
1311        }
1312    }
1313    ok = JS_TRUE;
1314
1315    if (!reportp &&
1316        exnObject &&
1317        OBJ_GET_CLASS(cx, exnObject) == &js_ErrorClass) {
1318        const char *filename;
1319        uint32 lineno;
1320
1321        ok = JS_GetProperty(cx, exnObject, js_message_str, &roots[2]);
1322        if (!ok)
1323            goto out;
1324        if (JSVAL_IS_STRING(roots[2])) {
1325            bytes = js_GetStringBytes(cx, JSVAL_TO_STRING(roots[2]));
1326            if (!bytes) {
1327                ok = JS_FALSE;
1328                goto out;
1329            }
1330        }
1331
1332        ok = JS_GetProperty(cx, exnObject, js_fileName_str, &roots[3]);
1333        if (!ok)
1334            goto out;
1335        str = js_ValueToString(cx, roots[3]);
1336        if (!str) {
1337            ok = JS_FALSE;
1338            goto out;
1339        }
1340        filename = StringToFilename(cx, str);
1341        if (!filename) {
1342            ok = JS_FALSE;
1343            goto out;
1344        }
1345
1346        ok = JS_GetProperty(cx, exnObject, js_lineNumber_str, &roots[4]);
1347        if (!ok)
1348            goto out;
1349        lineno = js_ValueToECMAUint32 (cx, &roots[4]);
1350        ok = !JSVAL_IS_NULL(roots[4]);
1351        if (!ok)
1352            goto out;
1353
1354        reportp = &report;
1355        memset(&report, 0, sizeof report);
1356        report.filename = filename;
1357        report.lineno = (uintN) lineno;
1358    }
1359
1360    if (!reportp) {
1361        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1362                             JSMSG_UNCAUGHT_EXCEPTION, bytes);
1363    } else {
1364        /* Flag the error as an exception. */
1365        reportp->flags |= JSREPORT_EXCEPTION;
1366
1367        /* Pass the exception object. */
1368        JS_SetPendingException(cx, exn);
1369        js_ReportErrorAgain(cx, bytes, reportp);
1370        JS_ClearPendingException(cx);
1371    }
1372
1373out:
1374    JS_POP_TEMP_ROOT(cx, &tvr);
1375    return ok;
1376}