Esempio n. 1
0
static void
TestArgFormatter(JSContext* jscontext, JSObject* glob, nsIXPConnect* xpc)
{
    JSBool ok = JS_TRUE;

    const char*                  a_in = "some string";
    nsCOMPtr<nsITestXPCFoo>      b_in = new nsTestXPCFoo();
    nsCOMPtr<nsIWritableVariant> c_in = do_CreateInstance("@mozilla.org/variant;1"); 
    static NS_NAMED_LITERAL_STRING(d_in, "foo bar");
    const char*                  e_in = "another meaningless chunck of text";
    

    JSBool                  a_match;
    nsCOMPtr<nsISupports>   b_out;
    nsCOMPtr<nsIVariant>    c_out;
    nsAutoString            d_out;
    JSBool                  e_match;

    nsCOMPtr<nsITestXPCFoo> specified;
    PRInt32                 val;

    printf("ArgumentFormatter test: ");

    if(!b_in || !c_in || NS_FAILED(c_in->SetAsInt32(5)))
    {
        printf(" failed to construct test objects -- FAILED!\n");
        return;
    }

    do {
        JSAutoRequest ar(jscontext);

        // Prepare an array of arguments for JS_ConvertArguments
        jsval argv[5];
        js::AutoArrayRooter tvr(jscontext, JS_ARRAY_LENGTH(argv), argv);

        if (!PushArguments(jscontext, 5, argv,
                           "s %ip %iv %is s",
                           a_in,
                           &NS_GET_IID(nsITestXPCFoo2), b_in.get(),
                           c_in.get(),
                           static_cast<const nsAString*>(&d_in),
                           e_in))
        {
            printf(" could not convert from native to JS -- FAILED!\n");
            return;
        }

        JSString *a_out, *e_out;
        ok = JS_ConvertArguments(jscontext, 5, argv, "S %ip %iv %is S",
                                &a_out, 
                                static_cast<nsISupports**>(getter_AddRefs(b_out)), 
                                static_cast<nsIVariant**>(getter_AddRefs(c_out)),
                                static_cast<nsAString*>(&d_out), 
                                 &e_out);
        TAF_CHECK(ok, " could not convert from JS to native -- FAILED!\n");
        TAF_CHECK(b_out, " JS to native for %%ip returned NULL -- FAILED!\n");
    
        specified = do_QueryInterface(b_out);
        TAF_CHECK(specified, " could not QI value JS to native returned -- FAILED!\n");
        ok = specified.get() == b_in.get();
        TAF_CHECK(ok, " JS to native returned wrong value -- FAILED!\n");
        TAF_CHECK(c_out, " JS to native for %%iv returned NULL -- FAILED!\n");
        TAF_CHECK(NS_SUCCEEDED(c_out->GetAsInt32(&val)) && val == 5, " JS to native for %%iv holds wrong value -- FAILED!\n");
        TAF_CHECK(d_in.Equals(d_out), " JS to native for %%is returned the wrong value -- FAILED!\n");
        TAF_CHECK(JS_StringEqualsAscii(jscontext, a_out, a_in, &a_match), " oom -- FAILED!\n");
        TAF_CHECK(JS_StringEqualsAscii(jscontext, e_out, e_in, &e_match), " oom -- FAILED!\n");
    } while (0);
    if (!ok)
        return;

    if(a_match && e_match)
        printf("passed\n");
    else
        printf(" conversion OK, but surrounding was mangled -- FAILED!\n");
}
Esempio n. 2
0
    js_star_str,                /* starAtom                     */
    js_starQualifier_str,       /* starQualifierAtom            */
    js_tagc_str,                /* tagcAtom                     */
    js_xml_str,                 /* xmlAtom                      */
#endif

#ifdef NARCISSUS
    js___call___str,            /* __call__Atom                 */
    js___construct___str,       /* __construct__Atom            */
    js___hasInstance___str,     /* __hasInstance__Atom          */
    js_ExecutionContext_str,    /* ExecutionContextAtom         */
    js_current_str,             /* currentAtom                  */
