Back to index

lightning-sunbird  0.9+nobinonly
jscntxt.c
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  * vim: set ts=8 sw=4 et tw=80:
00003  *
00004  * ***** BEGIN LICENSE BLOCK *****
00005  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00006  *
00007  * The contents of this file are subject to the Mozilla Public License Version
00008  * 1.1 (the "License"); you may not use this file except in compliance with
00009  * the License. You may obtain a copy of the License at
00010  * http://www.mozilla.org/MPL/
00011  *
00012  * Software distributed under the License is distributed on an "AS IS" basis,
00013  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00014  * for the specific language governing rights and limitations under the
00015  * License.
00016  *
00017  * The Original Code is Mozilla Communicator client code, released
00018  * March 31, 1998.
00019  *
00020  * The Initial Developer of the Original Code is
00021  * Netscape Communications Corporation.
00022  * Portions created by the Initial Developer are Copyright (C) 1998
00023  * the Initial Developer. All Rights Reserved.
00024  *
00025  * Contributor(s):
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 /*
00042  * JS execution context.
00043  */
00044 #include "jsstddef.h"
00045 #include <stdarg.h>
00046 #include <stdlib.h>
00047 #include <string.h>
00048 #include "jstypes.h"
00049 #include "jsarena.h" /* Added by JSIFY */
00050 #include "jsutil.h" /* Added by JSIFY */
00051 #include "jsclist.h"
00052 #include "jsprf.h"
00053 #include "jsatom.h"
00054 #include "jscntxt.h"
00055 #include "jsconfig.h"
00056 #include "jsdbgapi.h"
00057 #include "jsexn.h"
00058 #include "jsgc.h"
00059 #include "jslock.h"
00060 #include "jsnum.h"
00061 #include "jsobj.h"
00062 #include "jsopcode.h"
00063 #include "jsscan.h"
00064 #include "jsscope.h"
00065 #include "jsscript.h"
00066 #include "jsstr.h"
00067 
00068 #ifdef JS_THREADSAFE
00069 
00070 /*
00071  * Callback function to delete a JSThread info when the thread that owns it
00072  * is destroyed.
00073  */
00074 void JS_DLL_CALLBACK
00075 js_ThreadDestructorCB(void *ptr)
00076 {
00077     JSThread *thread = (JSThread *)ptr;
00078 
00079     if (!thread)
00080         return;
00081     while (!JS_CLIST_IS_EMPTY(&thread->contextList)) {
00082         /* NB: use a temporary, as the macro evaluates its args many times. */
00083         JSCList *link = thread->contextList.next;
00084 
00085         JS_REMOVE_AND_INIT_LINK(link);
00086     }
00087     GSN_CACHE_CLEAR(&thread->gsnCache);
00088     free(thread);
00089 }
00090 
00091 /*
00092  * Get current thread-local JSThread info, creating one if it doesn't exist.
00093  * Each thread has a unique JSThread pointer.
00094  *
00095  * Since we are dealing with thread-local data, no lock is needed.
00096  *
00097  * Return a pointer to the thread local info, NULL if the system runs out
00098  * of memory, or it failed to set thread private data (neither case is very
00099  * likely; both are probably due to out-of-memory).  It is up to the caller
00100  * to report an error, if possible.
00101  */
00102 JSThread *
00103 js_GetCurrentThread(JSRuntime *rt)
00104 {
00105     JSThread *thread;
00106 
00107     thread = (JSThread *)PR_GetThreadPrivate(rt->threadTPIndex);
00108     if (!thread) {
00109         thread = (JSThread *) calloc(1, sizeof(JSThread));
00110         if (!thread)
00111             return NULL;
00112 
00113         if (PR_FAILURE == PR_SetThreadPrivate(rt->threadTPIndex, thread)) {
00114             free(thread);
00115             return NULL;
00116         }
00117 
00118         JS_INIT_CLIST(&thread->contextList);
00119         thread->id = js_CurrentThreadId();
00120 
00121         /* js_SetContextThread initialize gcFreeLists as necessary. */
00122 #ifdef DEBUG
00123         memset(thread->gcFreeLists, JS_FREE_PATTERN,
00124                sizeof(thread->gcFreeLists));
00125 #endif
00126     }
00127     return thread;
00128 }
00129 
00130 /*
00131  * Sets current thread as owning thread of a context by assigning the
00132  * thread-private info to the context. If the current thread doesn't have
00133  * private JSThread info, create one.
00134  */
00135 JSBool
00136 js_SetContextThread(JSContext *cx)
00137 {
00138     JSThread *thread = js_GetCurrentThread(cx->runtime);
00139 
00140     if (!thread) {
00141         JS_ReportOutOfMemory(cx);
00142         return JS_FALSE;
00143     }
00144 
00145     /*
00146      * Clear gcFreeLists on each transition from 0 to 1 context active on the
00147      * current thread. See bug 351602.
00148      */
00149     if (JS_CLIST_IS_EMPTY(&thread->contextList))
00150         memset(thread->gcFreeLists, 0, sizeof(thread->gcFreeLists));
00151 
00152     cx->thread = thread;
00153     JS_REMOVE_LINK(&cx->threadLinks);
00154     JS_APPEND_LINK(&cx->threadLinks, &thread->contextList);
00155     return JS_TRUE;
00156 }
00157 
00158 /* Remove the owning thread info of a context. */
00159 void
00160 js_ClearContextThread(JSContext *cx)
00161 {
00162     JS_REMOVE_AND_INIT_LINK(&cx->threadLinks);
00163 #ifdef DEBUG
00164     if (JS_CLIST_IS_EMPTY(&cx->thread->contextList)) {
00165         memset(cx->thread->gcFreeLists, JS_FREE_PATTERN,
00166                sizeof(cx->thread->gcFreeLists));
00167     }
00168 #endif
00169     cx->thread = NULL;
00170 }
00171 
00172 #endif /* JS_THREADSAFE */
00173 
00174 void
00175 js_OnVersionChange(JSContext *cx)
00176 {
00177 #ifdef DEBUG
00178     JSVersion version = JSVERSION_NUMBER(cx);
00179 
00180     JS_ASSERT(version == JSVERSION_DEFAULT || version >= JSVERSION_ECMA_3);
00181 #endif
00182 }
00183 
00184 void
00185 js_SetVersion(JSContext *cx, JSVersion version)
00186 {
00187     cx->version = version;
00188     js_OnVersionChange(cx);
00189 }
00190 
00191 JSContext *
00192 js_NewContext(JSRuntime *rt, size_t stackChunkSize)
00193 {
00194     JSContext *cx;
00195     JSBool ok, first;
00196     JSContextCallback cxCallback;
00197 
00198     cx = (JSContext *) malloc(sizeof *cx);
00199     if (!cx)
00200         return NULL;
00201     memset(cx, 0, sizeof *cx);
00202 
00203     cx->runtime = rt;
00204 #if JS_STACK_GROWTH_DIRECTION > 0
00205     cx->stackLimit = (jsuword)-1;
00206 #endif
00207 #ifdef JS_THREADSAFE
00208     JS_INIT_CLIST(&cx->threadLinks);
00209     js_SetContextThread(cx);
00210 #endif
00211 
00212     JS_LOCK_GC(rt);
00213     for (;;) {
00214         first = (rt->contextList.next == &rt->contextList);
00215         if (rt->state == JSRTS_UP) {
00216             JS_ASSERT(!first);
00217             break;
00218         }
00219         if (rt->state == JSRTS_DOWN) {
00220             JS_ASSERT(first);
00221             rt->state = JSRTS_LAUNCHING;
00222             break;
00223         }
00224         JS_WAIT_CONDVAR(rt->stateChange, JS_NO_TIMEOUT);
00225     }
00226     JS_APPEND_LINK(&cx->links, &rt->contextList);
00227     JS_UNLOCK_GC(rt);
00228 
00229     /*
00230      * First we do the infallible, every-time per-context initializations.
00231      * Should a later, fallible initialization (js_InitRegExpStatics, e.g.,
00232      * or the stuff under 'if (first)' below) fail, at least the version
00233      * and arena-pools will be valid and safe to use (say, from the last GC
00234      * done by js_DestroyContext).
00235      */
00236     cx->version = JSVERSION_DEFAULT;
00237     cx->jsop_eq = JSOP_EQ;
00238     cx->jsop_ne = JSOP_NE;
00239     JS_InitArenaPool(&cx->stackPool, "stack", stackChunkSize, sizeof(jsval));
00240     JS_InitArenaPool(&cx->tempPool, "temp", 1024, sizeof(jsdouble));
00241 
00242     if (!js_InitRegExpStatics(cx, &cx->regExpStatics)) {
00243         js_DestroyContext(cx, JSDCM_NEW_FAILED);
00244         return NULL;
00245     }
00246 
00247     /*
00248      * If cx is the first context on this runtime, initialize well-known atoms,
00249      * keywords, numbers, and strings.  If one of these steps should fail, the
00250      * runtime will be left in a partially initialized state, with zeroes and
00251      * nulls stored in the default-initialized remainder of the struct.  We'll
00252      * clean the runtime up under js_DestroyContext, because cx will be "last"
00253      * as well as "first".
00254      */
00255     if (first) {
00256 #ifdef JS_THREADSAFE
00257         JS_BeginRequest(cx);
00258 #endif
00259         /*
00260          * Both atomState and the scriptFilenameTable may be left over from a
00261          * previous episode of non-zero contexts alive in rt, so don't re-init
00262          * either table if it's not necessary.  Just repopulate atomState with
00263          * well-known internal atoms, and with the reserved identifiers added
00264          * by the scanner.
00265          */
00266         ok = (rt->atomState.liveAtoms == 0)
00267              ? js_InitAtomState(cx, &rt->atomState)
00268              : js_InitPinnedAtoms(cx, &rt->atomState);
00269         if (ok && !rt->scriptFilenameTable)
00270             ok = js_InitRuntimeScriptState(rt);
00271         if (ok)
00272             ok = js_InitRuntimeNumberState(cx);
00273         if (ok)
00274             ok = js_InitRuntimeStringState(cx);
00275 #ifdef JS_THREADSAFE
00276         JS_EndRequest(cx);
00277 #endif
00278         if (!ok) {
00279             js_DestroyContext(cx, JSDCM_NEW_FAILED);
00280             return NULL;
00281         }
00282 
00283         JS_LOCK_GC(rt);
00284         rt->state = JSRTS_UP;
00285         JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
00286         JS_UNLOCK_GC(rt);
00287     }
00288 
00289     cxCallback = rt->cxCallback;
00290     if (cxCallback && !cxCallback(cx, JSCONTEXT_NEW)) {
00291         js_DestroyContext(cx, JSDCM_NEW_FAILED);
00292         return NULL;
00293     }
00294     return cx;
00295 }
00296 
00297 void
00298 js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
00299 {
00300     JSRuntime *rt;
00301     JSContextCallback cxCallback;
00302     JSBool last;
00303     JSArgumentFormatMap *map;
00304     JSLocalRootStack *lrs;
00305     JSLocalRootChunk *lrc;
00306 
00307     rt = cx->runtime;
00308 
00309     if (mode != JSDCM_NEW_FAILED) {
00310         cxCallback = rt->cxCallback;
00311         if (cxCallback) {
00312             /*
00313              * JSCONTEXT_DESTROY callback is not allowed to fail and must
00314              * return true.
00315              */
00316 #ifdef DEBUG
00317             JSBool callbackStatus =
00318 #endif
00319             cxCallback(cx, JSCONTEXT_DESTROY);
00320             JS_ASSERT(callbackStatus);
00321         }
00322     }
00323 
00324     /* Remove cx from context list first. */
00325     JS_LOCK_GC(rt);
00326     JS_ASSERT(rt->state == JSRTS_UP || rt->state == JSRTS_LAUNCHING);
00327     JS_REMOVE_LINK(&cx->links);
00328     last = (rt->contextList.next == &rt->contextList);
00329     if (last)
00330         rt->state = JSRTS_LANDING;
00331     JS_UNLOCK_GC(rt);
00332 
00333     if (last) {
00334 #ifdef JS_THREADSAFE
00335         /*
00336          * If cx is not in a request already, begin one now so that we wait
00337          * for any racing GC started on a not-last context to finish, before
00338          * we plow ahead and unpin atoms.  Note that even though we begin a
00339          * request here if necessary, we end all requests on cx below before
00340          * forcing a final GC.  This lets any not-last context destruction
00341          * racing in another thread try to force or maybe run the GC, but by
00342          * that point, rt->state will not be JSRTS_UP, and that GC attempt
00343          * will return early.
00344          */
00345         if (cx->requestDepth == 0)
00346             JS_BeginRequest(cx);
00347 #endif
00348 
00349         /* Unpin all pinned atoms before final GC. */
00350         js_UnpinPinnedAtoms(&rt->atomState);
00351 
00352         /* Unlock and clear GC things held by runtime pointers. */
00353         js_FinishRuntimeNumberState(cx);
00354         js_FinishRuntimeStringState(cx);
00355 
00356         /* Clear debugging state to remove GC roots. */
00357         JS_ClearAllTraps(cx);
00358         JS_ClearAllWatchPoints(cx);
00359     }
00360 
00361     /*
00362      * Remove more GC roots in regExpStatics, then collect garbage.
00363      * XXX anti-modularity alert: we rely on the call to js_RemoveRoot within
00364      * XXX this function call to wait for any racing GC to complete, in the
00365      * XXX case where JS_DestroyContext is called outside of a request on cx
00366      */
00367     js_FreeRegExpStatics(cx, &cx->regExpStatics);
00368 
00369 #ifdef JS_THREADSAFE
00370     /*
00371      * Destroying a context implicitly calls JS_EndRequest().  Also, we must
00372      * end our request here in case we are "last" -- in that event, another
00373      * js_DestroyContext that was not last might be waiting in the GC for our
00374      * request to end.  We'll let it run below, just before we do the truly
00375      * final GC and then free atom state.
00376      *
00377      * At this point, cx must be inaccessible to other threads.  It's off the
00378      * rt->contextList, and it should not be reachable via any object private
00379      * data structure.
00380      */
00381     while (cx->requestDepth != 0)
00382         JS_EndRequest(cx);
00383 #endif
00384 
00385     if (last) {
00386         js_GC(cx, GC_LAST_CONTEXT);
00387 
00388         /* Try to free atom state, now that no unrooted scripts survive. */
00389         if (rt->atomState.liveAtoms == 0)
00390             js_FreeAtomState(cx, &rt->atomState);
00391 
00392         /* Also free the script filename table if it exists and is empty. */
00393         if (rt->scriptFilenameTable && rt->scriptFilenameTable->nentries == 0)
00394             js_FinishRuntimeScriptState(rt);
00395 
00396         /*
00397          * Free the deflated string cache, but only after the last GC has
00398          * collected all unleaked strings.
00399          */
00400         js_FinishDeflatedStringCache(rt);
00401 
00402         /* Take the runtime down, now that it has no contexts or atoms. */
00403         JS_LOCK_GC(rt);
00404         rt->state = JSRTS_DOWN;
00405         JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
00406         JS_UNLOCK_GC(rt);
00407     } else {
00408         if (mode == JSDCM_FORCE_GC)
00409             js_GC(cx, GC_NORMAL);
00410         else if (mode == JSDCM_MAYBE_GC)
00411             JS_MaybeGC(cx);
00412     }
00413 
00414     /* Free the stuff hanging off of cx. */
00415     JS_FinishArenaPool(&cx->stackPool);
00416     JS_FinishArenaPool(&cx->tempPool);
00417 
00418     if (cx->lastMessage)
00419         free(cx->lastMessage);
00420 
00421     /* Remove any argument formatters. */
00422     map = cx->argumentFormatMap;
00423     while (map) {
00424         JSArgumentFormatMap *temp = map;
00425         map = map->next;
00426         JS_free(cx, temp);
00427     }
00428 
00429     /* Destroy the resolve recursion damper. */
00430     if (cx->resolvingTable) {
00431         JS_DHashTableDestroy(cx->resolvingTable);
00432         cx->resolvingTable = NULL;
00433     }
00434 
00435     lrs = cx->localRootStack;
00436     if (lrs) {
00437         while ((lrc = lrs->topChunk) != &lrs->firstChunk) {
00438             lrs->topChunk = lrc->down;
00439             JS_free(cx, lrc);
00440         }
00441         JS_free(cx, lrs);
00442     }
00443 
00444 #ifdef JS_THREADSAFE
00445     js_ClearContextThread(cx);
00446 #endif
00447 
00448     /* Finally, free cx itself. */
00449     free(cx);
00450 }
00451 
00452 JSBool
00453 js_ValidContextPointer(JSRuntime *rt, JSContext *cx)
00454 {
00455     JSCList *cl;
00456 
00457     for (cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next) {
00458         if (cl == &cx->links)
00459             return JS_TRUE;
00460     }
00461     JS_RUNTIME_METER(rt, deadContexts);
00462     return JS_FALSE;
00463 }
00464 
00465 JSContext *
00466 js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp)
00467 {
00468     JSContext *cx = *iterp;
00469 
00470     if (unlocked)
00471         JS_LOCK_GC(rt);
00472     if (!cx)
00473         cx = (JSContext *)&rt->contextList;
00474     cx = (JSContext *)cx->links.next;
00475     if (&cx->links == &rt->contextList)
00476         cx = NULL;
00477     *iterp = cx;
00478     if (unlocked)
00479         JS_UNLOCK_GC(rt);
00480     return cx;
00481 }
00482 
00483 JS_STATIC_DLL_CALLBACK(const void *)
00484 resolving_GetKey(JSDHashTable *table, JSDHashEntryHdr *hdr)
00485 {
00486     JSResolvingEntry *entry = (JSResolvingEntry *)hdr;
00487 
00488     return &entry->key;
00489 }
00490 
00491 JS_STATIC_DLL_CALLBACK(JSDHashNumber)
00492 resolving_HashKey(JSDHashTable *table, const void *ptr)
00493 {
00494     const JSResolvingKey *key = (const JSResolvingKey *)ptr;
00495 
00496     return ((JSDHashNumber)JS_PTR_TO_UINT32(key->obj) >> JSVAL_TAGBITS) ^ key->id;
00497 }
00498 
00499 JS_PUBLIC_API(JSBool)
00500 resolving_MatchEntry(JSDHashTable *table,
00501                      const JSDHashEntryHdr *hdr,
00502                      const void *ptr)
00503 {
00504     const JSResolvingEntry *entry = (const JSResolvingEntry *)hdr;
00505     const JSResolvingKey *key = (const JSResolvingKey *)ptr;
00506 
00507     return entry->key.obj == key->obj && entry->key.id == key->id;
00508 }
00509 
00510 static const JSDHashTableOps resolving_dhash_ops = {
00511     JS_DHashAllocTable,
00512     JS_DHashFreeTable,
00513     resolving_GetKey,
00514     resolving_HashKey,
00515     resolving_MatchEntry,
00516     JS_DHashMoveEntryStub,
00517     JS_DHashClearEntryStub,
00518     JS_DHashFinalizeStub,
00519     NULL
00520 };
00521 
00522 JSBool
00523 js_StartResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
00524                   JSResolvingEntry **entryp)
00525 {
00526     JSDHashTable *table;
00527     JSResolvingEntry *entry;
00528 
00529     table = cx->resolvingTable;
00530     if (!table) {
00531         table = JS_NewDHashTable(&resolving_dhash_ops, NULL,
00532                                  sizeof(JSResolvingEntry),
00533                                  JS_DHASH_MIN_SIZE);
00534         if (!table)
00535             goto outofmem;
00536         cx->resolvingTable = table;
00537     }
00538 
00539     entry = (JSResolvingEntry *)
00540             JS_DHashTableOperate(table, key, JS_DHASH_ADD);
00541     if (!entry)
00542         goto outofmem;
00543 
00544     if (entry->flags & flag) {
00545         /* An entry for (key, flag) exists already -- dampen recursion. */
00546         entry = NULL;
00547     } else {
00548         /* Fill in key if we were the first to add entry, then set flag. */
00549         if (!entry->key.obj)
00550             entry->key = *key;
00551         entry->flags |= flag;
00552     }
00553     *entryp = entry;
00554     return JS_TRUE;
00555 
00556 outofmem:
00557     JS_ReportOutOfMemory(cx);
00558     return JS_FALSE;
00559 }
00560 
00561 void
00562 js_StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
00563                  JSResolvingEntry *entry, uint32 generation)
00564 {
00565     JSDHashTable *table;
00566 
00567     /*
00568      * Clear flag from entry->flags and return early if other flags remain.
00569      * We must take care to re-lookup entry if the table has changed since
00570      * it was found by js_StartResolving.
00571      */
00572     table = cx->resolvingTable;
00573     if (!entry || table->generation != generation) {
00574         entry = (JSResolvingEntry *)
00575                 JS_DHashTableOperate(table, key, JS_DHASH_LOOKUP);
00576     }
00577     JS_ASSERT(JS_DHASH_ENTRY_IS_BUSY(&entry->hdr));
00578     entry->flags &= ~flag;
00579     if (entry->flags)
00580         return;
00581 
00582     /*
00583      * Do a raw remove only if fewer entries were removed than would cause
00584      * alpha to be less than .5 (alpha is at most .75).  Otherwise, we just
00585      * call JS_DHashTableOperate to re-lookup the key and remove its entry,
00586      * compressing or shrinking the table as needed.
00587      */
00588     if (table->removedCount < JS_DHASH_TABLE_SIZE(table) >> 2)
00589         JS_DHashTableRawRemove(table, &entry->hdr);
00590     else
00591         JS_DHashTableOperate(table, key, JS_DHASH_REMOVE);
00592 }
00593 
00594 JSBool
00595 js_EnterLocalRootScope(JSContext *cx)
00596 {
00597     JSLocalRootStack *lrs;
00598     int mark;
00599 
00600     lrs = cx->localRootStack;
00601     if (!lrs) {
00602         lrs = (JSLocalRootStack *) JS_malloc(cx, sizeof *lrs);
00603         if (!lrs)
00604             return JS_FALSE;
00605         lrs->scopeMark = JSLRS_NULL_MARK;
00606         lrs->rootCount = 0;
00607         lrs->topChunk = &lrs->firstChunk;
00608         lrs->firstChunk.down = NULL;
00609         cx->localRootStack = lrs;
00610     }
00611 
00612     /* Push lrs->scopeMark to save it for restore when leaving. */
00613     mark = js_PushLocalRoot(cx, lrs, INT_TO_JSVAL(lrs->scopeMark));
00614     if (mark < 0)
00615         return JS_FALSE;
00616     lrs->scopeMark = (uint32) mark;
00617     return JS_TRUE;
00618 }
00619 
00620 void
00621 js_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval)
00622 {
00623     JSLocalRootStack *lrs;
00624     uint32 mark, m, n;
00625     JSLocalRootChunk *lrc;
00626 
00627     /* Defend against buggy native callers. */
00628     lrs = cx->localRootStack;
00629     JS_ASSERT(lrs && lrs->rootCount != 0);
00630     if (!lrs || lrs->rootCount == 0)
00631         return;
00632 
00633     mark = lrs->scopeMark;
00634     JS_ASSERT(mark != JSLRS_NULL_MARK);
00635     if (mark == JSLRS_NULL_MARK)
00636         return;
00637 
00638     /* Free any chunks being popped by this leave operation. */
00639     m = mark >> JSLRS_CHUNK_SHIFT;
00640     n = (lrs->rootCount - 1) >> JSLRS_CHUNK_SHIFT;
00641     while (n > m) {
00642         lrc = lrs->topChunk;
00643         JS_ASSERT(lrc != &lrs->firstChunk);
00644         lrs->topChunk = lrc->down;
00645         JS_free(cx, lrc);
00646         --n;
00647     }
00648 
00649     /*
00650      * Pop the scope, restoring lrs->scopeMark.  If rval is a GC-thing, push
00651      * it on the caller's scope, or store it in lastInternalResult if we are
00652      * leaving the outermost scope.  We don't need to allocate a new lrc
00653      * because we can overwrite the old mark's slot with rval.
00654      */
00655     lrc = lrs->topChunk;
00656     m = mark & JSLRS_CHUNK_MASK;
00657     lrs->scopeMark = (uint32) JSVAL_TO_INT(lrc->roots[m]);
00658     if (JSVAL_IS_GCTHING(rval) && !JSVAL_IS_NULL(rval)) {
00659         if (mark == 0) {
00660             cx->weakRoots.lastInternalResult = rval;
00661         } else {
00662             /*
00663              * Increment m to avoid the "else if (m == 0)" case below.  If
00664              * rval is not a GC-thing, that case would take care of freeing
00665              * any chunk that contained only the old mark.  Since rval *is*
00666              * a GC-thing here, we want to reuse that old mark's slot.
00667              */
00668             lrc->roots[m++] = rval;
00669             ++mark;
00670         }
00671     }
00672     lrs->rootCount = (uint32) mark;
00673 
00674     /*
00675      * Free the stack eagerly, risking malloc churn.  The alternative would
00676      * require an lrs->entryCount member, maintained by Enter and Leave, and
00677      * tested by the GC in addition to the cx->localRootStack non-null test.
00678      *
00679      * That approach would risk hoarding 264 bytes (net) per context.  Right
00680      * now it seems better to give fresh (dirty in CPU write-back cache, and
00681      * the data is no longer needed) memory back to the malloc heap.
00682      */
00683     if (mark == 0) {
00684         cx->localRootStack = NULL;
00685         JS_free(cx, lrs);
00686     } else if (m == 0) {
00687         lrs->topChunk = lrc->down;
00688         JS_free(cx, lrc);
00689     }
00690 }
00691 
00692 void
00693 js_ForgetLocalRoot(JSContext *cx, jsval v)
00694 {
00695     JSLocalRootStack *lrs;
00696     uint32 i, j, m, n, mark;
00697     JSLocalRootChunk *lrc, *lrc2;
00698     jsval top;
00699 
00700     lrs = cx->localRootStack;
00701     JS_ASSERT(lrs && lrs->rootCount);
00702     if (!lrs || lrs->rootCount == 0)
00703         return;
00704 
00705     /* Prepare to pop the top-most value from the stack. */
00706     n = lrs->rootCount - 1;
00707     m = n & JSLRS_CHUNK_MASK;
00708     lrc = lrs->topChunk;
00709     top = lrc->roots[m];
00710 
00711     /* Be paranoid about calls on an empty scope. */
00712     mark = lrs->scopeMark;
00713     JS_ASSERT(mark < n);
00714     if (mark >= n)
00715         return;
00716 
00717     /* If v was not the last root pushed in the top scope, find it. */
00718     if (top != v) {
00719         /* Search downward in case v was recently pushed. */
00720         i = n;
00721         j = m;
00722         lrc2 = lrc;
00723         while (--i > mark) {
00724             if (j == 0)
00725                 lrc2 = lrc2->down;
00726             j = i & JSLRS_CHUNK_MASK;
00727             if (lrc2->roots[j] == v)
00728                 break;
00729         }
00730 
00731         /* If we didn't find v in this scope, assert and bail out. */
00732         JS_ASSERT(i != mark);
00733         if (i == mark)
00734             return;
00735 
00736         /* Swap top and v so common tail code can pop v. */
00737         lrc2->roots[j] = top;
00738     }
00739 
00740     /* Pop the last value from the stack. */
00741     lrc->roots[m] = JSVAL_NULL;
00742     lrs->rootCount = n;
00743     if (m == 0) {
00744         JS_ASSERT(n != 0);
00745         JS_ASSERT(lrc != &lrs->firstChunk);
00746         lrs->topChunk = lrc->down;
00747         JS_free(cx, lrc);
00748     }
00749 }
00750 
00751 int
00752 js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, jsval v)
00753 {
00754     uint32 n, m;
00755     JSLocalRootChunk *lrc;
00756 
00757     n = lrs->rootCount;
00758     m = n & JSLRS_CHUNK_MASK;
00759     if (n == 0 || m != 0) {
00760         /*
00761          * At start of first chunk, or not at start of a non-first top chunk.
00762          * Check for lrs->rootCount overflow.
00763          */
00764         if ((uint32)(n + 1) == 0) {
00765             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
00766                                  JSMSG_TOO_MANY_LOCAL_ROOTS);
00767             return -1;
00768         }
00769         lrc = lrs->topChunk;
00770         JS_ASSERT(n != 0 || lrc == &lrs->firstChunk);
00771     } else {
00772         /*
00773          * After lrs->firstChunk, trying to index at a power-of-two chunk
00774          * boundary: need a new chunk.
00775          */
00776         lrc = (JSLocalRootChunk *) JS_malloc(cx, sizeof *lrc);
00777         if (!lrc)
00778             return -1;
00779         lrc->down = lrs->topChunk;
00780         lrs->topChunk = lrc;
00781     }
00782     lrs->rootCount = n + 1;
00783     lrc->roots[m] = v;
00784     return (int) n;
00785 }
00786 
00787 void
00788 js_MarkLocalRoots(JSContext *cx, JSLocalRootStack *lrs)
00789 {
00790     uint32 n, m, mark;
00791     JSLocalRootChunk *lrc;
00792 
00793     n = lrs->rootCount;
00794     if (n == 0)
00795         return;
00796 
00797     mark = lrs->scopeMark;
00798     lrc = lrs->topChunk;
00799     do {
00800         while (--n > mark) {
00801 #ifdef GC_MARK_DEBUG
00802             char name[22];
00803             JS_snprintf(name, sizeof name, "<local root %u>", n);
00804 #endif
00805             m = n & JSLRS_CHUNK_MASK;
00806             JS_ASSERT(JSVAL_IS_GCTHING(lrc->roots[m]));
00807             GC_MARK(cx, JSVAL_TO_GCTHING(lrc->roots[m]), name);
00808             if (m == 0)
00809                 lrc = lrc->down;
00810         }
00811         m = n & JSLRS_CHUNK_MASK;
00812         mark = JSVAL_TO_INT(lrc->roots[m]);
00813         if (m == 0)
00814             lrc = lrc->down;
00815     } while (n != 0);
00816     JS_ASSERT(!lrc);
00817 }
00818 
00819 static void
00820 ReportError(JSContext *cx, const char *message, JSErrorReport *reportp)
00821 {
00822     /*
00823      * Check the error report, and set a JavaScript-catchable exception
00824      * if the error is defined to have an associated exception.  If an
00825      * exception is thrown, then the JSREPORT_EXCEPTION flag will be set
00826      * on the error report, and exception-aware hosts should ignore it.
00827      */
00828     JS_ASSERT(reportp);
00829     if (reportp->errorNumber == JSMSG_UNCAUGHT_EXCEPTION)
00830         reportp->flags |= JSREPORT_EXCEPTION;
00831 
00832     /*
00833      * Call the error reporter only if an exception wasn't raised.
00834      *
00835      * If an exception was raised, then we call the debugErrorHook
00836      * (if present) to give it a chance to see the error before it
00837      * propagates out of scope.  This is needed for compatability
00838      * with the old scheme.
00839      */
00840     if (!js_ErrorToException(cx, message, reportp)) {
00841         js_ReportErrorAgain(cx, message, reportp);
00842     } else if (cx->runtime->debugErrorHook && cx->errorReporter) {
00843         JSDebugErrorHook hook = cx->runtime->debugErrorHook;
00844         /* test local in case debugErrorHook changed on another thread */
00845         if (hook)
00846             hook(cx, message, reportp, cx->runtime->debugErrorHookData);
00847     }
00848 }
00849 
00850 /*
00851  * We don't post an exception in this case, since doing so runs into
00852  * complications of pre-allocating an exception object which required
00853  * running the Exception class initializer early etc.
00854  * Instead we just invoke the errorReporter with an "Out Of Memory"
00855  * type message, and then hope the process ends swiftly.
00856  */
00857 void
00858 js_ReportOutOfMemory(JSContext *cx)
00859 {
00860     JSStackFrame *fp;
00861     JSErrorReport report;
00862     JSErrorReporter onError = cx->errorReporter;
00863 
00864     /* Get the message for this error, but we won't expand any arguments. */
00865     const JSErrorFormatString *efs =
00866         js_GetLocalizedErrorMessage(cx, NULL, NULL, JSMSG_OUT_OF_MEMORY);
00867     const char *msg = efs ? efs->format : "Out of memory";
00868 
00869     /* Fill out the report, but don't do anything that requires allocation. */
00870     memset(&report, 0, sizeof (struct JSErrorReport));
00871     report.flags = JSREPORT_ERROR;
00872     report.errorNumber = JSMSG_OUT_OF_MEMORY;
00873 
00874     /*
00875      * Walk stack until we find a frame that is associated with some script
00876      * rather than a native frame.
00877      */
00878     for (fp = cx->fp; fp; fp = fp->down) {
00879         if (fp->script && fp->pc) {
00880             report.filename = fp->script->filename;
00881             report.lineno = js_PCToLineNumber(cx, fp->script, fp->pc);
00882             break;
00883         }
00884     }
00885 
00886     /*
00887      * If debugErrorHook is present then we give it a chance to veto
00888      * sending the error on to the regular ErrorReporter.
00889      */
00890     if (onError) {
00891         JSDebugErrorHook hook = cx->runtime->debugErrorHook;
00892         if (hook &&
00893             !hook(cx, msg, &report, cx->runtime->debugErrorHookData)) {
00894             onError = NULL;
00895         }
00896     }
00897 
00898     if (onError)
00899         onError(cx, msg, &report);
00900 }
00901 
00902 JSBool
00903 js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap)
00904 {
00905     char *message;
00906     jschar *ucmessage;
00907     size_t messagelen;
00908     JSStackFrame *fp;
00909     JSErrorReport report;
00910     JSBool warning;
00911 
00912     if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx))
00913         return JS_TRUE;
00914 
00915     message = JS_vsmprintf(format, ap);
00916     if (!message)
00917         return JS_FALSE;
00918     messagelen = strlen(message);
00919 
00920     memset(&report, 0, sizeof (struct JSErrorReport));
00921     report.flags = flags;
00922     report.errorNumber = JSMSG_USER_DEFINED_ERROR;
00923     report.ucmessage = ucmessage = js_InflateString(cx, message, &messagelen);
00924 
00925     /* Find the top-most active script frame, for best line number blame. */
00926     for (fp = cx->fp; fp; fp = fp->down) {
00927         if (fp->script && fp->pc) {
00928             report.filename = fp->script->filename;
00929             report.lineno = js_PCToLineNumber(cx, fp->script, fp->pc);
00930             break;
00931         }
00932     }
00933 
00934     warning = JSREPORT_IS_WARNING(report.flags);
00935     if (warning && JS_HAS_WERROR_OPTION(cx)) {
00936         report.flags &= ~JSREPORT_WARNING;
00937         warning = JS_FALSE;
00938     }
00939 
00940     ReportError(cx, message, &report);
00941     free(message);
00942     JS_free(cx, ucmessage);
00943     return warning;
00944 }
00945 
00946 /*
00947  * The arguments from ap need to be packaged up into an array and stored
00948  * into the report struct.
00949  *
00950  * The format string addressed by the error number may contain operands
00951  * identified by the format {N}, where N is a decimal digit. Each of these
00952  * is to be replaced by the Nth argument from the va_list. The complete
00953  * message is placed into reportp->ucmessage converted to a JSString.
00954  *
00955  * Returns true if the expansion succeeds (can fail if out of memory).
00956  */
00957 JSBool
00958 js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback,
00959                         void *userRef, const uintN errorNumber,
00960                         char **messagep, JSErrorReport *reportp,
00961                         JSBool *warningp, JSBool charArgs, va_list ap)
00962 {
00963     const JSErrorFormatString *efs;
00964     int i;
00965     int argCount;
00966 
00967     *warningp = JSREPORT_IS_WARNING(reportp->flags);
00968     if (*warningp && JS_HAS_WERROR_OPTION(cx)) {
00969         reportp->flags &= ~JSREPORT_WARNING;
00970         *warningp = JS_FALSE;
00971     }
00972 
00973     *messagep = NULL;
00974 
00975     /* Most calls supply js_GetErrorMessage; if this is so, assume NULL. */
00976     if (!callback || callback == js_GetErrorMessage)
00977         efs = js_GetLocalizedErrorMessage(cx, userRef, NULL, errorNumber);
00978     else
00979         efs = callback(userRef, NULL, errorNumber);
00980     if (efs) {
00981         size_t totalArgsLength = 0;
00982         size_t argLengths[10]; /* only {0} thru {9} supported */
00983         argCount = efs->argCount;
00984         JS_ASSERT(argCount <= 10);
00985         if (argCount > 0) {
00986             /*
00987              * Gather the arguments into an array, and accumulate
00988              * their sizes. We allocate 1 more than necessary and
00989              * null it out to act as the caboose when we free the
00990              * pointers later.
00991              */
00992             reportp->messageArgs = (const jschar **)
00993                 JS_malloc(cx, sizeof(jschar *) * (argCount + 1));
00994             if (!reportp->messageArgs)
00995                 return JS_FALSE;
00996             reportp->messageArgs[argCount] = NULL;
00997             for (i = 0; i < argCount; i++) {
00998                 if (charArgs) {
00999                     char *charArg = va_arg(ap, char *);
01000                     size_t charArgLength = strlen(charArg);
01001                     reportp->messageArgs[i]
01002                         = js_InflateString(cx, charArg, &charArgLength);
01003                     if (!reportp->messageArgs[i])
01004                         goto error;
01005                 } else {
01006                     reportp->messageArgs[i] = va_arg(ap, jschar *);
01007                 }
01008                 argLengths[i] = js_strlen(reportp->messageArgs[i]);
01009                 totalArgsLength += argLengths[i];
01010             }
01011             /* NULL-terminate for easy copying. */
01012             reportp->messageArgs[i] = NULL;
01013         }
01014         /*
01015          * Parse the error format, substituting the argument X
01016          * for {X} in the format.
01017          */
01018         if (argCount > 0) {
01019             if (efs->format) {
01020                 jschar *buffer, *fmt, *out;
01021                 int expandedArgs = 0;
01022                 size_t expandedLength;
01023                 size_t len = strlen(efs->format);
01024 
01025                 buffer = fmt = js_InflateString (cx, efs->format, &len);
01026                 if (!buffer)
01027                     goto error;
01028                 expandedLength = len
01029                                  - (3 * argCount)       /* exclude the {n} */
01030                                  + totalArgsLength;
01031 
01032                 /*
01033                 * Note - the above calculation assumes that each argument
01034                 * is used once and only once in the expansion !!!
01035                 */
01036                 reportp->ucmessage = out = (jschar *)
01037                     JS_malloc(cx, (expandedLength + 1) * sizeof(jschar));
01038                 if (!out) {
01039                     JS_free (cx, buffer);
01040                     goto error;
01041                 }
01042                 while (*fmt) {
01043                     if (*fmt == '{') {
01044                         if (isdigit(fmt[1])) {
01045                             int d = JS7_UNDEC(fmt[1]);
01046                             JS_ASSERT(d < argCount);
01047                             js_strncpy(out, reportp->messageArgs[d],
01048                                        argLengths[d]);
01049                             out += argLengths[d];
01050                             fmt += 3;
01051                             expandedArgs++;
01052                             continue;
01053                         }
01054                     }
01055                     *out++ = *fmt++;
01056                 }
01057                 JS_ASSERT(expandedArgs == argCount);
01058                 *out = 0;
01059                 JS_free (cx, buffer);
01060                 *messagep =
01061                     js_DeflateString(cx, reportp->ucmessage,
01062                                      (size_t)(out - reportp->ucmessage));
01063                 if (!*messagep)
01064                     goto error;
01065             }
01066         } else {
01067             /*
01068              * Zero arguments: the format string (if it exists) is the
01069              * entire message.
01070              */
01071             if (efs->format) {
01072                 size_t len;
01073                 *messagep = JS_strdup(cx, efs->format);
01074                 if (!*messagep)
01075                     goto error;
01076                 len = strlen(*messagep);
01077                 reportp->ucmessage = js_InflateString(cx, *messagep, &len);
01078                 if (!reportp->ucmessage)
01079                     goto error;
01080             }
01081         }
01082     }
01083     if (*messagep == NULL) {
01084         /* where's the right place for this ??? */
01085         const char *defaultErrorMessage
01086             = "No error message available for error number %d";
01087         size_t nbytes = strlen(defaultErrorMessage) + 16;
01088         *messagep = (char *)JS_malloc(cx, nbytes);
01089         if (!*messagep)
01090             goto error;
01091         JS_snprintf(*messagep, nbytes, defaultErrorMessage, errorNumber);
01092     }
01093     return JS_TRUE;
01094 
01095 error:
01096     if (reportp->messageArgs) {
01097         /* free the arguments only if we allocated them */
01098         if (charArgs) {
01099             i = 0;
01100             while (reportp->messageArgs[i])
01101                 JS_free(cx, (void *)reportp->messageArgs[i++]);
01102         }
01103         JS_free(cx, (void *)reportp->messageArgs);
01104         reportp->messageArgs = NULL;
01105     }
01106     if (reportp->ucmessage) {
01107         JS_free(cx, (void *)reportp->ucmessage);
01108         reportp->ucmessage = NULL;
01109     }
01110     if (*messagep) {
01111         JS_free(cx, (void *)*messagep);
01112         *messagep = NULL;
01113     }
01114     return JS_FALSE;
01115 }
01116 
01117 JSBool
01118 js_ReportErrorNumberVA(JSContext *cx, uintN flags, JSErrorCallback callback,
01119                        void *userRef, const uintN errorNumber,
01120                        JSBool charArgs, va_list ap)
01121 {
01122     JSStackFrame *fp;
01123     JSErrorReport report;
01124     char *message;
01125     JSBool warning;
01126 
01127     if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx))
01128         return JS_TRUE;
01129 
01130     memset(&report, 0, sizeof (struct JSErrorReport));
01131     report.flags = flags;
01132     report.errorNumber = errorNumber;
01133 
01134     /*
01135      * If we can't find out where the error was based on the current frame,
01136      * see if the next frame has a script/pc combo we can use.
01137      */
01138     for (fp = cx->fp; fp; fp = fp->down) {
01139         if (fp->script && fp->pc) {
01140             report.filename = fp->script->filename;
01141             report.lineno = js_PCToLineNumber(cx, fp->script, fp->pc);
01142             break;
01143         }
01144     }
01145 
01146     if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber,
01147                                  &message, &report, &warning, charArgs, ap)) {
01148         return JS_FALSE;
01149     }
01150 
01151     ReportError(cx, message, &report);
01152 
01153     if (message)
01154         JS_free(cx, message);
01155     if (report.messageArgs) {
01156         /*
01157          * js_ExpandErrorArguments owns its messageArgs only if it had to
01158          * inflate the arguments (from regular |char *|s).
01159          */
01160         if (charArgs) {
01161             int i = 0;
01162             while (report.messageArgs[i])
01163                 JS_free(cx, (void *)report.messageArgs[i++]);
01164         }
01165         JS_free(cx, (void *)report.messageArgs);
01166     }
01167     if (report.ucmessage)
01168         JS_free(cx, (void *)report.ucmessage);
01169 
01170     return warning;
01171 }
01172 
01173 JS_FRIEND_API(void)
01174 js_ReportErrorAgain(JSContext *cx, const char *message, JSErrorReport *reportp)
01175 {
01176     JSErrorReporter onError;
01177 
01178     if (!message)
01179         return;
01180 
01181     if (cx->lastMessage)
01182         free(cx->lastMessage);
01183     cx->lastMessage = JS_strdup(cx, message);
01184     if (!cx->lastMessage)
01185         return;
01186     onError = cx->errorReporter;
01187 
01188     /*
01189      * If debugErrorHook is present then we give it a chance to veto
01190      * sending the error on to the regular ErrorReporter.
01191      */
01192     if (onError) {
01193         JSDebugErrorHook hook = cx->runtime->debugErrorHook;
01194         if (hook &&
01195             !hook(cx, cx->lastMessage, reportp,
01196                   cx->runtime->debugErrorHookData)) {
01197             onError = NULL;
01198         }
01199     }
01200     if (onError)
01201         onError(cx, cx->lastMessage, reportp);
01202 }
01203 
01204 void
01205 js_ReportIsNotDefined(JSContext *cx, const char *name)
01206 {
01207     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_DEFINED, name);
01208 }
01209 
01210 #if defined DEBUG && defined XP_UNIX
01211 /* For gdb usage. */
01212 void js_traceon(JSContext *cx)  { cx->tracefp = stderr; }
01213 void js_traceoff(JSContext *cx) { cx->tracefp = NULL; }
01214 #endif
01215 
01216 JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = {
01217 #define MSG_DEF(name, number, count, exception, format) \
01218     { format, count, exception } ,
01219 #include "js.msg"
01220 #undef MSG_DEF
01221 };
01222 
01223 const JSErrorFormatString *
01224 js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber)
01225 {
01226     if ((errorNumber > 0) && (errorNumber < JSErr_Limit))
01227         return &js_ErrorFormatString[errorNumber];
01228     return NULL;
01229 }