PageRenderTime 104ms CodeModel.GetById 17ms app.highlight 77ms RepoModel.GetById 1ms app.codeStats 0ms

/js/src/jsexn.cpp

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