#endif
};

JS_STATIC_ASSERT(JS_ARRAY_LENGTH(js_common_atom_names) * sizeof(JSAtom *) ==
                 LAZY_ATOM_OFFSET_START - ATOM_OFFSET_START);

/*
 * Interpreter macros called by the trace recorder assume common atom indexes
 * fit in one byte of immediate operand.
 */
JS_STATIC_ASSERT(JS_ARRAY_LENGTH(js_common_atom_names) < 256);

const size_t js_common_atom_count = JS_ARRAY_LENGTH(js_common_atom_names);

const char js_anonymous_str[]       = "anonymous";
const char js_apply_str[]           = "apply";
const char js_arguments_str[]       = "arguments";
const char js_arity_str[]           = "arity";
const char js_call_str[]            = "call";
Esempio n. 3
0
JSBool
js_ReportUncaughtException(JSContext *cx)
{
    jsval exn;
    JSObject *exnObject;
    jsval roots[5];
    JSTempValueRooter tvr;
    JSErrorReport *reportp, report;
    JSString *str;
    const char *bytes;
    JSBool ok;

    if (!JS_IsExceptionPending(cx))
        return JS_TRUE;

    if (!JS_GetPendingException(cx, &exn))
        return JS_FALSE;

    memset(roots, 0, sizeof roots);
    JS_PUSH_TEMP_ROOT(cx, JS_ARRAY_LENGTH(roots), roots, &tvr);

    /*
     * Because js_ValueToString below could error and an exception object
     * could become unrooted, we must root exnObject.  Later, if exnObject is
     * non-null, we need to root other intermediates, so allocate an operand
     * stack segment to protect all of these values.
     */
    if (JSVAL_IS_PRIMITIVE(exn)) {
        exnObject = NULL;
    } else {
        exnObject = JSVAL_TO_OBJECT(exn);
        roots[0] = exn;
    }

    JS_ClearPendingException(cx);
    reportp = js_ErrorFromException(cx, exn);

    /* XXX L10N angels cry once again (see also jsemit.c, /L10N gaffes/) */
    str = js_ValueToString(cx, exn);
    if (!str) {
        bytes = "unknown (can't convert to string)";
    } else {
        roots[1] = STRING_TO_JSVAL(str);
        bytes = js_GetStringBytes(cx, str);
        if (!bytes) {
            ok = JS_FALSE;
            goto out;
        }
    }
    ok = JS_TRUE;

    if (!reportp &&
        exnObject &&
        OBJ_GET_CLASS(cx, exnObject) == &js_ErrorClass) {
        const char *filename;
        uint32 lineno;

        ok = JS_GetProperty(cx, exnObject, js_message_str, &roots[2]);
        if (!ok)
            goto out;
        if (JSVAL_IS_STRING(roots[2])) {
            bytes = js_GetStringBytes(cx, JSVAL_TO_STRING(roots[2]));
            if (!bytes) {
                ok = JS_FALSE;
                goto out;
            }
        }

        ok = JS_GetProperty(cx, exnObject, js_fileName_str, &roots[3]);
        if (!ok)
            goto out;
        str = js_ValueToString(cx, roots[3]);
        if (!str) {
            ok = JS_FALSE;
            goto out;
        }
        filename = StringToFilename(cx, str);
        if (!filename) {
            ok = JS_FALSE;
            goto out;
        }

        ok = JS_GetProperty(cx, exnObject, js_lineNumber_str, &roots[4]);
        if (!ok)
            goto out;
        lineno = js_ValueToECMAUint32 (cx, &roots[4]);
        ok = !JSVAL_IS_NULL(roots[4]);
        if (!ok)
            goto out;

        reportp = &report;
        memset(&report, 0, sizeof report);
        report.filename = filename;
        report.lineno = (uintN) lineno;
    }

    if (!reportp) {
        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                             JSMSG_UNCAUGHT_EXCEPTION, bytes);
    } else {
        /* Flag the error as an exception. */
        reportp->flags |= JSREPORT_EXCEPTION;

        /* Pass the exception object. */
        JS_SetPendingException(cx, exn);
        js_ReportErrorAgain(cx, bytes, reportp);
        JS_ClearPendingException(cx);
    }

