PageRenderTime 246ms CodeModel.GetById 31ms app.highlight 169ms RepoModel.GetById 32ms app.codeStats 0ms

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

http://github.com/onedayitwillmake/RealtimeMultiplayerNodeJs
C++ | 1394 lines | 951 code | 150 blank | 293 comment | 236 complexity | 221c2c0028dee65e33af630c8ce805fb 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=80:
   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 execution context.
  43 */
  44#include "jsstddef.h"
  45#include <stdarg.h>
  46#include <stdlib.h>
  47#include <string.h>
  48#include "jstypes.h"
  49#include "jsarena.h" /* Added by JSIFY */
  50#include "jsutil.h" /* Added by JSIFY */
  51#include "jsclist.h"
  52#include "jsprf.h"
  53#include "jsatom.h"
  54#include "jscntxt.h"
  55#include "jsversion.h"
  56#include "jsdbgapi.h"
  57#include "jsexn.h"
  58#include "jsfun.h"
  59#include "jsgc.h"
  60#include "jslock.h"
  61#include "jsnum.h"
  62#include "jsobj.h"
  63#include "jsopcode.h"
  64#include "jsscan.h"
  65#include "jsscope.h"
  66#include "jsscript.h"
  67#include "jsstr.h"
  68#include "jstracer.h"
  69
  70#ifdef JS_THREADSAFE
  71#include "prtypes.h"
  72
  73/*
  74 * The index for JSThread info, returned by PR_NewThreadPrivateIndex.  The
  75 * index value is visible and shared by all threads, but the data associated
  76 * with it is private to each thread.
  77 */
  78static PRUintn threadTPIndex;
  79static JSBool  tpIndexInited = JS_FALSE;
  80
  81JS_BEGIN_EXTERN_C
  82JSBool
  83js_InitThreadPrivateIndex(void (*ptr)(void *))
  84{
  85    PRStatus status;
  86
  87    if (tpIndexInited)
  88        return JS_TRUE;
  89
  90    status = PR_NewThreadPrivateIndex(&threadTPIndex, ptr);
  91
  92    if (status == PR_SUCCESS)
  93        tpIndexInited = JS_TRUE;
  94    return status == PR_SUCCESS;
  95}
  96JS_END_EXTERN_C
  97
  98/*
  99 * Callback function to delete a JSThread info when the thread that owns it
 100 * is destroyed.
 101 */
 102void
 103js_ThreadDestructorCB(void *ptr)
 104{
 105    JSThread *thread = (JSThread *)ptr;
 106
 107    if (!thread)
 108        return;
 109
 110    /*
 111     * Check that this thread properly called either JS_DestroyContext or
 112     * JS_ClearContextThread on each JSContext it created or used.
 113     */
 114    JS_ASSERT(JS_CLIST_IS_EMPTY(&thread->contextList));
 115    GSN_CACHE_CLEAR(&thread->gsnCache);
 116#if defined JS_TRACER
 117    js_FinishJIT(&thread->traceMonitor);
 118#endif
 119    free(thread);
 120}
 121
 122/*
 123 * Get current thread-local JSThread info, creating one if it doesn't exist.
 124 * Each thread has a unique JSThread pointer.
 125 *
 126 * Since we are dealing with thread-local data, no lock is needed.
 127 *
 128 * Return a pointer to the thread local info, NULL if the system runs out
 129 * of memory, or it failed to set thread private data (neither case is very
 130 * likely; both are probably due to out-of-memory).  It is up to the caller
 131 * to report an error, if possible.
 132 */
 133JSThread *
 134js_GetCurrentThread(JSRuntime *rt)
 135{
 136    JSThread *thread;
 137
 138    thread = (JSThread *)PR_GetThreadPrivate(threadTPIndex);
 139    if (!thread) {
 140        thread = (JSThread *) malloc(sizeof(JSThread));
 141        if (!thread)
 142            return NULL;
 143#ifdef DEBUG
 144        memset(thread, JS_FREE_PATTERN, sizeof(JSThread));
 145#endif
 146        if (PR_FAILURE == PR_SetThreadPrivate(threadTPIndex, thread)) {
 147            free(thread);
 148            return NULL;
 149        }
 150
 151        JS_INIT_CLIST(&thread->contextList);
 152        thread->id = js_CurrentThreadId();
 153        thread->gcMallocBytes = 0;
 154#ifdef JS_TRACER
 155        memset(&thread->traceMonitor, 0, sizeof(thread->traceMonitor));
 156        js_InitJIT(&thread->traceMonitor);
 157#endif
 158        thread->scriptsToGC = NULL;
 159
 160        /*
 161         * js_SetContextThread initializes the remaining fields as necessary.
 162         */
 163    }
 164    return thread;
 165}
 166
 167/*
 168 * Sets current thread as owning thread of a context by assigning the
 169 * thread-private info to the context. If the current thread doesn't have
 170 * private JSThread info, create one.
 171 */
 172JSBool
 173js_SetContextThread(JSContext *cx)
 174{
 175    JSThread *thread = js_GetCurrentThread(cx->runtime);
 176
 177    if (!thread) {
 178        JS_ReportOutOfMemory(cx);
 179        return JS_FALSE;
 180    }
 181
 182    /*
 183     * Clear caches on each transition from 0 to 1 context active on the
 184     * current thread. See bug 425828.
 185     */
 186    if (JS_CLIST_IS_EMPTY(&thread->contextList)) {
 187        memset(&thread->gsnCache, 0, sizeof(thread->gsnCache));
 188        memset(&thread->propertyCache, 0, sizeof(thread->propertyCache));
 189    }
 190
 191    /* Assert that the previous cx->thread called JS_ClearContextThread(). */
 192    JS_ASSERT(!cx->thread || cx->thread == thread);
 193    if (!cx->thread)
 194        JS_APPEND_LINK(&cx->threadLinks, &thread->contextList);
 195    cx->thread = thread;
 196    return JS_TRUE;
 197}
 198
 199/* Remove the owning thread info of a context. */
 200void
 201js_ClearContextThread(JSContext *cx)
 202{
 203    /*
 204     * If cx is associated with a thread, this must be called only from that
 205     * thread.  If not, this is a harmless no-op.
 206     */
 207    JS_ASSERT(cx->thread == js_GetCurrentThread(cx->runtime) || !cx->thread);
 208    JS_REMOVE_AND_INIT_LINK(&cx->threadLinks);
 209    cx->thread = NULL;
 210}
 211
 212#endif /* JS_THREADSAFE */
 213
 214void
 215js_OnVersionChange(JSContext *cx)
 216{
 217#ifdef DEBUG
 218    JSVersion version = JSVERSION_NUMBER(cx);
 219
 220    JS_ASSERT(version == JSVERSION_DEFAULT || version >= JSVERSION_ECMA_3);
 221#endif
 222}
 223
 224void
 225js_SetVersion(JSContext *cx, JSVersion version)
 226{
 227    cx->version = version;
 228    js_OnVersionChange(cx);
 229}
 230
 231JSContext *
 232js_NewContext(JSRuntime *rt, size_t stackChunkSize)
 233{
 234    JSContext *cx;
 235    JSBool ok, first;
 236    JSContextCallback cxCallback;
 237
 238    cx = (JSContext *) malloc(sizeof *cx);
 239    if (!cx)
 240        return NULL;
 241    memset(cx, 0, sizeof *cx);
 242
 243    cx->runtime = rt;
 244    JS_ClearOperationCallback(cx);
 245    cx->debugHooks = &rt->globalDebugHooks;
 246#if JS_STACK_GROWTH_DIRECTION > 0
 247    cx->stackLimit = (jsuword)-1;
 248#endif
 249    cx->scriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA;
 250#ifdef JS_THREADSAFE
 251    cx->gcLocalFreeLists = (JSGCFreeListSet *) &js_GCEmptyFreeListSet;
 252    JS_INIT_CLIST(&cx->threadLinks);
 253    js_SetContextThread(cx);
 254#endif
 255
 256    JS_LOCK_GC(rt);
 257    for (;;) {
 258        first = (rt->contextList.next == &rt->contextList);
 259        if (rt->state == JSRTS_UP) {
 260            JS_ASSERT(!first);
 261            break;
 262        }
 263        if (rt->state == JSRTS_DOWN) {
 264            JS_ASSERT(first);
 265            rt->state = JSRTS_LAUNCHING;
 266            break;
 267        }
 268        JS_WAIT_CONDVAR(rt->stateChange, JS_NO_TIMEOUT);
 269    }
 270    JS_APPEND_LINK(&cx->links, &rt->contextList);
 271    JS_UNLOCK_GC(rt);
 272
 273    /*
 274     * First we do the infallible, every-time per-context initializations.
 275     * Should a later, fallible initialization (js_InitRegExpStatics, e.g.,
 276     * or the stuff under 'if (first)' below) fail, at least the version
 277     * and arena-pools will be valid and safe to use (say, from the last GC
 278     * done by js_DestroyContext).
 279     */
 280    cx->version = JSVERSION_DEFAULT;
 281    JS_INIT_ARENA_POOL(&cx->stackPool, "stack", stackChunkSize, sizeof(jsval),
 282                       &cx->scriptStackQuota);
 283
 284    JS_INIT_ARENA_POOL(&cx->tempPool, "temp",
 285                       1024,  /* FIXME: bug 421435 */
 286                       sizeof(jsdouble), &cx->scriptStackQuota);
 287
 288    /*
 289     * To avoid multiple allocations in InitMatch() (in jsregexp.c), the arena
 290     * size parameter should be at least as big as:
 291     *   INITIAL_BACKTRACK
 292     *   + (sizeof(REProgState) * INITIAL_STATESTACK)
 293     *   + (offsetof(REMatchState, parens) + avgParanSize * sizeof(RECapture))
 294     */
 295    JS_INIT_ARENA_POOL(&cx->regexpPool, "regexp",
 296                       12 * 1024 - 40,  /* FIXME: bug 421435 */
 297                       sizeof(void *), &cx->scriptStackQuota);
 298
 299    if (!js_InitRegExpStatics(cx, &cx->regExpStatics)) {
 300        js_DestroyContext(cx, JSDCM_NEW_FAILED);
 301        return NULL;
 302    }
 303
 304    cx->resolveFlags = 0;
 305
 306    /*
 307     * If cx is the first context on this runtime, initialize well-known atoms,
 308     * keywords, numbers, and strings.  If one of these steps should fail, the
 309     * runtime will be left in a partially initialized state, with zeroes and
 310     * nulls stored in the default-initialized remainder of the struct.  We'll
 311     * clean the runtime up under js_DestroyContext, because cx will be "last"
 312     * as well as "first".
 313     */
 314    if (first) {
 315#ifdef JS_THREADSAFE
 316        JS_BeginRequest(cx);
 317#endif
 318        ok = js_InitCommonAtoms(cx);
 319
 320        /*
 321         * scriptFilenameTable may be left over from a previous episode of
 322         * non-zero contexts alive in rt, so don't re-init the table if it's
 323         * not necessary.
 324         */
 325        if (ok && !rt->scriptFilenameTable)
 326            ok = js_InitRuntimeScriptState(rt);
 327        if (ok)
 328            ok = js_InitRuntimeNumberState(cx);
 329        if (ok)
 330            ok = js_InitRuntimeStringState(cx);
 331#ifdef JS_THREADSAFE
 332        JS_EndRequest(cx);
 333#endif
 334        if (!ok) {
 335            js_DestroyContext(cx, JSDCM_NEW_FAILED);
 336            return NULL;
 337        }
 338
 339        JS_LOCK_GC(rt);
 340        rt->state = JSRTS_UP;
 341        JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
 342        JS_UNLOCK_GC(rt);
 343    }
 344
 345    cxCallback = rt->cxCallback;
 346    if (cxCallback && !cxCallback(cx, JSCONTEXT_NEW)) {
 347        js_DestroyContext(cx, JSDCM_NEW_FAILED);
 348        return NULL;
 349    }
 350
 351    return cx;
 352}
 353
 354void
 355js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
 356{
 357    JSRuntime *rt;
 358    JSContextCallback cxCallback;
 359    JSBool last;
 360    JSArgumentFormatMap *map;
 361    JSLocalRootStack *lrs;
 362    JSLocalRootChunk *lrc;
 363
 364    rt = cx->runtime;
 365
 366    if (mode != JSDCM_NEW_FAILED) {
 367        cxCallback = rt->cxCallback;
 368        if (cxCallback) {
 369            /*
 370             * JSCONTEXT_DESTROY callback is not allowed to fail and must
 371             * return true.
 372             */
 373#ifdef DEBUG
 374            JSBool callbackStatus =
 375#endif
 376            cxCallback(cx, JSCONTEXT_DESTROY);
 377            JS_ASSERT(callbackStatus);
 378        }
 379    }
 380
 381    /* Remove cx from context list first. */
 382    JS_LOCK_GC(rt);
 383    JS_ASSERT(rt->state == JSRTS_UP || rt->state == JSRTS_LAUNCHING);
 384    JS_REMOVE_LINK(&cx->links);
 385    last = (rt->contextList.next == &rt->contextList);
 386    if (last)
 387        rt->state = JSRTS_LANDING;
 388#ifdef JS_THREADSAFE
 389    js_RevokeGCLocalFreeLists(cx);
 390#endif
 391    JS_UNLOCK_GC(rt);
 392
 393    if (last) {
 394#ifdef JS_THREADSAFE
 395        /*
 396         * If cx is not in a request already, begin one now so that we wait
 397         * for any racing GC started on a not-last context to finish, before
 398         * we plow ahead and unpin atoms.  Note that even though we begin a
 399         * request here if necessary, we end all requests on cx below before
 400         * forcing a final GC.  This lets any not-last context destruction
 401         * racing in another thread try to force or maybe run the GC, but by
 402         * that point, rt->state will not be JSRTS_UP, and that GC attempt
 403         * will return early.
 404         */
 405        if (cx->requestDepth == 0)
 406            JS_BeginRequest(cx);
 407#endif
 408
 409        /* Unlock and clear GC things held by runtime pointers. */
 410        js_FinishRuntimeNumberState(cx);
 411        js_FinishRuntimeStringState(cx);
 412
 413        /* Unpin all common atoms before final GC. */
 414        js_FinishCommonAtoms(cx);
 415
 416        /* Clear debugging state to remove GC roots. */
 417        JS_ClearAllTraps(cx);
 418        JS_ClearAllWatchPoints(cx);
 419    }
 420
 421    /*
 422     * Remove more GC roots in regExpStatics, then collect garbage.
 423     * XXX anti-modularity alert: we rely on the call to js_RemoveRoot within
 424     * XXX this function call to wait for any racing GC to complete, in the
 425     * XXX case where JS_DestroyContext is called outside of a request on cx
 426     */
 427    js_FreeRegExpStatics(cx, &cx->regExpStatics);
 428
 429#ifdef JS_THREADSAFE
 430    /*
 431     * Destroying a context implicitly calls JS_EndRequest().  Also, we must
 432     * end our request here in case we are "last" -- in that event, another
 433     * js_DestroyContext that was not last might be waiting in the GC for our
 434     * request to end.  We'll let it run below, just before we do the truly
 435     * final GC and then free atom state.
 436     *
 437     * At this point, cx must be inaccessible to other threads.  It's off the
 438     * rt->contextList, and it should not be reachable via any object private
 439     * data structure.
 440     */
 441    while (cx->requestDepth != 0)
 442        JS_EndRequest(cx);
 443#endif
 444
 445    if (last) {
 446        js_GC(cx, GC_LAST_CONTEXT);
 447
 448        /*
 449         * Free the script filename table if it exists and is empty. Do this
 450         * after the last GC to avoid finalizers tripping on free memory.
 451         */
 452        if (rt->scriptFilenameTable && rt->scriptFilenameTable->nentries == 0)
 453            js_FinishRuntimeScriptState(rt);
 454
 455        /* Take the runtime down, now that it has no contexts or atoms. */
 456        JS_LOCK_GC(rt);
 457        rt->state = JSRTS_DOWN;
 458        JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
 459        JS_UNLOCK_GC(rt);
 460    } else {
 461        if (mode == JSDCM_FORCE_GC)
 462            js_GC(cx, GC_NORMAL);
 463        else if (mode == JSDCM_MAYBE_GC)
 464            JS_MaybeGC(cx);
 465    }
 466
 467    /* Free the stuff hanging off of cx. */
 468    JS_FinishArenaPool(&cx->stackPool);
 469    JS_FinishArenaPool(&cx->tempPool);
 470    JS_FinishArenaPool(&cx->regexpPool);
 471
 472    if (cx->lastMessage)
 473        free(cx->lastMessage);
 474
 475    /* Remove any argument formatters. */
 476    map = cx->argumentFormatMap;
 477    while (map) {
 478        JSArgumentFormatMap *temp = map;
 479        map = map->next;
 480        JS_free(cx, temp);
 481    }
 482
 483    /* Destroy the resolve recursion damper. */
 484    if (cx->resolvingTable) {
 485        JS_DHashTableDestroy(cx->resolvingTable);
 486        cx->resolvingTable = NULL;
 487    }
 488
 489    lrs = cx->localRootStack;
 490    if (lrs) {
 491        while ((lrc = lrs->topChunk) != &lrs->firstChunk) {
 492            lrs->topChunk = lrc->down;
 493            JS_free(cx, lrc);
 494        }
 495        JS_free(cx, lrs);
 496    }
 497
 498#ifdef JS_THREADSAFE
 499    js_ClearContextThread(cx);
 500#endif
 501
 502    /* Finally, free cx itself. */
 503    free(cx);
 504}
 505
 506JSBool
 507js_ValidContextPointer(JSRuntime *rt, JSContext *cx)
 508{
 509    JSCList *cl;
 510
 511    for (cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next) {
 512        if (cl == &cx->links)
 513            return JS_TRUE;
 514    }
 515    JS_RUNTIME_METER(rt, deadContexts);
 516    return JS_FALSE;
 517}
 518
 519JSContext *
 520js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp)
 521{
 522    JSContext *cx = *iterp;
 523
 524    if (unlocked)
 525        JS_LOCK_GC(rt);
 526    cx = (JSContext *) (cx ? cx->links.next : rt->contextList.next);
 527    if (&cx->links == &rt->contextList)
 528        cx = NULL;
 529    *iterp = cx;
 530    if (unlocked)
 531        JS_UNLOCK_GC(rt);
 532    return cx;
 533}
 534
 535static JSDHashNumber
 536resolving_HashKey(JSDHashTable *table, const void *ptr)
 537{
 538    const JSResolvingKey *key = (const JSResolvingKey *)ptr;
 539
 540    return ((JSDHashNumber)JS_PTR_TO_UINT32(key->obj) >> JSVAL_TAGBITS) ^ key->id;
 541}
 542
 543JS_PUBLIC_API(JSBool)
 544resolving_MatchEntry(JSDHashTable *table,
 545                     const JSDHashEntryHdr *hdr,
 546                     const void *ptr)
 547{
 548    const JSResolvingEntry *entry = (const JSResolvingEntry *)hdr;
 549    const JSResolvingKey *key = (const JSResolvingKey *)ptr;
 550
 551    return entry->key.obj == key->obj && entry->key.id == key->id;
 552}
 553
 554static const JSDHashTableOps resolving_dhash_ops = {
 555    JS_DHashAllocTable,
 556    JS_DHashFreeTable,
 557    resolving_HashKey,
 558    resolving_MatchEntry,
 559    JS_DHashMoveEntryStub,
 560    JS_DHashClearEntryStub,
 561    JS_DHashFinalizeStub,
 562    NULL
 563};
 564
 565JSBool
 566js_StartResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
 567                  JSResolvingEntry **entryp)
 568{
 569    JSDHashTable *table;
 570    JSResolvingEntry *entry;
 571
 572    table = cx->resolvingTable;
 573    if (!table) {
 574        table = JS_NewDHashTable(&resolving_dhash_ops, NULL,
 575                                 sizeof(JSResolvingEntry),
 576                                 JS_DHASH_MIN_SIZE);
 577        if (!table)
 578            goto outofmem;
 579        cx->resolvingTable = table;
 580    }
 581
 582    entry = (JSResolvingEntry *)
 583            JS_DHashTableOperate(table, key, JS_DHASH_ADD);
 584    if (!entry)
 585        goto outofmem;
 586
 587    if (entry->flags & flag) {
 588        /* An entry for (key, flag) exists already -- dampen recursion. */
 589        entry = NULL;
 590    } else {
 591        /* Fill in key if we were the first to add entry, then set flag. */
 592        if (!entry->key.obj)
 593            entry->key = *key;
 594        entry->flags |= flag;
 595    }
 596    *entryp = entry;
 597    return JS_TRUE;
 598
 599outofmem:
 600    JS_ReportOutOfMemory(cx);
 601    return JS_FALSE;
 602}
 603
 604void
 605js_StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
 606                 JSResolvingEntry *entry, uint32 generation)
 607{
 608    JSDHashTable *table;
 609
 610    /*
 611     * Clear flag from entry->flags and return early if other flags remain.
 612     * We must take care to re-lookup entry if the table has changed since
 613     * it was found by js_StartResolving.
 614     */
 615    table = cx->resolvingTable;
 616    if (!entry || table->generation != generation) {
 617        entry = (JSResolvingEntry *)
 618                JS_DHashTableOperate(table, key, JS_DHASH_LOOKUP);
 619    }
 620    JS_ASSERT(JS_DHASH_ENTRY_IS_BUSY(&entry->hdr));
 621    entry->flags &= ~flag;
 622    if (entry->flags)
 623        return;
 624
 625    /*
 626     * Do a raw remove only if fewer entries were removed than would cause
 627     * alpha to be less than .5 (alpha is at most .75).  Otherwise, we just
 628     * call JS_DHashTableOperate to re-lookup the key and remove its entry,
 629     * compressing or shrinking the table as needed.
 630     */
 631    if (table->removedCount < JS_DHASH_TABLE_SIZE(table) >> 2)
 632        JS_DHashTableRawRemove(table, &entry->hdr);
 633    else
 634        JS_DHashTableOperate(table, key, JS_DHASH_REMOVE);
 635}
 636
 637JSBool
 638js_EnterLocalRootScope(JSContext *cx)
 639{
 640    JSLocalRootStack *lrs;
 641    int mark;
 642
 643    lrs = cx->localRootStack;
 644    if (!lrs) {
 645        lrs = (JSLocalRootStack *) JS_malloc(cx, sizeof *lrs);
 646        if (!lrs)
 647            return JS_FALSE;
 648        lrs->scopeMark = JSLRS_NULL_MARK;
 649        lrs->rootCount = 0;
 650        lrs->topChunk = &lrs->firstChunk;
 651        lrs->firstChunk.down = NULL;
 652        cx->localRootStack = lrs;
 653    }
 654
 655    /* Push lrs->scopeMark to save it for restore when leaving. */
 656    mark = js_PushLocalRoot(cx, lrs, INT_TO_JSVAL(lrs->scopeMark));
 657    if (mark < 0)
 658        return JS_FALSE;
 659    lrs->scopeMark = (uint32) mark;
 660    return JS_TRUE;
 661}
 662
 663void
 664js_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval)
 665{
 666    JSLocalRootStack *lrs;
 667    uint32 mark, m, n;
 668    JSLocalRootChunk *lrc;
 669
 670    /* Defend against buggy native callers. */
 671    lrs = cx->localRootStack;
 672    JS_ASSERT(lrs && lrs->rootCount != 0);
 673    if (!lrs || lrs->rootCount == 0)
 674        return;
 675
 676    mark = lrs->scopeMark;
 677    JS_ASSERT(mark != JSLRS_NULL_MARK);
 678    if (mark == JSLRS_NULL_MARK)
 679        return;
 680
 681    /* Free any chunks being popped by this leave operation. */
 682    m = mark >> JSLRS_CHUNK_SHIFT;
 683    n = (lrs->rootCount - 1) >> JSLRS_CHUNK_SHIFT;
 684    while (n > m) {
 685        lrc = lrs->topChunk;
 686        JS_ASSERT(lrc != &lrs->firstChunk);
 687        lrs->topChunk = lrc->down;
 688        JS_free(cx, lrc);
 689        --n;
 690    }
 691
 692    /*
 693     * Pop the scope, restoring lrs->scopeMark.  If rval is a GC-thing, push
 694     * it on the caller's scope, or store it in lastInternalResult if we are
 695     * leaving the outermost scope.  We don't need to allocate a new lrc
 696     * because we can overwrite the old mark's slot with rval.
 697     */
 698    lrc = lrs->topChunk;
 699    m = mark & JSLRS_CHUNK_MASK;
 700    lrs->scopeMark = (uint32) JSVAL_TO_INT(lrc->roots[m]);
 701    if (JSVAL_IS_GCTHING(rval) && !JSVAL_IS_NULL(rval)) {
 702        if (mark == 0) {
 703            cx->weakRoots.lastInternalResult = rval;
 704        } else {
 705            /*
 706             * Increment m to avoid the "else if (m == 0)" case below.  If
 707             * rval is not a GC-thing, that case would take care of freeing
 708             * any chunk that contained only the old mark.  Since rval *is*
 709             * a GC-thing here, we want to reuse that old mark's slot.
 710             */
 711            lrc->roots[m++] = rval;
 712            ++mark;
 713        }
 714    }
 715    lrs->rootCount = (uint32) mark;
 716
 717    /*
 718     * Free the stack eagerly, risking malloc churn.  The alternative would
 719     * require an lrs->entryCount member, maintained by Enter and Leave, and
 720     * tested by the GC in addition to the cx->localRootStack non-null test.
 721     *
 722     * That approach would risk hoarding 264 bytes (net) per context.  Right
 723     * now it seems better to give fresh (dirty in CPU write-back cache, and
 724     * the data is no longer needed) memory back to the malloc heap.
 725     */
 726    if (mark == 0) {
 727        cx->localRootStack = NULL;
 728        JS_free(cx, lrs);
 729    } else if (m == 0) {
 730        lrs->topChunk = lrc->down;
 731        JS_free(cx, lrc);
 732    }
 733}
 734
 735void
 736js_ForgetLocalRoot(JSContext *cx, jsval v)
 737{
 738    JSLocalRootStack *lrs;
 739    uint32 i, j, m, n, mark;
 740    JSLocalRootChunk *lrc, *lrc2;
 741    jsval top;
 742
 743    lrs = cx->localRootStack;
 744    JS_ASSERT(lrs && lrs->rootCount);
 745    if (!lrs || lrs->rootCount == 0)
 746        return;
 747
 748    /* Prepare to pop the top-most value from the stack. */
 749    n = lrs->rootCount - 1;
 750    m = n & JSLRS_CHUNK_MASK;
 751    lrc = lrs->topChunk;
 752    top = lrc->roots[m];
 753
 754    /* Be paranoid about calls on an empty scope. */
 755    mark = lrs->scopeMark;
 756    JS_ASSERT(mark < n);
 757    if (mark >= n)
 758        return;
 759
 760    /* If v was not the last root pushed in the top scope, find it. */
 761    if (top != v) {
 762        /* Search downward in case v was recently pushed. */
 763        i = n;
 764        j = m;
 765        lrc2 = lrc;
 766        while (--i > mark) {
 767            if (j == 0)
 768                lrc2 = lrc2->down;
 769            j = i & JSLRS_CHUNK_MASK;
 770            if (lrc2->roots[j] == v)
 771                break;
 772        }
 773
 774        /* If we didn't find v in this scope, assert and bail out. */
 775        JS_ASSERT(i != mark);
 776        if (i == mark)
 777            return;
 778
 779        /* Swap top and v so common tail code can pop v. */
 780        lrc2->roots[j] = top;
 781    }
 782
 783    /* Pop the last value from the stack. */
 784    lrc->roots[m] = JSVAL_NULL;
 785    lrs->rootCount = n;
 786    if (m == 0) {
 787        JS_ASSERT(n != 0);
 788        JS_ASSERT(lrc != &lrs->firstChunk);
 789        lrs->topChunk = lrc->down;
 790        JS_free(cx, lrc);
 791    }
 792}
 793
 794int
 795js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, jsval v)
 796{
 797    uint32 n, m;
 798    JSLocalRootChunk *lrc;
 799
 800    n = lrs->rootCount;
 801    m = n & JSLRS_CHUNK_MASK;
 802    if (n == 0 || m != 0) {
 803        /*
 804         * At start of first chunk, or not at start of a non-first top chunk.
 805         * Check for lrs->rootCount overflow.
 806         */
 807        if ((uint32)(n + 1) == 0) {
 808            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 809                                 JSMSG_TOO_MANY_LOCAL_ROOTS);
 810            return -1;
 811        }
 812        lrc = lrs->topChunk;
 813        JS_ASSERT(n != 0 || lrc == &lrs->firstChunk);
 814    } else {
 815        /*
 816         * After lrs->firstChunk, trying to index at a power-of-two chunk
 817         * boundary: need a new chunk.
 818         */
 819        lrc = (JSLocalRootChunk *) JS_malloc(cx, sizeof *lrc);
 820        if (!lrc)
 821            return -1;
 822        lrc->down = lrs->topChunk;
 823        lrs->topChunk = lrc;
 824    }
 825    lrs->rootCount = n + 1;
 826    lrc->roots[m] = v;
 827    return (int) n;
 828}
 829
 830void
 831js_TraceLocalRoots(JSTracer *trc, JSLocalRootStack *lrs)
 832{
 833    uint32 n, m, mark;
 834    JSLocalRootChunk *lrc;
 835    jsval v;
 836
 837    n = lrs->rootCount;
 838    if (n == 0)
 839        return;
 840
 841    mark = lrs->scopeMark;
 842    lrc = lrs->topChunk;
 843    do {
 844        while (--n > mark) {
 845            m = n & JSLRS_CHUNK_MASK;
 846            v = lrc->roots[m];
 847            JS_ASSERT(JSVAL_IS_GCTHING(v) && v != JSVAL_NULL);
 848            JS_SET_TRACING_INDEX(trc, "local_root", n);
 849            js_CallValueTracerIfGCThing(trc, v);
 850            if (m == 0)
 851                lrc = lrc->down;
 852        }
 853        m = n & JSLRS_CHUNK_MASK;
 854        mark = JSVAL_TO_INT(lrc->roots[m]);
 855        if (m == 0)
 856            lrc = lrc->down;
 857    } while (n != 0);
 858    JS_ASSERT(!lrc);
 859}
 860
 861static void
 862ReportError(JSContext *cx, const char *message, JSErrorReport *reportp)
 863{
 864    /*
 865     * Check the error report, and set a JavaScript-catchable exception
 866     * if the error is defined to have an associated exception.  If an
 867     * exception is thrown, then the JSREPORT_EXCEPTION flag will be set
 868     * on the error report, and exception-aware hosts should ignore it.
 869     */
 870    JS_ASSERT(reportp);
 871    if (reportp->errorNumber == JSMSG_UNCAUGHT_EXCEPTION)
 872        reportp->flags |= JSREPORT_EXCEPTION;
 873
 874    /*
 875     * Call the error reporter only if an exception wasn't raised.
 876     *
 877     * If an exception was raised, then we call the debugErrorHook
 878     * (if present) to give it a chance to see the error before it
 879     * propagates out of scope.  This is needed for compatability
 880     * with the old scheme.
 881     */
 882    if (!cx->fp || !js_ErrorToException(cx, message, reportp)) {
 883        js_ReportErrorAgain(cx, message, reportp);
 884    } else if (cx->debugHooks->debugErrorHook && cx->errorReporter) {
 885        JSDebugErrorHook hook = cx->debugHooks->debugErrorHook;
 886        /* test local in case debugErrorHook changed on another thread */
 887        if (hook)
 888            hook(cx, message, reportp, cx->debugHooks->debugErrorHookData);
 889    }
 890}
 891
 892/*
 893 * We don't post an exception in this case, since doing so runs into
 894 * complications of pre-allocating an exception object which required
 895 * running the Exception class initializer early etc.
 896 * Instead we just invoke the errorReporter with an "Out Of Memory"
 897 * type message, and then hope the process ends swiftly.
 898 */
 899void
 900js_ReportOutOfMemory(JSContext *cx)
 901{
 902    JSStackFrame *fp;
 903    JSErrorReport report;
 904    JSErrorReporter onError = cx->errorReporter;
 905
 906    /* Get the message for this error, but we won't expand any arguments. */
 907    const JSErrorFormatString *efs =
 908        js_GetLocalizedErrorMessage(cx, NULL, NULL, JSMSG_OUT_OF_MEMORY);
 909    const char *msg = efs ? efs->format : "Out of memory";
 910
 911    /* Fill out the report, but don't do anything that requires allocation. */
 912    memset(&report, 0, sizeof (struct JSErrorReport));
 913    report.flags = JSREPORT_ERROR;
 914    report.errorNumber = JSMSG_OUT_OF_MEMORY;
 915
 916    /*
 917     * Walk stack until we find a frame that is associated with some script
 918     * rather than a native frame.
 919     */
 920    for (fp = cx->fp; fp; fp = fp->down) {
 921        if (fp->regs) {
 922            report.filename = fp->script->filename;
 923            report.lineno = js_FramePCToLineNumber(cx, fp);
 924            break;
 925        }
 926    }
 927
 928    /*
 929     * If debugErrorHook is present then we give it a chance to veto sending
 930     * the error on to the regular ErrorReporter. We also clear a pending
 931     * exception if any now so the hooks can replace the out-of-memory error
 932     * by a script-catchable exception.
 933     */
 934    cx->throwing = JS_FALSE;
 935    if (onError) {
 936        JSDebugErrorHook hook = cx->debugHooks->debugErrorHook;
 937        if (hook &&
 938            !hook(cx, msg, &report, cx->debugHooks->debugErrorHookData)) {
 939            onError = NULL;
 940        }
 941    }
 942
 943    if (onError)
 944        onError(cx, msg, &report);
 945}
 946
 947void
 948js_ReportOutOfScriptQuota(JSContext *cx)
 949{
 950    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 951                         JSMSG_SCRIPT_STACK_QUOTA);
 952}
 953
 954void
 955js_ReportOverRecursed(JSContext *cx)
 956{
 957    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
 958}
 959
 960void
 961js_ReportAllocationOverflow(JSContext *cx)
 962{
 963    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_ALLOC_OVERFLOW);
 964}
 965
 966JSBool
 967js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap)
 968{
 969    char *message;
 970    jschar *ucmessage;
 971    size_t messagelen;
 972    JSStackFrame *fp;
 973    JSErrorReport report;
 974    JSBool warning;
 975
 976    if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx))
 977        return JS_TRUE;
 978
 979    message = JS_vsmprintf(format, ap);
 980    if (!message)
 981        return JS_FALSE;
 982    messagelen = strlen(message);
 983
 984    memset(&report, 0, sizeof (struct JSErrorReport));
 985    report.flags = flags;
 986    report.errorNumber = JSMSG_USER_DEFINED_ERROR;
 987    report.ucmessage = ucmessage = js_InflateString(cx, message, &messagelen);
 988
 989    /* Find the top-most active script frame, for best line number blame. */
 990    for (fp = cx->fp; fp; fp = fp->down) {
 991        if (fp->regs) {
 992            report.filename = fp->script->filename;
 993            report.lineno = js_FramePCToLineNumber(cx, fp);
 994            break;
 995        }
 996    }
 997
 998    warning = JSREPORT_IS_WARNING(report.flags);
 999    if (warning && JS_HAS_WERROR_OPTION(cx)) {
1000        report.flags &= ~JSREPORT_WARNING;
1001        warning = JS_FALSE;
1002    }
1003
1004    ReportError(cx, message, &report);
1005    free(message);
1006    JS_free(cx, ucmessage);
1007    return warning;
1008}
1009
1010/*
1011 * The arguments from ap need to be packaged up into an array and stored
1012 * into the report struct.
1013 *
1014 * The format string addressed by the error number may contain operands
1015 * identified by the format {N}, where N is a decimal digit. Each of these
1016 * is to be replaced by the Nth argument from the va_list. The complete
1017 * message is placed into reportp->ucmessage converted to a JSString.
1018 *
1019 * Returns true if the expansion succeeds (can fail if out of memory).
1020 */
1021JSBool
1022js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback,
1023                        void *userRef, const uintN errorNumber,
1024                        char **messagep, JSErrorReport *reportp,
1025                        JSBool *warningp, JSBool charArgs, va_list ap)
1026{
1027    const JSErrorFormatString *efs;
1028    int i;
1029    int argCount;
1030
1031    *warningp = JSREPORT_IS_WARNING(reportp->flags);
1032    if (*warningp && JS_HAS_WERROR_OPTION(cx)) {
1033        reportp->flags &= ~JSREPORT_WARNING;
1034        *warningp = JS_FALSE;
1035    }
1036
1037    *messagep = NULL;
1038
1039    /* Most calls supply js_GetErrorMessage; if this is so, assume NULL. */
1040    if (!callback || callback == js_GetErrorMessage)
1041        efs = js_GetLocalizedErrorMessage(cx, userRef, NULL, errorNumber);
1042    else
1043        efs = callback(userRef, NULL, errorNumber);
1044    if (efs) {
1045        size_t totalArgsLength = 0;
1046        size_t argLengths[10]; /* only {0} thru {9} supported */
1047        argCount = efs->argCount;
1048        JS_ASSERT(argCount <= 10);
1049        if (argCount > 0) {
1050            /*
1051             * Gather the arguments into an array, and accumulate
1052             * their sizes. We allocate 1 more than necessary and
1053             * null it out to act as the caboose when we free the
1054             * pointers later.
1055             */
1056            reportp->messageArgs = (const jschar **)
1057                JS_malloc(cx, sizeof(jschar *) * (argCount + 1));
1058            if (!reportp->messageArgs)
1059                return JS_FALSE;
1060            reportp->messageArgs[argCount] = NULL;
1061            for (i = 0; i < argCount; i++) {
1062                if (charArgs) {
1063                    char *charArg = va_arg(ap, char *);
1064                    size_t charArgLength = strlen(charArg);
1065                    reportp->messageArgs[i]
1066                        = js_InflateString(cx, charArg, &charArgLength);
1067                    if (!reportp->messageArgs[i])
1068                        goto error;
1069                } else {
1070                    reportp->messageArgs[i] = va_arg(ap, jschar *);
1071                }
1072                argLengths[i] = js_strlen(reportp->messageArgs[i]);
1073                totalArgsLength += argLengths[i];
1074            }
1075            /* NULL-terminate for easy copying. */
1076            reportp->messageArgs[i] = NULL;
1077        }
1078        /*
1079         * Parse the error format, substituting the argument X
1080         * for {X} in the format.
1081         */
1082        if (argCount > 0) {
1083            if (efs->format) {
1084                jschar *buffer, *fmt, *out;
1085                int expandedArgs = 0;
1086                size_t expandedLength;
1087                size_t len = strlen(efs->format);
1088
1089                buffer = fmt = js_InflateString (cx, efs->format, &len);
1090                if (!buffer)
1091                    goto error;
1092                expandedLength = len
1093                                 - (3 * argCount)       /* exclude the {n} */
1094                                 + totalArgsLength;
1095
1096                /*
1097                * Note - the above calculation assumes that each argument
1098                * is used once and only once in the expansion !!!
1099                */
1100                reportp->ucmessage = out = (jschar *)
1101                    JS_malloc(cx, (expandedLength + 1) * sizeof(jschar));
1102                if (!out) {
1103                    JS_free (cx, buffer);
1104                    goto error;
1105                }
1106                while (*fmt) {
1107                    if (*fmt == '{') {
1108                        if (isdigit(fmt[1])) {
1109                            int d = JS7_UNDEC(fmt[1]);
1110                            JS_ASSERT(d < argCount);
1111                            js_strncpy(out, reportp->messageArgs[d],
1112                                       argLengths[d]);
1113                            out += argLengths[d];
1114                            fmt += 3;
1115                            expandedArgs++;
1116                            continue;
1117                        }
1118                    }
1119                    *out++ = *fmt++;
1120                }
1121                JS_ASSERT(expandedArgs == argCount);
1122                *out = 0;
1123                JS_free (cx, buffer);
1124                *messagep =
1125                    js_DeflateString(cx, reportp->ucmessage,
1126                                     (size_t)(out - reportp->ucmessage));
1127                if (!*messagep)
1128                    goto error;
1129            }
1130        } else {
1131            /*
1132             * Zero arguments: the format string (if it exists) is the
1133             * entire message.
1134             */
1135            if (efs->format) {
1136                size_t len;
1137                *messagep = JS_strdup(cx, efs->format);
1138                if (!*messagep)
1139                    goto error;
1140                len = strlen(*messagep);
1141                reportp->ucmessage = js_InflateString(cx, *messagep, &len);
1142                if (!reportp->ucmessage)
1143                    goto error;
1144            }
1145        }
1146    }
1147    if (*messagep == NULL) {
1148        /* where's the right place for this ??? */
1149        const char *defaultErrorMessage
1150            = "No error message available for error number %d";
1151        size_t nbytes = strlen(defaultErrorMessage) + 16;
1152        *messagep = (char *)JS_malloc(cx, nbytes);
1153        if (!*messagep)
1154            goto error;
1155        JS_snprintf(*messagep, nbytes, defaultErrorMessage, errorNumber);
1156    }
1157    return JS_TRUE;
1158
1159error:
1160    if (reportp->messageArgs) {
1161        /* free the arguments only if we allocated them */
1162        if (charArgs) {
1163            i = 0;
1164            while (reportp->messageArgs[i])
1165                JS_free(cx, (void *)reportp->messageArgs[i++]);
1166        }
1167        JS_free(cx, (void *)reportp->messageArgs);
1168        reportp->messageArgs = NULL;
1169    }
1170    if (reportp->ucmessage) {
1171        JS_free(cx, (void *)reportp->ucmessage);
1172        reportp->ucmessage = NULL;
1173    }
1174    if (*messagep) {
1175        JS_free(cx, (void *)*messagep);
1176        *messagep = NULL;
1177    }
1178    return JS_FALSE;
1179}
1180
1181JSBool
1182js_ReportErrorNumberVA(JSContext *cx, uintN flags, JSErrorCallback callback,
1183                       void *userRef, const uintN errorNumber,
1184                       JSBool charArgs, va_list ap)
1185{
1186    JSStackFrame *fp;
1187    JSErrorReport report;
1188    char *message;
1189    JSBool warning;
1190
1191    if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx))
1192        return JS_TRUE;
1193
1194    memset(&report, 0, sizeof (struct JSErrorReport));
1195    report.flags = flags;
1196    report.errorNumber = errorNumber;
1197
1198    /*
1199     * If we can't find out where the error was based on the current frame,
1200     * see if the next frame has a script/pc combo we can use.
1201     */
1202    for (fp = cx->fp; fp; fp = fp->down) {
1203        if (fp->regs) {
1204            report.filename = fp->script->filename;
1205            report.lineno = js_FramePCToLineNumber(cx, fp);
1206            break;
1207        }
1208    }
1209
1210    if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber,
1211                                 &message, &report, &warning, charArgs, ap)) {
1212        return JS_FALSE;
1213    }
1214
1215    ReportError(cx, message, &report);
1216
1217    if (message)
1218        JS_free(cx, message);
1219    if (report.messageArgs) {
1220        /*
1221         * js_ExpandErrorArguments owns its messageArgs only if it had to
1222         * inflate the arguments (from regular |char *|s).
1223         */
1224        if (charArgs) {
1225            int i = 0;
1226            while (report.messageArgs[i])
1227                JS_free(cx, (void *)report.messageArgs[i++]);
1228        }
1229        JS_free(cx, (void *)report.messageArgs);
1230    }
1231    if (report.ucmessage)
1232        JS_free(cx, (void *)report.ucmessage);
1233
1234    return warning;
1235}
1236
1237JS_FRIEND_API(void)
1238js_ReportErrorAgain(JSContext *cx, const char *message, JSErrorReport *reportp)
1239{
1240    JSErrorReporter onError;
1241
1242    if (!message)
1243        return;
1244
1245    if (cx->lastMessage)
1246        free(cx->lastMessage);
1247    cx->lastMessage = JS_strdup(cx, message);
1248    if (!cx->lastMessage)
1249        return;
1250    onError = cx->errorReporter;
1251
1252    /*
1253     * If debugErrorHook is present then we give it a chance to veto
1254     * sending the error on to the regular ErrorReporter.
1255     */
1256    if (onError) {
1257        JSDebugErrorHook hook = cx->debugHooks->debugErrorHook;
1258        if (hook &&
1259            !hook(cx, cx->lastMessage, reportp,
1260                  cx->debugHooks->debugErrorHookData)) {
1261            onError = NULL;
1262        }
1263    }
1264    if (onError)
1265        onError(cx, cx->lastMessage, reportp);
1266}
1267
1268void
1269js_ReportIsNotDefined(JSContext *cx, const char *name)
1270{
1271    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_DEFINED, name);
1272}
1273
1274JSBool
1275js_ReportIsNullOrUndefined(JSContext *cx, intN spindex, jsval v,
1276                           JSString *fallback)
1277{
1278    char *bytes;
1279    JSBool ok;
1280
1281    bytes = js_DecompileValueGenerator(cx, spindex, v, fallback);
1282    if (!bytes)
1283        return JS_FALSE;
1284
1285    if (strcmp(bytes, js_undefined_str) == 0 ||
1286        strcmp(bytes, js_null_str) == 0) {
1287        ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
1288                                          js_GetErrorMessage, NULL,
1289                                          JSMSG_NO_PROPERTIES, bytes,
1290                                          NULL, NULL);
1291    } else if (JSVAL_IS_VOID(v)) {
1292        ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
1293                                          js_GetErrorMessage, NULL,
1294                                          JSMSG_NULL_OR_UNDEFINED, bytes,
1295                                          js_undefined_str, NULL);
1296    } else {
1297        JS_ASSERT(JSVAL_IS_NULL(v));
1298        ok = JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
1299                                          js_GetErrorMessage, NULL,
1300                                          JSMSG_NULL_OR_UNDEFINED, bytes,
1301                                          js_null_str, NULL);
1302    }
1303
1304    JS_free(cx, bytes);
1305    return ok;
1306}
1307
1308void
1309js_ReportMissingArg(JSContext *cx, jsval *vp, uintN arg)
1310{
1311    char argbuf[11];
1312    char *bytes;
1313    JSAtom *atom;
1314
1315    JS_snprintf(argbuf, sizeof argbuf, "%u", arg);
1316    bytes = NULL;
1317    if (VALUE_IS_FUNCTION(cx, *vp)) {
1318        atom = GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(*vp))->atom;
1319        bytes = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, *vp,
1320                                           ATOM_TO_STRING(atom));
1321        if (!bytes)
1322            return;
1323    }
1324    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1325                         JSMSG_MISSING_FUN_ARG, argbuf,
1326                         bytes ? bytes : "");
1327    JS_free(cx, bytes);
1328}
1329
1330JSBool
1331js_ReportValueErrorFlags(JSContext *cx, uintN flags, const uintN errorNumber,
1332                         intN spindex, jsval v, JSString *fallback,
1333                         const char *arg1, const char *arg2)
1334{
1335    char *bytes;
1336    JSBool ok;
1337
1338    JS_ASSERT(js_ErrorFormatString[errorNumber].argCount >= 1);
1339    JS_ASSERT(js_ErrorFormatString[errorNumber].argCount <= 3);
1340    bytes = js_DecompileValueGenerator(cx, spindex, v, fallback);
1341    if (!bytes)
1342        return JS_FALSE;
1343
1344    ok = JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage,
1345                                      NULL, errorNumber, bytes, arg1, arg2);
1346    JS_free(cx, bytes);
1347    return ok;
1348}
1349
1350#if defined DEBUG && defined XP_UNIX
1351/* For gdb usage. */
1352void js_traceon(JSContext *cx)  { cx->tracefp = stderr; }
1353void js_traceoff(JSContext *cx) { cx->tracefp = NULL; }
1354#endif
1355
1356JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = {
1357#define MSG_DEF(name, number, count, exception, format) \
1358    { format, count, exception } ,
1359#include "js.msg"
1360#undef MSG_DEF
1361};
1362
1363JS_FRIEND_API(const JSErrorFormatString *)
1364js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber)
1365{
1366    if ((errorNumber > 0) && (errorNumber < JSErr_Limit))
1367        return &js_ErrorFormatString[errorNumber];
1368    return NULL;
1369}
1370
1371JSBool
1372js_ResetOperationCount(JSContext *cx)
1373{
1374    JSScript *script;
1375
1376    JS_ASSERT(cx->operationCount <= 0);
1377    JS_ASSERT(cx->operationLimit > 0);
1378
1379    cx->operationCount = (int32) cx->operationLimit;
1380    if (cx->operationCallbackIsSet)
1381        return cx->operationCallback(cx);
1382
1383    if (cx->operationCallback) {
1384        /*
1385         * Invoke the deprecated branch callback. It may be called only when
1386         * the top-most frame is scripted or JSOPTION_NATIVE_BRANCH_CALLBACK
1387         * is set.
1388         */
1389        script = cx->fp ? cx->fp->script : NULL;
1390        if (script || JS_HAS_OPTION(cx, JSOPTION_NATIVE_BRANCH_CALLBACK))
1391            return ((JSBranchCallback) cx->operationCallback)(cx, script);
1392    }
1393    return JS_TRUE;
1394}