out:
    JS_POP_TEMP_ROOT(cx, &tvr);
    return ok;
}
Esempio n. 4
0
JSBool
js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp)
{
    JSErrNum errorNumber;
    const JSErrorFormatString *errorString;
    JSExnType exn;
    jsval tv[4];
    JSTempValueRooter tvr;
    JSBool ok;
    JSObject *errProto, *errObject;
    JSString *messageStr, *filenameStr;

    /*
     * Tell our caller to report immediately if cx has no active frames, or if
     * this report is just a warning.
     */
    JS_ASSERT(reportp);
    if (!cx->fp || JSREPORT_IS_WARNING(reportp->flags))
        return JS_FALSE;

    /* Find the exception index associated with this error. */
    errorNumber = (JSErrNum) reportp->errorNumber;
    errorString = js_GetLocalizedErrorMessage(cx, NULL, NULL, errorNumber);
    exn = errorString ? (JSExnType) errorString->exnType : JSEXN_NONE;
    JS_ASSERT(exn < JSEXN_LIMIT);

#if defined( DEBUG_mccabe ) && defined ( PRINTNAMES )
    /* Print the error name and the associated exception name to stderr */
    fprintf(stderr, "%s\t%s\n",
            errortoexnname[errorNumber].name,
            errortoexnname[errorNumber].exception);
#endif

    /*
     * Return false (no exception raised) if no exception is associated
     * with the given error number.
     */
    if (exn == JSEXN_NONE)
        return JS_FALSE;

    /*
     * Prevent runaway recursion, via cx->generatingError.  If an out-of-memory
     * error occurs, no exception object will be created, but we don't assume
     * that OOM is the only kind of error that subroutines of this function
     * called below might raise.
     */
    if (cx->generatingError)
        return JS_FALSE;

    /* After this point the control must flow through the label out. */
    cx->generatingError = JS_TRUE;

    /* Protect the newly-created strings below from nesting GCs. */
    memset(tv, 0, sizeof tv);
    JS_PUSH_TEMP_ROOT(cx, JS_ARRAY_LENGTH(tv), tv, &tvr);

    /*
     * Try to get an appropriate prototype by looking up the corresponding
     * exception constructor name in the scope chain of the current context's
     * top stack frame, or in the global object if no frame is active.
     */
    ok = js_GetClassPrototype(cx, NULL, INT_TO_JSID(exceptions[exn].key),
                              &errProto);
    if (!ok)
        goto out;
    tv[0] = OBJECT_TO_JSVAL(errProto);

    errObject = js_NewObject(cx, &js_ErrorClass, errProto, NULL, 0);
    if (!errObject) {
        ok = JS_FALSE;
        goto out;
    }
    tv[1] = OBJECT_TO_JSVAL(errObject);

    messageStr = JS_NewStringCopyZ(cx, message);
    if (!messageStr) {
        ok = JS_FALSE;
        goto out;
    }
    tv[2] = STRING_TO_JSVAL(messageStr);

    filenameStr = JS_NewStringCopyZ(cx, reportp->filename);
    if (!filenameStr) {
        ok = JS_FALSE;
        goto out;
    }
    tv[3] = STRING_TO_JSVAL(filenameStr);

    ok = InitExnPrivate(cx, errObject, messageStr, filenameStr,
                        reportp->lineno, reportp);
    if (!ok)
        goto out;

    JS_SetPendingException(cx, OBJECT_TO_JSVAL(errObject));

    /* Flag the error report passed in to indicate an exception was raised. */
    reportp->flags |= JSREPORT_EXCEPTION;

out:
    JS_POP_TEMP_ROOT(cx, &tvr);
    cx->generatingError = JS_FALSE;
    return ok;
}
"readline()               Read a single line from stdin",
"print([exp ...])         Evaluate and print expressions",
"help([name ...])         Display usage and help messages",
"quit()                   Quit the shell",
#ifdef MOZ_SHARK
"startShark()             Start a Shark session.\n"
"                         Shark must be running with programatic sampling.",
"stopShark()              Stop a running Shark session.",
"connectShark()           Connect to Shark.\n"
"                         The -k switch does this automatically.",
"disconnectShark()        Disconnect from Shark.",
#endif
};

/* Help messages must match shell functions. */
JS_STATIC_ASSERT(JS_ARRAY_LENGTH(shell_help_messages) + 1 ==
                 JS_ARRAY_LENGTH(shell_functions));

#ifdef DEBUG
static void
CheckHelpMessages(void)
{
    const char *const *m;
    const char *lp;

    /* Each message must begin with "function_name(" prefix. */
    for (m = shell_help_messages; m != JS_ARRAY_END(shell_help_messages); ++m) {
        lp = strchr(*m, '(');
        JS_ASSERT(lp);
        JS_ASSERT(memcmp(shell_functions[m - shell_help_messages].name,
                         *m, lp - *m) == 0);
Esempio n. 6
0
namespace gc {

#if defined(JS_DUMP_CONSERVATIVE_GC_ROOTS) || defined(JS_GCMETER)

void
ConservativeGCStats::dump(FILE *fp)
{
    size_t words = 0;
    for (size_t i = 0; i != JS_ARRAY_LENGTH(counter); ++i)
        words += counter[i];
   
#define ULSTAT(x)       ((unsigned long)(x))
    fprintf(fp, "CONSERVATIVE STACK SCANNING:\n");
    fprintf(fp, "      number of stack words: %lu\n", ULSTAT(words));
    fprintf(fp, "      excluded, low bit set: %lu\n", ULSTAT(counter[CGCT_LOWBITSET]));
    fprintf(fp, "        not withing a chunk: %lu\n", ULSTAT(counter[CGCT_NOTCHUNK]));
    fprintf(fp, "     not within arena range: %lu\n", ULSTAT(counter[CGCT_NOTARENA]));
    fprintf(fp, "       points to free arena: %lu\n", ULSTAT(counter[CGCT_FREEARENA]));
    fprintf(fp, "        excluded, wrong tag: %lu\n", ULSTAT(counter[CGCT_WRONGTAG]));
    fprintf(fp, "         excluded, not live: %lu\n", ULSTAT(counter[CGCT_NOTLIVE]));
    fprintf(fp, "            valid GC things: %lu\n", ULSTAT(counter[CGCT_VALID]));
    fprintf(fp, "      valid but not aligned: %lu\n", ULSTAT(unaligned));
#undef ULSTAT
}
#endif

#ifdef JS_GCMETER
void
UpdateCompartmentGCStats(JSCompartment *comp, unsigned thingKind)
{
    JSGCArenaStats *compSt = &comp->arenas[thingKind].stats;
    JSGCArenaStats *globSt = &comp->rt->globalArenaStats[thingKind];
    JS_ASSERT(compSt->narenas >= compSt->livearenas);
    compSt->newarenas     = compSt->narenas - compSt->livearenas;
    if (compSt->maxarenas < compSt->narenas)
        compSt->maxarenas = compSt->narenas;
    compSt->totalarenas  += compSt->narenas;

    if (compSt->maxthings < compSt->nthings)
        compSt->maxthings = compSt->nthings;
    compSt->totalthings  += compSt->nthings;
    globSt->newarenas    += compSt->newarenas;
    globSt->narenas      += compSt->narenas;
    globSt->livearenas   += compSt->livearenas;
    globSt->totalarenas  += compSt->totalarenas;
    globSt->nthings      += compSt->nthings;
    globSt->totalthings  += compSt->totalthings;
    if (globSt->maxarenas < compSt->maxarenas)
        globSt->maxarenas = compSt->maxarenas;
    if (globSt->maxthings < compSt->maxthings)
        globSt->maxthings = compSt->maxthings;
}

void
UpdateAllCompartmentGCStats(JSCompartment *comp)
{
    /*
     * The stats for the list arenas scheduled for the background finalization
     * are updated after that finishes.
     */
    JS_ASSERT(comp->rt->gcRunning);
    for (unsigned i = 0; i != JS_ARRAY_LENGTH(comp->arenas); ++i) {
#ifdef JS_THREADSAFE
        if (comp->arenas[i].willBeFinalizedLater())
            continue;
#endif
        UpdateCompartmentGCStats(comp, i);
    }
}

static const char *const GC_ARENA_NAMES[] = {
    "object_0",
    "object_0_background",
    "object_2",
    "object_2_background",
    "object_4",
    "object_4_background",
    "object_8",
    "object_8_background",
    "object_12",
    "object_12_background",
    "object_16",
    "object_16_background",
    "function",
    "shape",
#if JS_HAS_XML_SUPPORT
    "xml",
#endif
    "short string",
    "string",
    "external_string",
};
JS_STATIC_ASSERT(JS_ARRAY_LENGTH(GC_ARENA_NAMES) == FINALIZE_LIMIT);

template <typename T>
static inline void
GetSizeAndThings(size_t &thingSize, size_t &thingsPerArena)
{
    thingSize = sizeof(T);
    thingsPerArena = Arena<T>::ThingsPerArena;
}

void GetSizeAndThingsPerArena(int thingKind, size_t &thingSize, size_t &thingsPerArena)
{
    switch (thingKind) {
        case FINALIZE_OBJECT0:
        case FINALIZE_OBJECT0_BACKGROUND:
            GetSizeAndThings<JSObject>(thingSize, thingsPerArena);
            break;
        case FINALIZE_OBJECT2:
        case FINALIZE_OBJECT2_BACKGROUND:
            GetSizeAndThings<JSObject_Slots2>(thingSize, thingsPerArena);
            break;
        case FINALIZE_OBJECT4:
        case FINALIZE_OBJECT4_BACKGROUND:
            GetSizeAndThings<JSObject_Slots4>(thingSize, thingsPerArena);
            break;
        case FINALIZE_OBJECT8:
        case FINALIZE_OBJECT8_BACKGROUND:
            GetSizeAndThings<JSObject_Slots8>(thingSize, thingsPerArena);
            break;
        case FINALIZE_OBJECT12:
        case FINALIZE_OBJECT12_BACKGROUND:
            GetSizeAndThings<JSObject_Slots12>(thingSize, thingsPerArena);
            break;
        case FINALIZE_OBJECT16:
        case FINALIZE_OBJECT16_BACKGROUND:
            GetSizeAndThings<JSObject_Slots16>(thingSize, thingsPerArena);
            break;
        case FINALIZE_EXTERNAL_STRING:
        case FINALIZE_STRING:
            GetSizeAndThings<JSString>(thingSize, thingsPerArena);
            break;
        case FINALIZE_SHORT_STRING:
            GetSizeAndThings<JSShortString>(thingSize, thingsPerArena);
            break;
        case FINALIZE_FUNCTION:
            GetSizeAndThings<JSFunction>(thingSize, thingsPerArena);
            break;
#if JS_HAS_XML_SUPPORT
        case FINALIZE_XML:
            GetSizeAndThings<JSXML>(thingSize, thingsPerArena);
            break;
#endif
        default:
            JS_NOT_REACHED("wrong kind");
    }
}

void
DumpArenaStats(JSGCArenaStats *stp, FILE *fp)
{
    size_t sumArenas = 0, sumTotalArenas = 0, sumThings =0,  sumMaxThings = 0;
    size_t sumThingSize = 0, sumTotalThingSize = 0, sumArenaCapacity = 0;
    size_t sumTotalArenaCapacity = 0, sumAlloc = 0, sumLocalAlloc = 0;

    for (int i = 0; i < (int) FINALIZE_LIMIT; i++) {
        JSGCArenaStats *st = &stp[i];
        if (st->maxarenas == 0)
            continue;
        size_t thingSize = 0, thingsPerArena = 0;
        GetSizeAndThingsPerArena(i, thingSize, thingsPerArena);

        fprintf(fp, "%s arenas (thing size %lu, %lu things per arena):\n",
                GC_ARENA_NAMES[i], UL(thingSize), UL(thingsPerArena));
        fprintf(fp, "           arenas before GC: %lu\n", UL(st->narenas));
        fprintf(fp, "            arenas after GC: %lu (%.1f%%)\n",
                UL(st->livearenas), PERCENT(st->livearenas, st->narenas));
        fprintf(fp, "                 max arenas: %lu\n", UL(st->maxarenas));
        fprintf(fp, "                     things: %lu\n", UL(st->nthings));
        fprintf(fp, "        GC cell utilization: %.1f%%\n",
                PERCENT(st->nthings, thingsPerArena * st->narenas));
        fprintf(fp, "   average cell utilization: %.1f%%\n",
                PERCENT(st->totalthings, thingsPerArena * st->totalarenas));
        fprintf(fp, "                 max things: %lu\n", UL(st->maxthings));
        fprintf(fp, "             alloc attempts: %lu\n", UL(st->alloc));
        fprintf(fp, "        alloc without locks: %lu  (%.1f%%)\n",
                UL(st->localalloc), PERCENT(st->localalloc, st->alloc));
        sumArenas += st->narenas;
        sumTotalArenas += st->totalarenas;
        sumThings += st->nthings;
        sumMaxThings += st->maxthings;
        sumThingSize += thingSize * st->nthings;
        sumTotalThingSize += size_t(thingSize * st->totalthings);
        sumArenaCapacity += thingSize * thingsPerArena * st->narenas;
        sumTotalArenaCapacity += thingSize * thingsPerArena * st->totalarenas;
        sumAlloc += st->alloc;
        sumLocalAlloc += st->localalloc;
        putc('\n', fp);
    }

    fputs("Never used arenas:\n", fp);
    for (int i = 0; i < (int) FINALIZE_LIMIT; i++) {
        JSGCArenaStats *st = &stp[i];
        if (st->maxarenas != 0)
            continue;
        fprintf(fp, "%s\n", GC_ARENA_NAMES[i]);
    }
    fprintf(fp, "\nTOTAL STATS:\n");
    fprintf(fp, "            total GC arenas: %lu\n", UL(sumArenas));
    fprintf(fp, "            total GC things: %lu\n", UL(sumThings));
    fprintf(fp, "        max total GC things: %lu\n", UL(sumMaxThings));
    fprintf(fp, "        GC cell utilization: %.1f%%\n",
            PERCENT(sumThingSize, sumArenaCapacity));
    fprintf(fp, "   average cell utilization: %.1f%%\n",
            PERCENT(sumTotalThingSize, sumTotalArenaCapacity));
    fprintf(fp, "             alloc attempts: %lu\n", UL(sumAlloc));
    fprintf(fp, "        alloc without locks: %lu  (%.1f%%)\n",
            UL(sumLocalAlloc), PERCENT(sumLocalAlloc, sumAlloc));
}

void
DumpCompartmentStats(JSCompartment *comp, FILE *fp)
{
    if (comp->rt->atomsCompartment == comp)
        fprintf(fp, "\n**** AtomsCompartment Allocation Statistics: %p ****\n\n", (void *) comp);
    else
        fprintf(fp, "\n**** Compartment Allocation Statistics: %p ****\n\n", (void *) comp);

    for (unsigned i = 0; i != FINALIZE_LIMIT; ++i)
        DumpArenaStats(&comp->arenas[i].stats, fp);
}

#endif

} //gc
Esempio n. 7
0
JSObject *
js_InitExceptionClasses(JSContext *cx, JSObject *obj)
{
    jsval roots[3];
    JSObject *obj_proto, *error_proto;
    jsval empty;

    /*
     * If lazy class initialization occurs for any Error subclass, then all
     * classes are initialized, starting with Error.  To avoid reentry and
     * redundant initialization, we must not pass a null proto parameter to
     * js_NewObject below, when called for the Error superclass.  We need to
     * ensure that Object.prototype is the proto of Error.prototype.
     *
     * See the equivalent code to ensure that parent_proto is non-null when
     * JS_InitClass calls js_NewObject, in jsapi.c.
     */
    if (!js_GetClassPrototype(cx, obj, INT_TO_JSID(JSProto_Object),
                              &obj_proto)) {
        return NULL;
    }

    memset(roots, 0, sizeof(roots));
    JSAutoTempValueRooter tvr(cx, JS_ARRAY_LENGTH(roots), roots);

#ifdef __GNUC__
    error_proto = NULL;   /* quell GCC overwarning */
#endif

    /* Initialize the prototypes first. */
    for (intN i = JSEXN_ERR; i != JSEXN_LIMIT; i++) {
        JSObject *proto;
        JSProtoKey protoKey;
        JSAtom *atom;
        JSFunction *fun;

        /* Make the prototype for the current constructor name. */
        proto = js_NewObject(cx, &js_ErrorClass,
                             (i != JSEXN_ERR) ? error_proto : obj_proto,
                             obj);
        if (!proto)
            return NULL;
        if (i == JSEXN_ERR) {
            error_proto = proto;
            roots[0] = OBJECT_TO_JSVAL(proto);
        } else {
            // We cannot share the root for error_proto and other prototypes
            // as error_proto must be rooted until the function returns.
            roots[1] = OBJECT_TO_JSVAL(proto);
        }

        /* So exn_finalize knows whether to destroy private data. */
        proto->setPrivate(NULL);

        /* Make a constructor function for the current name. */
        protoKey = GetExceptionProtoKey(i);
        atom = cx->runtime->atomState.classAtoms[protoKey];
        fun = js_DefineFunction(cx, obj, atom, Exception, 3, 0);
        if (!fun)
            return NULL;
        roots[2] = OBJECT_TO_JSVAL(FUN_OBJECT(fun));

        /* Make this constructor make objects of class Exception. */
        FUN_CLASP(fun) = &js_ErrorClass;

        /* Make the prototype and constructor links. */
        if (!js_SetClassPrototype(cx, FUN_OBJECT(fun), proto,
                                  JSPROP_READONLY | JSPROP_PERMANENT)) {
            return NULL;
        }

        /* Add the name property to the prototype. */
        if (!JS_DefineProperty(cx, proto, js_name_str, ATOM_KEY(atom),
                               NULL, NULL, JSPROP_ENUMERATE)) {
            return NULL;
        }

        /* Finally, stash the constructor for later uses. */
        if (!js_SetClassObject(cx, obj, protoKey, FUN_OBJECT(fun)))
            return NULL;
    }

    /*
     * Set default values and add methods. We do it only for Error.prototype
     * as the rest of exceptions delegate to it.
     */
    empty = STRING_TO_JSVAL(cx->runtime->emptyString);
    if (!JS_DefineProperty(cx, error_proto, js_message_str, empty,
                           NULL, NULL, JSPROP_ENUMERATE) ||
        !JS_DefineProperty(cx, error_proto, js_fileName_str, empty,
                           NULL, NULL, JSPROP_ENUMERATE) ||
        !JS_DefineProperty(cx, error_proto, js_lineNumber_str, JSVAL_ZERO,
                           NULL, NULL, JSPROP_ENUMERATE) ||
        !JS_DefineFunctions(cx, error_proto, exception_methods)) {
        return NULL;
    }

    return error_proto;
}